Commit cb6176f3 by Le Hoang Quyen Committed by Commit Bot

Metal: Support tri-fan & line-loop with primitive restart

Triangle fan: - If primitive restart is NOT enabled and there is no active render pass, use Compute Shader to generate indices. - If primitive restart is enabled, use CPU to generate indices. Line loop: - If draw non-instanced without primitive restart, generate and draw only one additional last segment (fastest). - If draw instanced, primitive restart is NOT enabled, and there is no active render pass, use Compute Shader to generate indices (OK). - Otherwise, use CPU to generate indices (slowest). Also Disable OcclusionQueriesTest.ClearNotCounted failure on NVIDIA. Bug: angleproject:2634 Bug: angleproject:5307 Change-Id: Ia5529825807a964f5fcb2a4af8844778896cd42a Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2435859 Commit-Queue: Le Hoang Quyen <le.hoang.q@gmail.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 945791e1
...@@ -74,6 +74,12 @@ struct FeaturesMtl : FeatureSetBase ...@@ -74,6 +74,12 @@ struct FeaturesMtl : FeatureSetBase
Feature allowGenMultipleMipsPerPass = { Feature allowGenMultipleMipsPerPass = {
"gen_multiple_mips_per_pass", FeatureCategory::MetalFeatures, "gen_multiple_mips_per_pass", FeatureCategory::MetalFeatures,
"The renderer supports generating multiple mipmaps per pass", &members}; "The renderer supports generating multiple mipmaps per pass", &members};
Feature forceBufferGPUStorage = {
"force_buffer_gpu_storage_mtl", FeatureCategory::MetalFeatures,
"On systems that support both buffer's memory allocation on GPU and shared memory (such as "
"macOS), force using GPU memory allocation for buffers.",
&members};
}; };
} // namespace angle } // namespace angle
......
...@@ -18,13 +18,13 @@ ...@@ -18,13 +18,13 @@
"src/libANGLE/renderer/metal/shaders/format_autogen.h": "src/libANGLE/renderer/metal/shaders/format_autogen.h":
"b1d6512b904a7eb151b0095b7898b0e5", "b1d6512b904a7eb151b0095b7898b0e5",
"src/libANGLE/renderer/metal/shaders/gen_indices.metal": "src/libANGLE/renderer/metal/shaders/gen_indices.metal":
"add45aa44305b1a64c4bb8ece1e3d2fc", "06e2b6f259fe019b46e2a9710eb11bff",
"src/libANGLE/renderer/metal/shaders/gen_mipmap.metal": "src/libANGLE/renderer/metal/shaders/gen_mipmap.metal":
"54dca94c48bead446624079070b9b309", "54dca94c48bead446624079070b9b309",
"src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py": "src/libANGLE/renderer/metal/shaders/gen_mtl_internal_shaders.py":
"b48af61c8b02dda646b4c8febce50227", "b48af61c8b02dda646b4c8febce50227",
"src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc": "src/libANGLE/renderer/metal/shaders/mtl_default_shaders_src_autogen.inc":
"a0164451469303a462fd777c289c36ee", "0658278106de1fe5147e1367ecc896b9",
"src/libANGLE/renderer/metal/shaders/visibility.metal": "src/libANGLE/renderer/metal/shaders/visibility.metal":
"b82aa740cf4b0aed606aacef1024beea" "b82aa740cf4b0aed606aacef1024beea"
} }
\ No newline at end of file
...@@ -57,10 +57,14 @@ struct VertexConversionBufferMtl : public ConversionBufferMtl ...@@ -57,10 +57,14 @@ struct VertexConversionBufferMtl : public ConversionBufferMtl
struct IndexConversionBufferMtl : public ConversionBufferMtl struct IndexConversionBufferMtl : public ConversionBufferMtl
{ {
IndexConversionBufferMtl(ContextMtl *context, gl::DrawElementsType elemType, size_t offsetIn); IndexConversionBufferMtl(ContextMtl *context,
gl::DrawElementsType elemType,
bool primitiveRestartEnabled,
size_t offsetIn);
const gl::DrawElementsType elemType; const gl::DrawElementsType elemType;
const size_t offset; const size_t offset;
bool primitiveRestartEnabled;
}; };
struct UniformConversionBufferMtl : public ConversionBufferMtl struct UniformConversionBufferMtl : public ConversionBufferMtl
...@@ -143,6 +147,7 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl ...@@ -143,6 +147,7 @@ class BufferMtl : public BufferImpl, public BufferHolderMtl
IndexConversionBufferMtl *getIndexConversionBuffer(ContextMtl *context, IndexConversionBufferMtl *getIndexConversionBuffer(ContextMtl *context,
gl::DrawElementsType elemType, gl::DrawElementsType elemType,
bool primitiveRestartEnabled,
size_t offset); size_t offset);
ConversionBufferMtl *getUniformConversionBuffer(ContextMtl *context, size_t offset); ConversionBufferMtl *getUniformConversionBuffer(ContextMtl *context, size_t offset);
......
...@@ -55,12 +55,14 @@ ConversionBufferMtl::~ConversionBufferMtl() = default; ...@@ -55,12 +55,14 @@ ConversionBufferMtl::~ConversionBufferMtl() = default;
// IndexConversionBufferMtl implementation. // IndexConversionBufferMtl implementation.
IndexConversionBufferMtl::IndexConversionBufferMtl(ContextMtl *context, IndexConversionBufferMtl::IndexConversionBufferMtl(ContextMtl *context,
gl::DrawElementsType elemTypeIn, gl::DrawElementsType elemTypeIn,
bool primitiveRestartEnabledIn,
size_t offsetIn) size_t offsetIn)
: ConversionBufferMtl(context, : ConversionBufferMtl(context,
kConvertedElementArrayBufferInitialSize, kConvertedElementArrayBufferInitialSize,
mtl::kIndexBufferOffsetAlignment), mtl::kIndexBufferOffsetAlignment),
elemType(elemTypeIn), elemType(elemTypeIn),
offset(offsetIn) offset(offsetIn),
primitiveRestartEnabled(primitiveRestartEnabledIn)
{} {}
// UniformConversionBufferMtl implementation // UniformConversionBufferMtl implementation
...@@ -331,17 +333,19 @@ ConversionBufferMtl *BufferMtl::getVertexConversionBuffer(ContextMtl *context, ...@@ -331,17 +333,19 @@ ConversionBufferMtl *BufferMtl::getVertexConversionBuffer(ContextMtl *context,
IndexConversionBufferMtl *BufferMtl::getIndexConversionBuffer(ContextMtl *context, IndexConversionBufferMtl *BufferMtl::getIndexConversionBuffer(ContextMtl *context,
gl::DrawElementsType elemType, gl::DrawElementsType elemType,
bool primitiveRestartEnabled,
size_t offset) size_t offset)
{ {
for (auto &buffer : mIndexConversionBuffers) for (auto &buffer : mIndexConversionBuffers)
{ {
if (buffer.elemType == elemType && buffer.offset == offset) if (buffer.elemType == elemType && buffer.offset == offset &&
buffer.primitiveRestartEnabled == primitiveRestartEnabled)
{ {
return &buffer; return &buffer;
} }
} }
mIndexConversionBuffers.emplace_back(context, elemType, offset); mIndexConversionBuffers.emplace_back(context, elemType, primitiveRestartEnabled, offset);
return &mIndexConversionBuffers.back(); return &mIndexConversionBuffers.back();
} }
......
...@@ -368,13 +368,6 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -368,13 +368,6 @@ class ContextMtl : public ContextImpl, public mtl::Context
gl::DrawElementsType indexTypeOrNone, gl::DrawElementsType indexTypeOrNone,
const void *indices, const void *indices,
bool xfbPass); bool xfbPass);
angle::Result genLineLoopLastSegment(const gl::Context *context,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
gl::DrawElementsType indexTypeOrNone,
const void *indices,
mtl::BufferRef *lastSegmentIndexBufferOut);
angle::Result drawTriFanArrays(const gl::Context *context, angle::Result drawTriFanArrays(const gl::Context *context,
GLint first, GLint first,
...@@ -394,6 +387,23 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -394,6 +387,23 @@ class ContextMtl : public ContextImpl, public mtl::Context
const void *indices, const void *indices,
GLsizei instances); GLsizei instances);
angle::Result drawLineLoopArraysNonInstanced(const gl::Context *context,
GLint first,
GLsizei count);
angle::Result drawLineLoopArrays(const gl::Context *context,
GLint first,
GLsizei count,
GLsizei instances);
angle::Result drawLineLoopElementsNonInstancedNoPrimitiveRestart(const gl::Context *context,
GLsizei count,
gl::DrawElementsType type,
const void *indices);
angle::Result drawLineLoopElements(const gl::Context *context,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instances);
angle::Result drawArraysImpl(const gl::Context *context, angle::Result drawArraysImpl(const gl::Context *context,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
GLint first, GLint first,
...@@ -540,6 +550,7 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -540,6 +550,7 @@ class ContextMtl : public ContextImpl, public mtl::Context
// Lineloop and TriFan index buffer // Lineloop and TriFan index buffer
mtl::BufferPool mLineLoopIndexBuffer; mtl::BufferPool mLineLoopIndexBuffer;
mtl::BufferPool mLineLoopLastSegmentIndexBuffer;
mtl::BufferPool mTriFanIndexBuffer; mtl::BufferPool mTriFanIndexBuffer;
// one buffer can be reused for any starting vertex in DrawArrays() // one buffer can be reused for any starting vertex in DrawArrays()
mtl::BufferRef mTriFanArraysIndexBuffer; mtl::BufferRef mTriFanArraysIndexBuffer;
......
...@@ -728,22 +728,18 @@ void DisplayMtl::initializeFeatures() ...@@ -728,22 +728,18 @@ void DisplayMtl::initializeFeatures()
isMetal2_2 = true; isMetal2_2 = true;
} }
// default values: bool isOSX = TARGET_OS_OSX;
mFeatures.hasBaseVertexInstancedDraw.enabled = true; bool isCatalyst = TARGET_OS_MACCATALYST;
mFeatures.hasDepthTextureFiltering.enabled = false; bool isSimulator = TARGET_OS_SIMULATOR;
mFeatures.hasExplicitMemBarrier.enabled = false; bool isARM = ANGLE_MTL_ARM;
mFeatures.hasNonUniformDispatch.enabled = true;
mFeatures.hasStencilOutput.enabled = false; ANGLE_FEATURE_CONDITION((&mFeatures), allowGenMultipleMipsPerPass, true);
mFeatures.hasTextureSwizzle.enabled = false; ANGLE_FEATURE_CONDITION((&mFeatures), forceBufferGPUStorage, false);
mFeatures.allowSeparatedDepthStencilBuffers.enabled = false;
mFeatures.allowGenMultipleMipsPerPass.enabled = true;
mFeatures.hasCheapRenderPass.enabled = false;
ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthTextureFiltering, ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthTextureFiltering,
TARGET_OS_OSX || TARGET_OS_MACCATALYST); (isOSX || isCatalyst) && !isARM);
ANGLE_FEATURE_CONDITION( ANGLE_FEATURE_CONDITION((&mFeatures), hasExplicitMemBarrier,
(&mFeatures), hasExplicitMemBarrier, isMetal2_1 && (isOSX || isCatalyst) && !isARM);
isMetal2_1 && (TARGET_OS_OSX || TARGET_OS_MACCATALYST) && !ANGLE_MTL_ARM);
ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthAutoResolve, supportsEitherGPUFamily(3, 2)); ANGLE_FEATURE_CONDITION((&mFeatures), hasDepthAutoResolve, supportsEitherGPUFamily(3, 2));
ANGLE_FEATURE_CONDITION((&mFeatures), hasStencilAutoResolve, supportsEitherGPUFamily(5, 2)); ANGLE_FEATURE_CONDITION((&mFeatures), hasStencilAutoResolve, supportsEitherGPUFamily(5, 2));
ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve, ANGLE_FEATURE_CONDITION((&mFeatures), allowMultisampleStoreAndResolve,
...@@ -761,19 +757,17 @@ void DisplayMtl::initializeFeatures() ...@@ -761,19 +757,17 @@ void DisplayMtl::initializeFeatures()
// Fence sync is flaky on Nvidia // Fence sync is flaky on Nvidia
ANGLE_FEATURE_CONDITION((&mFeatures), hasEvents, isMetal2_1 && !isNVIDIA()); ANGLE_FEATURE_CONDITION((&mFeatures), hasEvents, isMetal2_1 && !isNVIDIA());
ANGLE_FEATURE_CONDITION((&mFeatures), hasCheapRenderPass, ANGLE_FEATURE_CONDITION((&mFeatures), hasCheapRenderPass, (isOSX || isCatalyst) && !isARM);
(TARGET_OS_OSX || TARGET_OS_MACCATALYST) && !ANGLE_MTL_ARM);
#if !TARGET_OS_MACCATALYST && (TARGET_OS_IOS || TARGET_OS_TV)
// Base Vertex drawing is only supported since GPU family 3. // Base Vertex drawing is only supported since GPU family 3.
ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw, supportsIOSGPUFamily(3)); ANGLE_FEATURE_CONDITION((&mFeatures), hasBaseVertexInstancedDraw,
isOSX || isCatalyst || supportsIOSGPUFamily(3));
ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch, ANGLE_FEATURE_CONDITION((&mFeatures), hasNonUniformDispatch,
TARGET_OS_IOS && supportsIOSGPUFamily(4)); isOSX || isCatalyst || supportsIOSGPUFamily(4));
ANGLE_FEATURE_CONDITION((&mFeatures), allowSeparatedDepthStencilBuffers, !TARGET_OS_SIMULATOR);
#endif ANGLE_FEATURE_CONDITION((&mFeatures), allowSeparatedDepthStencilBuffers,
!isOSX && !isCatalyst && !isSimulator);
angle::PlatformMethods *platform = ANGLEPlatformCurrent(); angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesMtl(platform, &mFeatures); platform->overrideFeaturesMtl(platform, &mFeatures);
......
...@@ -76,6 +76,7 @@ angle::Result StreamIndexData(ContextMtl *contextMtl, ...@@ -76,6 +76,7 @@ angle::Result StreamIndexData(ContextMtl *contextMtl,
const uint8_t *sourcePointer, const uint8_t *sourcePointer,
gl::DrawElementsType indexType, gl::DrawElementsType indexType,
size_t indexCount, size_t indexCount,
bool primitiveRestartEnabled,
mtl::BufferRef *bufferOut, mtl::BufferRef *bufferOut,
size_t *bufferOffsetOut) size_t *bufferOffsetOut)
{ {
...@@ -94,13 +95,27 @@ angle::Result StreamIndexData(ContextMtl *contextMtl, ...@@ -94,13 +95,27 @@ angle::Result StreamIndexData(ContextMtl *contextMtl,
const GLubyte *in = static_cast<const GLubyte *>(sourcePointer); const GLubyte *in = static_cast<const GLubyte *>(sourcePointer);
GLushort *expandedDst = reinterpret_cast<GLushort *>(dst); GLushort *expandedDst = reinterpret_cast<GLushort *>(dst);
// NOTE(hqle): May need to handle primitive restart index in future when ES 3.0 if (primitiveRestartEnabled)
// is supported.
// Fast path for common case.
for (size_t index = 0; index < indexCount; index++)
{ {
expandedDst[index] = static_cast<GLushort>(in[index]); for (size_t index = 0; index < indexCount; index++)
} {
if (in[index] == 0xFF)
{
expandedDst[index] = 0xFFFF;
}
else
{
expandedDst[index] = static_cast<GLushort>(in[index]);
}
}
} // if (primitiveRestartEnabled)
else
{
for (size_t index = 0; index < indexCount; index++)
{
expandedDst[index] = static_cast<GLushort>(in[index]);
}
} // if (primitiveRestartEnabled)
} }
else else
{ {
...@@ -606,11 +621,12 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext, ...@@ -606,11 +621,12 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext,
alignedOffset = alignedOffset << 1; alignedOffset = alignedOffset << 1;
} }
ContextMtl *contextMtl = mtl::GetImpl(glContext); ContextMtl *contextMtl = mtl::GetImpl(glContext);
BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer()); const gl::State &glState = glContext->getState();
BufferMtl *idxBuffer = mtl::GetImpl(getState().getElementArrayBuffer());
IndexConversionBufferMtl *conversion = IndexConversionBufferMtl *conversion = idxBuffer->getIndexConversionBuffer(
idxBuffer->getIndexConversionBuffer(contextMtl, indexType, offsetModulo); contextMtl, indexType, glState.isPrimitiveRestartEnabled(), offsetModulo);
// Has the content of the buffer has changed since last conversion? // Has the content of the buffer has changed since last conversion?
if (!conversion->dirty) if (!conversion->dirty)
...@@ -629,8 +645,8 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext, ...@@ -629,8 +645,8 @@ angle::Result VertexArrayMtl::convertIndexBuffer(const gl::Context *glContext,
// We shouldn't use GPU to convert when we are in a middle of a render pass. // We shouldn't use GPU to convert when we are in a middle of a render pass.
ANGLE_TRY(StreamIndexData(contextMtl, &conversion->data, ANGLE_TRY(StreamIndexData(contextMtl, &conversion->data,
idxBuffer->getClientShadowCopyData(contextMtl) + offsetModulo, idxBuffer->getClientShadowCopyData(contextMtl) + offsetModulo,
indexType, indexCount, &conversion->convertedBuffer, indexType, indexCount, glState.isPrimitiveRestartEnabled(),
&conversion->convertedOffset)); &conversion->convertedBuffer, &conversion->convertedOffset));
} }
else else
{ {
...@@ -664,10 +680,10 @@ angle::Result VertexArrayMtl::convertIndexBufferGPU(const gl::Context *glContext ...@@ -664,10 +680,10 @@ angle::Result VertexArrayMtl::convertIndexBufferGPU(const gl::Context *glContext
// Do the conversion on GPU. // Do the conversion on GPU.
ANGLE_TRY(display->getUtils().convertIndexBufferGPU( ANGLE_TRY(display->getUtils().convertIndexBufferGPU(
mtl::GetImpl(glContext), contextMtl, {indexType, static_cast<uint32_t>(indexCount), idxBuffer->getCurrentBuffer(),
{indexType, static_cast<uint32_t>(indexCount), idxBuffer->getCurrentBuffer(), static_cast<uint32_t>(offset), conversion->convertedBuffer,
static_cast<uint32_t>(offset), conversion->convertedBuffer, static_cast<uint32_t>(conversion->convertedOffset),
static_cast<uint32_t>(conversion->convertedOffset)})); glContext->getState().isPrimitiveRestartEnabled()}));
ANGLE_TRY(conversion->data.commit(contextMtl)); ANGLE_TRY(conversion->data.commit(contextMtl));
...@@ -689,7 +705,8 @@ angle::Result VertexArrayMtl::streamIndexBufferFromClient(const gl::Context *con ...@@ -689,7 +705,8 @@ angle::Result VertexArrayMtl::streamIndexBufferFromClient(const gl::Context *con
auto srcData = static_cast<const uint8_t *>(sourcePointer); auto srcData = static_cast<const uint8_t *>(sourcePointer);
ANGLE_TRY(StreamIndexData(contextMtl, &mDynamicIndexData, srcData, indexType, indexCount, ANGLE_TRY(StreamIndexData(contextMtl, &mDynamicIndexData, srcData, indexType, indexCount,
idxBufferOut, idxBufferOffsetOut)); context->getState().isPrimitiveRestartEnabled(), idxBufferOut,
idxBufferOffsetOut));
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -112,6 +112,11 @@ BufferPool::~BufferPool() {} ...@@ -112,6 +112,11 @@ BufferPool::~BufferPool() {}
bool BufferPool::shouldAllocateInSharedMem(ContextMtl *contextMtl) const bool BufferPool::shouldAllocateInSharedMem(ContextMtl *contextMtl) const
{ {
if (ANGLE_UNLIKELY(contextMtl->getDisplay()->getFeatures().forceBufferGPUStorage.enabled))
{
return false;
}
switch (mMemPolicy) switch (mMemPolicy)
{ {
case BufferPoolMemPolicy::AlwaysSharedMem: case BufferPoolMemPolicy::AlwaysSharedMem:
......
...@@ -104,7 +104,7 @@ struct StencilBlitViaBufferParams : public DepthStencilBlitParams ...@@ -104,7 +104,7 @@ struct StencilBlitViaBufferParams : public DepthStencilBlitParams
bool dstPackedDepthStencilFormat = false; bool dstPackedDepthStencilFormat = false;
}; };
struct TriFanFromArrayParams struct TriFanOrLineLoopFromArrayParams
{ {
uint32_t firstVertex; uint32_t firstVertex;
uint32_t vertexCount; uint32_t vertexCount;
...@@ -123,6 +123,7 @@ struct IndexConversionParams ...@@ -123,6 +123,7 @@ struct IndexConversionParams
const BufferRef &dstBuffer; const BufferRef &dstBuffer;
// Must be multiples of kIndexBufferOffsetAlignment // Must be multiples of kIndexBufferOffsetAlignment
uint32_t dstOffset; uint32_t dstOffset;
bool primitiveRestartEnabled = false;
}; };
struct IndexGenerationParams struct IndexGenerationParams
...@@ -132,6 +133,7 @@ struct IndexGenerationParams ...@@ -132,6 +133,7 @@ struct IndexGenerationParams
const void *indices; const void *indices;
BufferRef dstBuffer; BufferRef dstBuffer;
uint32_t dstOffset; uint32_t dstOffset;
bool primitiveRestartEnabled = false;
}; };
struct CopyPixelsCommonParams struct CopyPixelsCommonParams
...@@ -312,18 +314,29 @@ class IndexGeneratorUtils final : angle::NonCopyable ...@@ -312,18 +314,29 @@ class IndexGeneratorUtils final : angle::NonCopyable
const IndexConversionParams &params); const IndexConversionParams &params);
// Generate triangle fan index buffer for glDrawArrays(). // Generate triangle fan index buffer for glDrawArrays().
angle::Result generateTriFanBufferFromArrays(ContextMtl *contextMtl, angle::Result generateTriFanBufferFromArrays(ContextMtl *contextMtl,
const TriFanFromArrayParams &params); const TriFanOrLineLoopFromArrayParams &params);
// Generate triangle fan index buffer for glDrawElements(). // Generate triangle fan index buffer for glDrawElements().
angle::Result generateTriFanBufferFromElementsArray(ContextMtl *contextMtl, angle::Result generateTriFanBufferFromElementsArray(ContextMtl *contextMtl,
const IndexGenerationParams &params); const IndexGenerationParams &params);
// Generate line loop index buffer for glDrawArrays().
angle::Result generateLineLoopBufferFromArrays(ContextMtl *contextMtl,
const TriFanOrLineLoopFromArrayParams &params);
// Generate line loop's last segment index buffer for glDrawArrays(). // Generate line loop's last segment index buffer for glDrawArrays().
// This is used when primitive restart is not enabled.
angle::Result generateLineLoopLastSegment(ContextMtl *contextMtl, angle::Result generateLineLoopLastSegment(ContextMtl *contextMtl,
uint32_t firstVertex, uint32_t firstVertex,
uint32_t lastVertex, uint32_t lastVertex,
const BufferRef &dstBuffer, const BufferRef &dstBuffer,
uint32_t dstOffset); uint32_t dstOffset);
// Generate line loop index buffer for glDrawElements().
// Destination buffer must have at least 2x the number of original indices if primitive restart
// is enabled.
angle::Result generateLineLoopBufferFromElementsArray(ContextMtl *contextMtl,
const IndexGenerationParams &params,
uint32_t *indicesGenerated);
// Generate line loop's last segment index buffer for glDrawElements(). // Generate line loop's last segment index buffer for glDrawElements().
// NOTE: this function assumes primitive restart is not enabled.
angle::Result generateLineLoopLastSegmentFromElementsArray(ContextMtl *contextMtl, angle::Result generateLineLoopLastSegmentFromElementsArray(ContextMtl *contextMtl,
const IndexGenerationParams &params); const IndexGenerationParams &params);
...@@ -340,13 +353,17 @@ class IndexGeneratorUtils final : angle::NonCopyable ...@@ -340,13 +353,17 @@ class IndexGeneratorUtils final : angle::NonCopyable
ContextMtl *contextMtl, ContextMtl *contextMtl,
gl::DrawElementsType srcType, gl::DrawElementsType srcType,
uint32_t srcOffset); uint32_t srcOffset);
// Get compute pipeline to generate tri fan index for glDrawElements(). // Get compute pipeline to generate tri fan/line loop index for glDrawElements().
AutoObjCPtr<id<MTLComputePipelineState>> getTriFanFromElemArrayGeneratorPipeline( AutoObjCPtr<id<MTLComputePipelineState>> getIndicesFromElemArrayGeneratorPipeline(
ContextMtl *contextMtl, ContextMtl *contextMtl,
gl::DrawElementsType srcType, gl::DrawElementsType srcType,
uint32_t srcOffset); uint32_t srcOffset,
NSString *shaderName,
IndexConversionPipelineArray *pipelineCacheArray);
// Defer loading of compute pipeline to generate tri fan index for glDrawArrays(). // Defer loading of compute pipeline to generate tri fan index for glDrawArrays().
void ensureTriFanFromArrayGeneratorInitialized(ContextMtl *contextMtl); void ensureTriFanFromArrayGeneratorInitialized(ContextMtl *contextMtl);
// Defer loading of compute pipeline to generate line loop index for glDrawArrays().
void ensureLineLoopFromArrayGeneratorInitialized(ContextMtl *contextMtl);
angle::Result generateTriFanBufferFromElementsArrayGPU( angle::Result generateTriFanBufferFromElementsArrayGPU(
ContextMtl *contextMtl, ContextMtl *contextMtl,
...@@ -360,6 +377,18 @@ class IndexGeneratorUtils final : angle::NonCopyable ...@@ -360,6 +377,18 @@ class IndexGeneratorUtils final : angle::NonCopyable
angle::Result generateTriFanBufferFromElementsArrayCPU(ContextMtl *contextMtl, angle::Result generateTriFanBufferFromElementsArrayCPU(ContextMtl *contextMtl,
const IndexGenerationParams &params); const IndexGenerationParams &params);
angle::Result generateLineLoopBufferFromElementsArrayGPU(
ContextMtl *contextMtl,
gl::DrawElementsType srcType,
uint32_t indexCount,
const BufferRef &srcBuffer,
uint32_t srcOffset,
const BufferRef &dstBuffer,
// Must be multiples of kIndexBufferOffsetAlignment
uint32_t dstOffset);
angle::Result generateLineLoopBufferFromElementsArrayCPU(ContextMtl *contextMtl,
const IndexGenerationParams &params,
uint32_t *indicesGenerated);
angle::Result generateLineLoopLastSegmentFromElementsArrayCPU( angle::Result generateLineLoopLastSegmentFromElementsArrayCPU(
ContextMtl *contextMtl, ContextMtl *contextMtl,
const IndexGenerationParams &params); const IndexGenerationParams &params);
...@@ -368,6 +397,9 @@ class IndexGeneratorUtils final : angle::NonCopyable ...@@ -368,6 +397,9 @@ class IndexGeneratorUtils final : angle::NonCopyable
IndexConversionPipelineArray mTriFanFromElemArrayGeneratorPipelineCaches; IndexConversionPipelineArray mTriFanFromElemArrayGeneratorPipelineCaches;
AutoObjCPtr<id<MTLComputePipelineState>> mTriFanFromArraysGeneratorPipeline; AutoObjCPtr<id<MTLComputePipelineState>> mTriFanFromArraysGeneratorPipeline;
IndexConversionPipelineArray mLineLoopFromElemArrayGeneratorPipelineCaches;
AutoObjCPtr<id<MTLComputePipelineState>> mLineLoopFromArraysGeneratorPipeline;
}; };
// Util class for handling visibility query result // Util class for handling visibility query result
...@@ -553,14 +585,20 @@ class RenderUtils : public Context, angle::NonCopyable ...@@ -553,14 +585,20 @@ class RenderUtils : public Context, angle::NonCopyable
angle::Result convertIndexBufferGPU(ContextMtl *contextMtl, angle::Result convertIndexBufferGPU(ContextMtl *contextMtl,
const IndexConversionParams &params); const IndexConversionParams &params);
angle::Result generateTriFanBufferFromArrays(ContextMtl *contextMtl, angle::Result generateTriFanBufferFromArrays(ContextMtl *contextMtl,
const TriFanFromArrayParams &params); const TriFanOrLineLoopFromArrayParams &params);
angle::Result generateTriFanBufferFromElementsArray(ContextMtl *contextMtl, angle::Result generateTriFanBufferFromElementsArray(ContextMtl *contextMtl,
const IndexGenerationParams &params); const IndexGenerationParams &params);
angle::Result generateLineLoopBufferFromArrays(ContextMtl *contextMtl,
const TriFanOrLineLoopFromArrayParams &params);
angle::Result generateLineLoopLastSegment(ContextMtl *contextMtl, angle::Result generateLineLoopLastSegment(ContextMtl *contextMtl,
uint32_t firstVertex, uint32_t firstVertex,
uint32_t lastVertex, uint32_t lastVertex,
const BufferRef &dstBuffer, const BufferRef &dstBuffer,
uint32_t dstOffset); uint32_t dstOffset);
angle::Result generateLineLoopBufferFromElementsArray(ContextMtl *contextMtl,
const IndexGenerationParams &params,
uint32_t *indicesGenerated);
angle::Result generateLineLoopLastSegmentFromElementsArray(ContextMtl *contextMtl, angle::Result generateLineLoopLastSegmentFromElementsArray(ContextMtl *contextMtl,
const IndexGenerationParams &params); const IndexGenerationParams &params);
......
...@@ -756,7 +756,7 @@ angle::Result Buffer::resetWithSharedMemOpt(ContextMtl *context, ...@@ -756,7 +756,7 @@ angle::Result Buffer::resetWithSharedMemOpt(ContextMtl *context,
options = 0; options = 0;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST #if TARGET_OS_OSX || TARGET_OS_MACCATALYST
if (!forceUseSharedMem) if (!forceUseSharedMem || context->getDisplay()->getFeatures().forceBufferGPUStorage.enabled)
{ {
options |= MTLResourceStorageModeManaged; options |= MTLResourceStorageModeManaged;
} }
......
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
namespace rx namespace rx
{ {
class ContextMtl;
namespace mtl namespace mtl
{ {
...@@ -146,6 +149,12 @@ gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion); ...@@ -146,6 +149,12 @@ gl::Box MTLRegionToGLBox(const MTLRegion &mtlRegion);
MipmapNativeLevel GetNativeMipLevel(GLuint level, GLuint base); MipmapNativeLevel GetNativeMipLevel(GLuint level, GLuint base);
GLuint GetGLMipLevel(const MipmapNativeLevel &nativeLevel, GLuint base); GLuint GetGLMipLevel(const MipmapNativeLevel &nativeLevel, GLuint base);
angle::Result TriangleFanBoundCheck(ContextMtl *context, size_t numTris);
angle::Result GetTriangleFanIndicesCount(ContextMtl *context,
GLsizei vetexCount,
uint32_t *numElemsOut);
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
} // namespace mtl } // namespace mtl
} // namespace rx } // namespace rx
......
...@@ -855,5 +855,32 @@ GLuint GetGLMipLevel(const MipmapNativeLevel &nativeLevel, GLuint base) ...@@ -855,5 +855,32 @@ GLuint GetGLMipLevel(const MipmapNativeLevel &nativeLevel, GLuint base)
return nativeLevel.get() + base; return nativeLevel.get() + base;
} }
angle::Result TriangleFanBoundCheck(ContextMtl *context, size_t numTris)
{
bool indexCheck =
(numTris > std::numeric_limits<unsigned int>::max() / (sizeof(unsigned int) * 3));
ANGLE_CHECK(context, !indexCheck,
"Failed to create a scratch index buffer for GL_TRIANGLE_FAN, "
"too many indices required.",
GL_OUT_OF_MEMORY);
return angle::Result::Continue;
}
angle::Result GetTriangleFanIndicesCount(ContextMtl *context,
GLsizei vetexCount,
uint32_t *numElemsOut)
{
size_t numTris = vetexCount - 2;
ANGLE_TRY(TriangleFanBoundCheck(context, numTris));
size_t numIndices = numTris * 3;
ANGLE_CHECK(context, numIndices <= std::numeric_limits<uint32_t>::max(),
"Failed to create a scratch index buffer for GL_TRIANGLE_FAN, "
"too many indices required.",
GL_OUT_OF_MEMORY);
*numElemsOut = static_cast<uint32_t>(numIndices);
return angle::Result::Continue;
}
} // namespace mtl } // namespace mtl
} // namespace rx } // namespace rx
...@@ -22,6 +22,7 @@ struct IndexConversionParams ...@@ -22,6 +22,7 @@ struct IndexConversionParams
{ {
uint32_t srcOffset; // offset in bytes uint32_t srcOffset; // offset in bytes
uint32_t indexCount; uint32_t indexCount;
bool primitiveRestartEnabled;
}; };
#define ANGLE_IDX_CONVERSION_GUARD(IDX, OPTS) ANGLE_KERNEL_GUARD(IDX, OPTS.indexCount) #define ANGLE_IDX_CONVERSION_GUARD(IDX, OPTS) ANGLE_KERNEL_GUARD(IDX, OPTS.indexCount)
...@@ -61,7 +62,17 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]], ...@@ -61,7 +62,17 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]],
device ushort *output[[buffer(2)]]) device ushort *output[[buffer(2)]])
{ {
ANGLE_IDX_CONVERSION_GUARD(idx, options); ANGLE_IDX_CONVERSION_GUARD(idx, options);
output[idx] = getIndexAligned(input, options.srcOffset, idx);
uchar value = getIndexAligned(input, options.srcOffset, idx);
if (options.primitiveRestartEnabled && value == 0xff)
{
output[idx] = 0xffff;
}
else
{
output[idx] = value;
}
} }
kernel void convertIndexU16( kernel void convertIndexU16(
...@@ -106,16 +117,19 @@ kernel void convertIndexU32( ...@@ -106,16 +117,19 @@ kernel void convertIndexU32(
output[idx] = value; output[idx] = value;
} }
struct TriFanArrayParams struct IndexFromArrayParams
{ {
uint firstVertex; uint firstVertex;
uint vertexCountFrom3rd; // vertex count excluding the 1st & 2nd vertices. // For triangle fan: vertex count excluding the 1st & 2nd vertices.
uint vertexCount;
}; };
kernel void genTriFanIndicesFromArray(uint idx[[thread_position_in_grid]],
constant TriFanArrayParams &options[[buffer(0)]], // Generate triangle fan indices for glDrawArray()
device uint *output[[buffer(2)]]) kernel void genTriFanIndicesFromArray(uint idx [[thread_position_in_grid]],
constant IndexFromArrayParams &options [[buffer(0)]],
device uint *output [[buffer(2)]])
{ {
ANGLE_KERNEL_GUARD(idx, options.vertexCountFrom3rd); ANGLE_KERNEL_GUARD(idx, options.vertexCount);
uint vertexIdx = options.firstVertex + 2 + idx; uint vertexIdx = options.firstVertex + 2 + idx;
...@@ -153,6 +167,7 @@ inline uint getIndexU32(uint offset, ...@@ -153,6 +167,7 @@ inline uint getIndexU32(uint offset,
return 0; return 0;
} }
// NOTE(hqle): triangle fan indices generation doesn't support primitive restart.
// Generate triangle fan indices from an indices buffer. indexCount options indicates number // Generate triangle fan indices from an indices buffer. indexCount options indicates number
// of indices starting from the 3rd. // of indices starting from the 3rd.
kernel void genTriFanIndicesFromElements( kernel void genTriFanIndicesFromElements(
...@@ -170,4 +185,34 @@ kernel void genTriFanIndicesFromElements( ...@@ -170,4 +185,34 @@ kernel void genTriFanIndicesFromElements(
output[3 * idx] = getIndexU32(options.srcOffset, 0, inputU8, inputU16, inputU32); output[3 * idx] = getIndexU32(options.srcOffset, 0, inputU8, inputU16, inputU32);
output[3 * idx + 1] = getIndexU32(options.srcOffset, elemIdx - 1, inputU8, inputU16, inputU32); output[3 * idx + 1] = getIndexU32(options.srcOffset, elemIdx - 1, inputU8, inputU16, inputU32);
output[3 * idx + 2] = getIndexU32(options.srcOffset, elemIdx, inputU8, inputU16, inputU32); output[3 * idx + 2] = getIndexU32(options.srcOffset, elemIdx, inputU8, inputU16, inputU32);
}
// Generate line loop indices for glDrawArray()
kernel void genLineLoopIndicesFromArray(uint idx [[thread_position_in_grid]],
constant IndexFromArrayParams &options [[buffer(0)]],
device uint *output [[buffer(2)]])
{
uint totalIndices = options.vertexCount + 1;
ANGLE_KERNEL_GUARD(idx, totalIndices);
output[idx] = options.firstVertex + idx % options.vertexCount;
}
// NOTE(hqle): lineloop indices generation doesn't support primitive restart.
// Generate line loop indices for glDrawElements()
kernel void genLineLoopIndicesFromElements(uint idx [[thread_position_in_grid]],
constant IndexConversionParams &options [[buffer(0)]],
constant uchar *inputU8
[[buffer(1), function_constant(kUseSourceBufferU8)]],
constant ushort *inputU16
[[buffer(1), function_constant(kUseSourceBufferU16)]],
constant uint *inputU32
[[buffer(1), function_constant(kUseSourceBufferU32)]],
device uint *output [[buffer(2)]])
{
uint totalTargetIndices = options.indexCount + 1;
ANGLE_KERNEL_GUARD(idx, totalTargetIndices);
output[idx] =
getIndexU32(options.srcOffset, idx % options.indexCount, inputU8, inputU16, inputU32);
} }
\ No newline at end of file
...@@ -725,6 +725,7 @@ struct IndexConversionParams ...@@ -725,6 +725,7 @@ struct IndexConversionParams
{ {
uint32_t srcOffset; uint32_t srcOffset;
uint32_t indexCount; uint32_t indexCount;
bool primitiveRestartEnabled;
}; };
...@@ -764,7 +765,17 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]], ...@@ -764,7 +765,17 @@ kernel void convertIndexU8ToU16(uint idx[[thread_position_in_grid]],
device ushort *output[[buffer(2)]]) device ushort *output[[buffer(2)]])
{ {
if (idx >= options.indexCount) { return; }; if (idx >= options.indexCount) { return; };
output[idx] = getIndexAligned(input, options.srcOffset, idx);
uchar value = getIndexAligned(input, options.srcOffset, idx);
if (options.primitiveRestartEnabled && value == 0xff)
{
output[idx] = 0xffff;
}
else
{
output[idx] = value;
}
} }
kernel void convertIndexU16( kernel void convertIndexU16(
...@@ -809,16 +820,19 @@ kernel void convertIndexU32( ...@@ -809,16 +820,19 @@ kernel void convertIndexU32(
output[idx] = value; output[idx] = value;
} }
struct TriFanArrayParams struct IndexFromArrayParams
{ {
uint firstVertex; uint firstVertex;
uint vertexCountFrom3rd;
uint vertexCount;
}; };
kernel void genTriFanIndicesFromArray(uint idx[[thread_position_in_grid]],
constant TriFanArrayParams &options[[buffer(0)]],
device uint *output[[buffer(2)]]) kernel void genTriFanIndicesFromArray(uint idx [[thread_position_in_grid]],
constant IndexFromArrayParams &options [[buffer(0)]],
device uint *output [[buffer(2)]])
{ {
if (idx >= options.vertexCountFrom3rd) { return; }; if (idx >= options.vertexCount) { return; };
uint vertexIdx = options.firstVertex + 2 + idx; uint vertexIdx = options.firstVertex + 2 + idx;
...@@ -858,6 +872,7 @@ inline uint getIndexU32(uint offset, ...@@ -858,6 +872,7 @@ inline uint getIndexU32(uint offset,
kernel void genTriFanIndicesFromElements( kernel void genTriFanIndicesFromElements(
uint idx[[thread_position_in_grid]], uint idx[[thread_position_in_grid]],
constant IndexConversionParams &options[[buffer(0)]], constant IndexConversionParams &options[[buffer(0)]],
...@@ -874,6 +889,36 @@ kernel void genTriFanIndicesFromElements( ...@@ -874,6 +889,36 @@ kernel void genTriFanIndicesFromElements(
output[3 * idx + 1] = getIndexU32(options.srcOffset, elemIdx - 1, inputU8, inputU16, inputU32); output[3 * idx + 1] = getIndexU32(options.srcOffset, elemIdx - 1, inputU8, inputU16, inputU32);
output[3 * idx + 2] = getIndexU32(options.srcOffset, elemIdx, inputU8, inputU16, inputU32); output[3 * idx + 2] = getIndexU32(options.srcOffset, elemIdx, inputU8, inputU16, inputU32);
} }
kernel void genLineLoopIndicesFromArray(uint idx [[thread_position_in_grid]],
constant IndexFromArrayParams &options [[buffer(0)]],
device uint *output [[buffer(2)]])
{
uint totalIndices = options.vertexCount + 1;
if (idx >= totalIndices) { return; };
output[idx] = options.firstVertex + idx % options.vertexCount;
}
kernel void genLineLoopIndicesFromElements(uint idx [[thread_position_in_grid]],
constant IndexConversionParams &options [[buffer(0)]],
constant uchar *inputU8
[[buffer(1), function_constant(kUseSourceBufferU8)]],
constant ushort *inputU16
[[buffer(1), function_constant(kUseSourceBufferU16)]],
constant uint *inputU32
[[buffer(1), function_constant(kUseSourceBufferU32)]],
device uint *output [[buffer(2)]])
{
uint totalTargetIndices = options.indexCount + 1;
if (idx >= totalTargetIndices) { return; };
output[idx] =
getIndexU32(options.srcOffset, idx % options.indexCount, inputU8, inputU16, inputU32);
}
# 4 "temp_master_source.metal" 2 # 4 "temp_master_source.metal" 2
# 1 "./gen_mipmap.metal" 1 # 1 "./gen_mipmap.metal" 1
......
...@@ -495,23 +495,31 @@ attribute vec2 a_position; ...@@ -495,23 +495,31 @@ attribute vec2 a_position;
// x,y = offset, z = scale // x,y = offset, z = scale
attribute vec3 a_transform; attribute vec3 a_transform;
attribute vec4 a_color;
varying vec4 v_color;
invariant gl_Position; invariant gl_Position;
void main() void main()
{ {
vec2 v_position = a_transform.z * a_position + a_transform.xy; vec2 v_position = a_transform.z * a_position + a_transform.xy;
gl_Position = vec4(v_position, 0.0, 1.0); gl_Position = vec4(v_position, 0.0, 1.0);
v_color = a_color;
})"; })";
constexpr char kFS[] = R"( constexpr char kFS[] = R"(
precision highp float; precision highp float;
varying vec4 v_color;
void main() void main()
{ {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); gl_FragColor = v_color;
})"; })";
ANGLE_GL_PROGRAM(program, kVS, kFS); ANGLE_GL_PROGRAM(program, kVS, kFS);
glBindAttribLocation(program, 0, "a_position"); glBindAttribLocation(program, 0, "a_position");
glBindAttribLocation(program, 1, "a_transform"); glBindAttribLocation(program, 1, "a_transform");
glBindAttribLocation(program, 2, "a_color");
glLinkProgram(program); glLinkProgram(program);
glUseProgram(program); glUseProgram(program);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -524,13 +532,18 @@ void main() ...@@ -524,13 +532,18 @@ void main()
0, 0, 9, 0.2, 0.1, 2, 0.5, -0.2, 3, -0.8, -0.5, 1, -0.4, 0.4, 6, 0, 0, 9, 0.2, 0.1, 2, 0.5, -0.2, 3, -0.8, -0.5, 1, -0.4, 0.4, 6,
}; };
constexpr GLushort lineloopAsStripIndices[] = {0, 1, 2, 3, 0};
constexpr GLsizei instances = ArraySize(transform) / 3; constexpr GLsizei instances = ArraySize(transform) / 3;
const GLfloat colors[instances * 3] = {
1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
};
constexpr GLushort lineloopAsStripIndices[] = {0, 1, 2, 3, 0};
std::vector<GLColor> expectedPixels(getWindowWidth() * getWindowHeight()); std::vector<GLColor> expectedPixels(getWindowWidth() * getWindowHeight());
// Draw in non-instanced way // Draw in non-instanced way
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
...@@ -545,6 +558,7 @@ void main() ...@@ -545,6 +558,7 @@ void main()
for (size_t i = 0; i < instances; ++i) for (size_t i = 0; i < instances; ++i)
{ {
glVertexAttrib3fv(1, transform + 3 * i); glVertexAttrib3fv(1, transform + 3 * i);
glVertexAttrib3fv(2, colors + 3 * i);
glDrawElements(GL_LINE_STRIP, ArraySize(lineloopAsStripIndices), GL_UNSIGNED_SHORT, glDrawElements(GL_LINE_STRIP, ArraySize(lineloopAsStripIndices), GL_UNSIGNED_SHORT,
lineloopAsStripIndices); lineloopAsStripIndices);
...@@ -557,7 +571,7 @@ void main() ...@@ -557,7 +571,7 @@ void main()
// Draw in instanced way: // Draw in instanced way:
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
GLBuffer vertexBuffer[2]; GLBuffer vertexBuffer[3];
GLBuffer indexBuffer; GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
...@@ -576,6 +590,12 @@ void main() ...@@ -576,6 +590,12 @@ void main()
glVertexAttribDivisorANGLE(1, 1); glVertexAttribDivisorANGLE(1, 1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribDivisorANGLE(2, 1);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArraysInstancedANGLE(GL_LINE_LOOP, 0, ArraySize(vertices) / 2, instances); glDrawArraysInstancedANGLE(GL_LINE_LOOP, 0, ArraySize(vertices) / 2, instances);
std::vector<GLColor> actualPixels(getWindowWidth() * getWindowHeight()); std::vector<GLColor> actualPixels(getWindowWidth() * getWindowHeight());
......
...@@ -57,9 +57,9 @@ class LineLoopTest : public ANGLETest ...@@ -57,9 +57,9 @@ class LineLoopTest : public ANGLETest
{ {
const GLubyte *pixel = &pixels[0] + ((y * getWindowWidth() + x) * 4); const GLubyte *pixel = &pixels[0] + ((y * getWindowWidth() + x) * 4);
EXPECT_EQ(pixel[0], 0); EXPECT_EQ(pixel[0], 0) << "Failed at " << x << ", " << y << std::endl;
EXPECT_EQ(pixel[1], pixel[2]); EXPECT_EQ(pixel[1], pixel[2]) << "Failed at " << x << ", " << y << std::endl;
ASSERT_EQ(pixel[3], 255); ASSERT_EQ(pixel[3], 255) << "Failed at " << x << ", " << y << std::endl;
} }
} }
} }
...@@ -202,6 +202,166 @@ TEST_P(LineLoopTest, DISABLED_DrawArraysWithLargeCount) ...@@ -202,6 +202,166 @@ TEST_P(LineLoopTest, DISABLED_DrawArraysWithLargeCount)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
} }
class LineLoopPrimitiveRestartTest : public ANGLETest
{
protected:
LineLoopPrimitiveRestartTest()
{
setWindowWidth(64);
setWindowHeight(64);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
TEST_P(LineLoopPrimitiveRestartTest, LineLoopWithPrimitiveRestart)
{
constexpr char kVS[] = R"(#version 300 es
in vec2 a_position;
// x,y = offset, z = scale
in vec3 a_transform;
invariant gl_Position;
void main()
{
vec2 v_position = a_transform.z * a_position + a_transform.xy;
gl_Position = vec4(v_position, 0.0, 1.0);
})";
constexpr char kFS[] = R"(#version 300 es
precision highp float;
layout (location=0) out vec4 fragColor;
void main()
{
fragColor = vec4(1.0, 0.0, 0.0, 1.0);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glBindAttribLocation(program, 0, "a_position");
glBindAttribLocation(program, 1, "a_transform");
glLinkProgram(program);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
// clang-format off
constexpr GLfloat vertices[] = {
0.1, 0.1, -0.1, 0.1, -0.1, -0.1, 0.1, -0.1,
0.1, 0.1, -0.1, 0.1, -0.1, -0.1, 0.1, -0.1,
0.1, 0.1, -0.1, 0.1, -0.1, -0.1, 0.1, -0.1,
0.1, 0.1, -0.1, 0.1, -0.1, -0.1, 0.1, -0.1,
};
constexpr GLfloat transform[] = {
// first loop transform
0, 0, 9,
0, 0, 9,
0, 0, 9,
0, 0, 9,
// second loop transform
0.2, 0.1, 2,
0.2, 0.1, 2,
0.2, 0.1, 2,
0.2, 0.1, 2,
// third loop transform
0.5, -0.2, 3,
0.5, -0.2, 3,
0.5, -0.2, 3,
0.5, -0.2, 3,
// forth loop transform
-0.8, -0.5, 1,
-0.8, -0.5, 1,
-0.8, -0.5, 1,
-0.8, -0.5, 1,
};
constexpr GLushort lineloopAsStripIndices[] = {
// first strip
0, 1, 2, 3, 0,
// second strip
4, 5, 6, 7, 4,
// third strip
8, 9, 10, 11, 8,
// forth strip
12, 13, 14, 15, 12 };
constexpr GLushort lineloopWithRestartIndices[] = {
// first loop
0, 1, 2, 3, 0xffff,
// second loop
4, 5, 6, 7, 0xffff,
// third loop
8, 9, 10, 11, 0xffff,
// forth loop
12, 13, 14, 15,
};
// clang-format on
std::vector<GLColor> expectedPixels(getWindowWidth() * getWindowHeight());
std::vector<GLColor> renderedPixels(getWindowWidth() * getWindowHeight());
// Draw in non-primitive restart way
glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
for (int loop = 0; loop < 4; ++loop)
{
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices + 8 * loop);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, transform + 12 * loop);
glDrawElements(GL_LINE_STRIP, 5, GL_UNSIGNED_SHORT, lineloopAsStripIndices);
}
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
expectedPixels.data());
ASSERT_GL_NO_ERROR();
// Draw line loop with primitive restart:
glClear(GL_COLOR_BUFFER_BIT);
GLBuffer vertexBuffer[2];
GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(lineloopWithRestartIndices),
lineloopWithRestartIndices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(transform), transform, GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glClear(GL_COLOR_BUFFER_BIT);
glDrawElements(GL_LINE_LOOP, ArraySize(lineloopWithRestartIndices), GL_UNSIGNED_SHORT, 0);
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
renderedPixels.data());
for (int y = 0; y < getWindowHeight(); ++y)
{
for (int x = 0; x < getWindowWidth(); ++x)
{
int idx = y * getWindowWidth() + x;
EXPECT_EQ(expectedPixels[idx], renderedPixels[idx])
<< "Expected pixel at " << x << ", " << y << " to be " << expectedPixels[idx]
<< std::endl;
}
}
}
class LineLoopIndirectTest : public LineLoopTest class LineLoopIndirectTest : public LineLoopTest
{ {
protected: protected:
...@@ -318,4 +478,12 @@ TEST_P(LineLoopIndirectTest, UShortIndexIndirectBuffer) ...@@ -318,4 +478,12 @@ TEST_P(LineLoopIndirectTest, UShortIndexIndirectBuffer)
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2(LineLoopTest); ANGLE_INSTANTIATE_TEST_ES2(LineLoopTest);
ANGLE_INSTANTIATE_TEST_ES3_AND(
LineLoopPrimitiveRestartTest,
ES3_METAL(),
WithMetalForcedBufferGPUStorage(ES3_METAL()),
WithMetalMemoryBarrierAndCheapRenderPass(ES3_METAL(),
/* hasBarrier */ false,
/* cheapRenderPass */ false));
ANGLE_INSTANTIATE_TEST_ES31(LineLoopIndirectTest); ANGLE_INSTANTIATE_TEST_ES31(LineLoopIndirectTest);
...@@ -132,6 +132,9 @@ TEST_P(OcclusionQueriesTest, ClearNotCounted) ...@@ -132,6 +132,9 @@ TEST_P(OcclusionQueriesTest, ClearNotCounted)
// http://anglebug.com/4925 // http://anglebug.com/4925
ANGLE_SKIP_TEST_IF(IsD3D11()); ANGLE_SKIP_TEST_IF(IsD3D11());
// http://anglebug.com/5307
ANGLE_SKIP_TEST_IF(IsMetal() && IsNVIDIA());
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
......
...@@ -464,57 +464,184 @@ TEST_P(SimpleOperationTest, DrawLineStrip) ...@@ -464,57 +464,184 @@ TEST_P(SimpleOperationTest, DrawLineStrip)
} }
} }
class TriangleFanDrawTest : public SimpleOperationTest
{
protected:
void testSetUp() override
{
// We assume in the test the width and height are equal and we are tracing
// 2 triangles to cover half the surface like this:
ASSERT_EQ(getWindowWidth(), getWindowHeight());
mProgram.makeRaster(kBasicVertexShader, kGreenFragmentShader);
ASSERT_TRUE(mProgram.valid());
glUseProgram(mProgram);
const GLint positionLocation = glGetAttribLocation(mProgram, "position");
ASSERT_NE(-1, positionLocation);
glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer.get());
glBufferData(GL_ARRAY_BUFFER, sizeof(mVertices[0]) * mVertices.size(), mVertices.data(),
GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLocation);
glClearColor(1, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
}
void readPixels()
{
if (mReadPixels.empty())
{
mReadPixels.resize(getWindowWidth() * getWindowWidth());
}
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
mReadPixels.data());
EXPECT_GL_NO_ERROR();
}
void verifyPixelAt(int x, int y, const GLColor &expected)
{
EXPECT_EQ(mReadPixels[y * getWindowWidth() + x], expected);
}
void verifyTriangles()
{
readPixels();
// Check 4 lines accross de triangles to make sure we filled it.
// Don't check every pixel as it would slow down our tests.
for (auto x = 0; x < getWindowWidth(); x++)
{
verifyPixelAt(x, x, GLColor::green);
}
for (auto x = getWindowWidth() / 3, y = 0; x < getWindowWidth(); x++, y++)
{
verifyPixelAt(x, y, GLColor::green);
}
for (auto x = getWindowWidth() / 2, y = 0; x < getWindowWidth(); x++, y++)
{
verifyPixelAt(x, y, GLColor::green);
}
for (auto x = (getWindowWidth() / 4) * 3, y = 0; x < getWindowWidth(); x++, y++)
{
verifyPixelAt(x, y, GLColor::green);
}
// Area outside triangles
for (auto x = 0; x < getWindowWidth() - 2; x++)
{
verifyPixelAt(x, x + 2, GLColor::red);
}
}
const std::vector<Vector3> mVertices = {{0.0f, 0.0f, 0.0f},
{-1.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{1.0f, -1.0f, 0.0f},
{1.0f, 1.0f, 0.0f}};
GLBuffer mVertexBuffer;
GLProgram mProgram;
std::vector<GLColor> mReadPixels;
};
// Simple triangle fans test. // Simple triangle fans test.
TEST_P(SimpleOperationTest, DrawTriangleFan) TEST_P(TriangleFanDrawTest, DrawTriangleFan)
{ {
// We assume in the test the width and height are equal and we are tracing glClear(GL_COLOR_BUFFER_BIT);
// 2 triangles to cover half the surface like this: glDrawArrays(GL_TRIANGLE_FAN, 0, static_cast<GLsizei>(mVertices.size()));
ASSERT_EQ(getWindowWidth(), getWindowHeight());
ANGLE_GL_PROGRAM(program, kBasicVertexShader, kGreenFragmentShader); EXPECT_GL_NO_ERROR();
glUseProgram(program);
auto vertices = std::vector<Vector3>{ verifyTriangles();
{-1.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}}; }
const GLint positionLocation = glGetAttribLocation(program, "position"); // Triangle fans test with index buffer.
ASSERT_NE(-1, positionLocation); TEST_P(TriangleFanDrawTest, DrawTriangleFanElements)
{
std::vector<GLubyte> indices = {0, 1, 2, 3, 4};
GLBuffer vertexBuffer; GLBuffer indexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get()); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(), glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), indices.data(),
GL_STATIC_DRAW); GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(positionLocation);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLE_FAN, 0, static_cast<GLsizei>(vertices.size())); glDrawElements(GL_TRIANGLE_FAN, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_BYTE, 0);
glDisableVertexAttribArray(positionLocation); EXPECT_GL_NO_ERROR();
verifyTriangles();
}
// Triangle fans test with primitive restart index at the middle.
TEST_P(TriangleFanDrawTest, DrawTriangleFanPrimitiveRestartAtMiddle)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
std::vector<GLubyte> indices = {0, 1, 2, 3, 0xff, 0, 4, 3};
GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), indices.data(),
GL_STATIC_DRAW);
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glDrawElements(GL_TRIANGLE_FAN, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_BYTE, 0);
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
// Check 4 lines accross de triangles to make sure we filled it. verifyTriangles();
// Don't check every pixel as it would slow down our tests. }
for (auto x = 0; x < getWindowWidth(); x++)
{
EXPECT_PIXEL_COLOR_EQ(x, x, GLColor::green);
}
for (auto x = getWindowWidth() / 3, y = 0; x < getWindowWidth(); x++, y++) // Triangle fans test with primitive restart at begin.
{ TEST_P(TriangleFanDrawTest, DrawTriangleFanPrimitiveRestartAtBegin)
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green); {
} ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
for (auto x = getWindowWidth() / 2, y = 0; x < getWindowWidth(); x++, y++) // Primitive restart index is at middle, but we will use draw call which index offset=4.
{ std::vector<GLubyte> indices = {0, 1, 2, 3, 0xff, 0, 4, 3};
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green);
}
for (auto x = (getWindowWidth() / 4) * 3, y = 0; x < getWindowWidth(); x++, y++) GLBuffer indexBuffer;
{ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::green); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), indices.data(),
} GL_STATIC_DRAW);
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_BYTE, 0);
glDrawElements(GL_TRIANGLE_FAN, 4, GL_UNSIGNED_BYTE,
reinterpret_cast<void *>(sizeof(indices[0]) * 4));
EXPECT_GL_NO_ERROR();
verifyTriangles();
}
// Triangle fans test with primitive restart at end.
TEST_P(TriangleFanDrawTest, DrawTriangleFanPrimitiveRestartAtEnd)
{
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
std::vector<GLubyte> indices = {0, 1, 2, 3, 4, 0xff};
GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer.get());
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), indices.data(),
GL_STATIC_DRAW);
glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
glDrawElements(GL_TRIANGLE_FAN, static_cast<GLsizei>(indices.size()), GL_UNSIGNED_BYTE, 0);
EXPECT_GL_NO_ERROR();
verifyTriangles();
} }
// Simple repeated draw and swap test. // Simple repeated draw and swap test.
...@@ -1080,6 +1207,20 @@ TEST_P(SimpleOperationTest, PrimitiveModeNegativeTest) ...@@ -1080,6 +1207,20 @@ TEST_P(SimpleOperationTest, PrimitiveModeNegativeTest)
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(SimpleOperationTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
SimpleOperationTest,
ES3_METAL(),
WithMetalForcedBufferGPUStorage(ES3_METAL()),
WithMetalMemoryBarrierAndCheapRenderPass(ES3_METAL(),
/* hasBarrier */ false,
/* cheapRenderPass */ false));
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
TriangleFanDrawTest,
ES3_METAL(),
WithMetalForcedBufferGPUStorage(ES3_METAL()),
WithMetalMemoryBarrierAndCheapRenderPass(ES3_METAL(),
/* hasBarrier */ false,
/* cheapRenderPass */ false));
} // namespace } // namespace
...@@ -236,12 +236,17 @@ std::ostream &operator<<(std::ostream &stream, const PlatformParameters &pp) ...@@ -236,12 +236,17 @@ std::ostream &operator<<(std::ostream &stream, const PlatformParameters &pp)
if (pp.eglParameters.hasExplicitMemBarrierFeatureMtl == EGL_FALSE) if (pp.eglParameters.hasExplicitMemBarrierFeatureMtl == EGL_FALSE)
{ {
stream << "_NoExplicitMemoryBarrier"; stream << "_NoMetalExplicitMemoryBarrier";
} }
if (pp.eglParameters.hasCheapRenderPassFeatureMtl == EGL_FALSE) if (pp.eglParameters.hasCheapRenderPassFeatureMtl == EGL_FALSE)
{ {
stream << "_NoCheapRenderPass"; stream << "_NoMetalCheapRenderPass";
}
if (pp.eglParameters.forceBufferGPUStorageFeatureMtl == EGL_TRUE)
{
stream << "_ForceMetalBufferGPUStorage";
} }
return stream; return stream;
......
...@@ -251,6 +251,13 @@ inline PlatformParameters WithMetalMemoryBarrierAndCheapRenderPass(const Platfor ...@@ -251,6 +251,13 @@ inline PlatformParameters WithMetalMemoryBarrierAndCheapRenderPass(const Platfor
return re; return re;
} }
inline PlatformParameters WithMetalForcedBufferGPUStorage(const PlatformParameters &params)
{
PlatformParameters re = params;
re.eglParameters.forceBufferGPUStorageFeatureMtl = EGL_TRUE;
return re;
}
inline PlatformParameters WithRobustness(const PlatformParameters &params) inline PlatformParameters WithRobustness(const PlatformParameters &params)
{ {
PlatformParameters withRobustness = params; PlatformParameters withRobustness = params;
......
...@@ -63,7 +63,7 @@ struct EGLPlatformParameters ...@@ -63,7 +63,7 @@ struct EGLPlatformParameters
allocateNonZeroMemoryFeature, emulateCopyTexImage2DFromRenderbuffers, allocateNonZeroMemoryFeature, emulateCopyTexImage2DFromRenderbuffers,
shaderStencilOutputFeature, genMultipleMipsPerPassFeature, platformMethods, shaderStencilOutputFeature, genMultipleMipsPerPassFeature, platformMethods,
robustness, emulatedPrerotation, hasExplicitMemBarrierFeatureMtl, robustness, emulatedPrerotation, hasExplicitMemBarrierFeatureMtl,
hasCheapRenderPassFeatureMtl); hasCheapRenderPassFeatureMtl, forceBufferGPUStorageFeatureMtl);
} }
EGLint renderer = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE; EGLint renderer = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
...@@ -82,6 +82,7 @@ struct EGLPlatformParameters ...@@ -82,6 +82,7 @@ struct EGLPlatformParameters
uint32_t emulatedPrerotation = 0; // Can be 0, 90, 180 or 270 uint32_t emulatedPrerotation = 0; // Can be 0, 90, 180 or 270
EGLint hasExplicitMemBarrierFeatureMtl = EGL_DONT_CARE; EGLint hasExplicitMemBarrierFeatureMtl = EGL_DONT_CARE;
EGLint hasCheapRenderPassFeatureMtl = EGL_DONT_CARE; EGLint hasCheapRenderPassFeatureMtl = EGL_DONT_CARE;
EGLint forceBufferGPUStorageFeatureMtl = EGL_DONT_CARE;
angle::PlatformMethods *platformMethods = nullptr; angle::PlatformMethods *platformMethods = nullptr;
}; };
......
...@@ -231,6 +231,11 @@ bool EGLWindow::initializeDisplay(OSWindow *osWindow, ...@@ -231,6 +231,11 @@ bool EGLWindow::initializeDisplay(OSWindow *osWindow,
disabledFeatureOverrides.push_back("has_cheap_render_pass_mtl"); disabledFeatureOverrides.push_back("has_cheap_render_pass_mtl");
} }
if (params.forceBufferGPUStorageFeatureMtl == EGL_TRUE)
{
enabledFeatureOverrides.push_back("force_buffer_gpu_storage_mtl");
}
if (!disabledFeatureOverrides.empty()) if (!disabledFeatureOverrides.empty())
{ {
if (strstr(extensionString, "EGL_ANGLE_feature_control") == nullptr) if (strstr(extensionString, "EGL_ANGLE_feature_control") == nullptr)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment