Commit e5b474c6 by Le Hoang Quyen Committed by Commit Bot

Metal: support ANGLE_instance_arrays/EXT_instance_arrays

Also added TRIANGLE_FAN & LINE_LOOP instanced draws test cases. Bug: angleproject:2634 Change-Id: I84740a7221ab49710cf23767c81fa2d303aad364 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1919280Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 34e5a96e
...@@ -276,15 +276,36 @@ class ContextMtl : public ContextImpl, public mtl::Context ...@@ -276,15 +276,36 @@ class ContextMtl : public ContextImpl, public mtl::Context
const void *indices, const void *indices,
mtl::BufferRef *lastSegmentIndexBufferOut); mtl::BufferRef *lastSegmentIndexBufferOut);
angle::Result drawTriFanArrays(const gl::Context *context, GLint first, GLsizei count); angle::Result drawTriFanArrays(const gl::Context *context,
GLint first,
GLsizei count,
GLsizei instances);
angle::Result drawTriFanArraysWithBaseVertex(const gl::Context *context, angle::Result drawTriFanArraysWithBaseVertex(const gl::Context *context,
GLint first, GLint first,
GLsizei count); GLsizei count,
angle::Result drawTriFanArraysLegacy(const gl::Context *context, GLint first, GLsizei count); GLsizei instances);
angle::Result drawTriFanArraysLegacy(const gl::Context *context,
GLint first,
GLsizei count,
GLsizei instances);
angle::Result drawTriFanElements(const gl::Context *context, angle::Result drawTriFanElements(const gl::Context *context,
GLsizei count, GLsizei count,
gl::DrawElementsType type, gl::DrawElementsType type,
const void *indices); const void *indices,
GLsizei instances);
angle::Result drawArraysImpl(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount);
angle::Result drawElementsImpl(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices,
GLsizei instanceCount);
void updateViewport(FramebufferMtl *framebufferMtl, void updateViewport(FramebufferMtl *framebufferMtl,
const gl::Rectangle &viewport, const gl::Rectangle &viewport,
......
...@@ -133,7 +133,8 @@ angle::Result ContextMtl::finish(const gl::Context *context) ...@@ -133,7 +133,8 @@ angle::Result ContextMtl::finish(const gl::Context *context)
// Drawing methods. // Drawing methods.
angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *context, angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *context,
GLint first, GLint first,
GLsizei count) GLsizei count,
GLsizei instances)
{ {
uint32_t genIndicesCount; uint32_t genIndicesCount;
ANGLE_TRY(GetTriangleFanIndicesCount(this, count, &genIndicesCount)); ANGLE_TRY(GetTriangleFanIndicesCount(this, count, &genIndicesCount));
...@@ -150,20 +151,25 @@ angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *cont ...@@ -150,20 +151,25 @@ angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *cont
context, {0, static_cast<uint32_t>(count), mTriFanArraysIndexBuffer, 0})); context, {0, static_cast<uint32_t>(count), mTriFanArraysIndexBuffer, 0}));
} }
ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, 1, ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances,
gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0))); gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0)));
// Draw with the zero starting index buffer, shift the vertex index using baseVertex instanced // Draw with the zero starting index buffer, shift the vertex index using baseVertex instanced
// draw: // draw:
mRenderEncoder.drawIndexedBaseVertex(MTLPrimitiveTypeTriangle, genIndicesCount, mRenderEncoder.drawIndexedInstancedBaseVertex(MTLPrimitiveTypeTriangle, genIndicesCount,
MTLIndexTypeUInt32, mTriFanArraysIndexBuffer, 0, first); MTLIndexTypeUInt32, mTriFanArraysIndexBuffer, 0,
instances, first);
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context, angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
GLint first, GLint first,
GLsizei count) GLsizei count,
GLsizei instances)
{ {
// Legacy method is only used for GPU lacking instanced draw capabilities.
ASSERT(instances == 1);
mtl::BufferRef genIdxBuffer; mtl::BufferRef genIdxBuffer;
uint32_t genIdxBufferOffset; uint32_t genIdxBufferOffset;
uint32_t genIndicesCount; uint32_t genIndicesCount;
...@@ -173,7 +179,7 @@ angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context, ...@@ -173,7 +179,7 @@ angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
context, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer, context, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer,
genIdxBufferOffset})); genIdxBufferOffset}));
ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, 1, ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, first, count, instances,
gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0))); gl::DrawElementsType::InvalidEnum, reinterpret_cast<const void *>(0)));
mRenderEncoder.drawIndexed(MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32, mRenderEncoder.drawIndexed(MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32,
...@@ -181,23 +187,31 @@ angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context, ...@@ -181,23 +187,31 @@ angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ContextMtl::drawTriFanArrays(const gl::Context *context, GLint first, GLsizei count) angle::Result ContextMtl::drawTriFanArrays(const gl::Context *context,
GLint first,
GLsizei count,
GLsizei instances)
{ {
if (count <= 3) if (count <= 3)
{ {
return drawArrays(context, gl::PrimitiveMode::Triangles, first, count); return drawArraysInstanced(context, gl::PrimitiveMode::Triangles, first, count, instances);
} }
if (getDisplay()->getFeatures().hasBaseVertexInstancedDraw.enabled) if (getDisplay()->getFeatures().hasBaseVertexInstancedDraw.enabled)
{ {
return drawTriFanArraysWithBaseVertex(context, first, count); return drawTriFanArraysWithBaseVertex(context, first, count, instances);
} }
return drawTriFanArraysLegacy(context, first, count); return drawTriFanArraysLegacy(context, first, count, instances);
} }
angle::Result ContextMtl::drawArrays(const gl::Context *context,
gl::PrimitiveMode mode, angle::Result ContextMtl::drawArraysImpl(const gl::Context *context,
GLint first, gl::PrimitiveMode mode,
GLsizei count) GLint first,
GLsizei count,
GLsizei instances)
{ {
// Real instances count. Zero means this is not instanced draw.
GLsizei instanceCount = instances ? instances : 1;
if (mCullAllPolygons && gl::IsPolygonMode(mode)) if (mCullAllPolygons && gl::IsPolygonMode(mode))
{ {
return angle::Result::Continue; return angle::Result::Continue;
...@@ -205,27 +219,46 @@ angle::Result ContextMtl::drawArrays(const gl::Context *context, ...@@ -205,27 +219,46 @@ angle::Result ContextMtl::drawArrays(const gl::Context *context,
if (mode == gl::PrimitiveMode::TriangleFan) if (mode == gl::PrimitiveMode::TriangleFan)
{ {
return drawTriFanArrays(context, first, count); return drawTriFanArrays(context, first, count, instanceCount);
} }
MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode); MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);
ANGLE_TRY( ANGLE_TRY(setupDraw(context, mode, first, count, instances, gl::DrawElementsType::InvalidEnum,
setupDraw(context, mode, first, count, 1, gl::DrawElementsType::InvalidEnum, nullptr)); nullptr));
mRenderEncoder.draw(mtlType, first, count); if (instances == 0)
{
// This method is called from normal drawArrays()
mRenderEncoder.draw(mtlType, first, count);
}
else
{
mRenderEncoder.drawInstanced(mtlType, first, count, instanceCount);
}
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ContextMtl::drawArrays(const gl::Context *context,
gl::PrimitiveMode mode,
GLint first,
GLsizei count)
{
return drawArraysImpl(context, mode, first, count, 0);
}
angle::Result ContextMtl::drawArraysInstanced(const gl::Context *context, angle::Result ContextMtl::drawArraysInstanced(const gl::Context *context,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
GLint first, GLint first,
GLsizei count, GLsizei count,
GLsizei instanceCount) GLsizei instances)
{ {
// NOTE(hqle): ES 3.0 if (instances == 0)
UNIMPLEMENTED(); {
return angle::Result::Stop; return angle::Result::Continue;
}
return drawArraysImpl(context, mode, first, count, instances);
} }
angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *context, angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *context,
...@@ -242,7 +275,8 @@ angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *con ...@@ -242,7 +275,8 @@ angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *con
angle::Result ContextMtl::drawTriFanElements(const gl::Context *context, angle::Result ContextMtl::drawTriFanElements(const gl::Context *context,
GLsizei count, GLsizei count,
gl::DrawElementsType type, gl::DrawElementsType type,
const void *indices) const void *indices,
GLsizei instances)
{ {
if (count > 3) if (count > 3)
{ {
...@@ -257,21 +291,29 @@ angle::Result ContextMtl::drawTriFanElements(const gl::Context *context, ...@@ -257,21 +291,29 @@ angle::Result ContextMtl::drawTriFanElements(const gl::Context *context,
ANGLE_TRY(mTriFanIndexBuffer.commit(this)); ANGLE_TRY(mTriFanIndexBuffer.commit(this));
ANGLE_TRY(setupDraw(context, gl::PrimitiveMode::TriangleFan, 0, count, 1, type, indices)); ANGLE_TRY(
setupDraw(context, gl::PrimitiveMode::TriangleFan, 0, count, instances, type, indices));
mRenderEncoder.drawIndexed(MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32, mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeTriangle, genIndicesCount,
genIdxBuffer, genIdxBufferOffset); MTLIndexTypeUInt32, genIdxBuffer, genIdxBufferOffset,
instances);
return angle::Result::Continue; return angle::Result::Continue;
} // if (count > 3) } // if (count > 3)
return drawElements(context, gl::PrimitiveMode::Triangles, count, type, indices); return drawElementsInstanced(context, gl::PrimitiveMode::Triangles, count, type, indices,
instances);
} }
angle::Result ContextMtl::drawElements(const gl::Context *context,
gl::PrimitiveMode mode, angle::Result ContextMtl::drawElementsImpl(const gl::Context *context,
GLsizei count, gl::PrimitiveMode mode,
gl::DrawElementsType type, GLsizei count,
const void *indices) gl::DrawElementsType type,
const void *indices,
GLsizei instances)
{ {
// Real instances count. Zero means this is not instanced draw.
GLsizei instanceCount = instances ? instances : 1;
if (mCullAllPolygons && gl::IsPolygonMode(mode)) if (mCullAllPolygons && gl::IsPolygonMode(mode))
{ {
return angle::Result::Continue; return angle::Result::Continue;
...@@ -279,7 +321,7 @@ angle::Result ContextMtl::drawElements(const gl::Context *context, ...@@ -279,7 +321,7 @@ angle::Result ContextMtl::drawElements(const gl::Context *context,
if (mode == gl::PrimitiveMode::TriangleFan) if (mode == gl::PrimitiveMode::TriangleFan)
{ {
return drawTriFanElements(context, count, type, indices); return drawTriFanElements(context, count, type, indices, instanceCount);
} }
mtl::BufferRef idxBuffer; mtl::BufferRef idxBuffer;
...@@ -292,16 +334,36 @@ angle::Result ContextMtl::drawElements(const gl::Context *context, ...@@ -292,16 +334,36 @@ angle::Result ContextMtl::drawElements(const gl::Context *context,
ASSERT(idxBuffer); ASSERT(idxBuffer);
ASSERT((convertedOffset % mtl::kIndexBufferOffsetAlignment) == 0); ASSERT((convertedOffset % mtl::kIndexBufferOffsetAlignment) == 0);
ANGLE_TRY(setupDraw(context, mode, 0, count, 1, type, indices)); ANGLE_TRY(setupDraw(context, mode, 0, count, instances, type, indices));
MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode); MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);
MTLIndexType mtlIdxType = mtl::GetIndexType(convertedType); MTLIndexType mtlIdxType = mtl::GetIndexType(convertedType);
mRenderEncoder.drawIndexed(mtlType, count, mtlIdxType, idxBuffer, convertedOffset); if (instances == 0)
{
// Normal draw
mRenderEncoder.drawIndexed(mtlType, count, mtlIdxType, idxBuffer, convertedOffset);
}
else
{
// Instanced draw
mRenderEncoder.drawIndexedInstanced(mtlType, count, mtlIdxType, idxBuffer, convertedOffset,
instanceCount);
}
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result ContextMtl::drawElements(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
gl::DrawElementsType type,
const void *indices)
{
return drawElementsImpl(context, mode, count, type, indices, 0);
}
angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context, angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
GLsizei count, GLsizei count,
...@@ -309,10 +371,13 @@ angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context, ...@@ -309,10 +371,13 @@ angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context,
const void *indices, const void *indices,
GLsizei instanceCount) GLsizei instanceCount)
{ {
// NOTE(hqle): ES 3.0 if (instanceCount == 0)
UNIMPLEMENTED(); {
return angle::Result::Stop; return angle::Result::Continue;
}
return drawElementsImpl(context, mode, count, type, indices, instanceCount);
} }
angle::Result ContextMtl::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context, angle::Result ContextMtl::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
GLsizei count, GLsizei count,
...@@ -1242,12 +1307,15 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context, ...@@ -1242,12 +1307,15 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
gl::PrimitiveMode mode, gl::PrimitiveMode mode,
GLint firstVertex, GLint firstVertex,
GLsizei vertexOrIndexCount, GLsizei vertexOrIndexCount,
GLsizei instanceCount, GLsizei instances,
gl::DrawElementsType indexTypeOrNone, gl::DrawElementsType indexTypeOrNone,
const void *indices) const void *indices)
{ {
ASSERT(mProgram); ASSERT(mProgram);
// instances=0 means no instanced draw.
GLsizei instanceCount = instances ? instances : 1;
mtl::BufferRef lineLoopLastSegmentIndexBuffer; mtl::BufferRef lineLoopLastSegmentIndexBuffer;
if (mode == gl::PrimitiveMode::LineLoop) if (mode == gl::PrimitiveMode::LineLoop)
{ {
...@@ -1349,8 +1417,16 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context, ...@@ -1349,8 +1417,16 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
if (mode == gl::PrimitiveMode::LineLoop) if (mode == gl::PrimitiveMode::LineLoop)
{ {
// Draw last segment of line loop here // Draw last segment of line loop here
mRenderEncoder.drawIndexed(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32, if (instances == 0)
lineLoopLastSegmentIndexBuffer, 0); {
mRenderEncoder.drawIndexed(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32,
lineLoopLastSegmentIndexBuffer, 0);
}
else
{
mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32,
lineLoopLastSegmentIndexBuffer, 0, instanceCount);
}
} }
return angle::Result::Continue; return angle::Result::Continue;
...@@ -1364,8 +1440,6 @@ angle::Result ContextMtl::genLineLoopLastSegment(const gl::Context *context, ...@@ -1364,8 +1440,6 @@ angle::Result ContextMtl::genLineLoopLastSegment(const gl::Context *context,
const void *indices, const void *indices,
mtl::BufferRef *lastSegmentIndexBufferOut) mtl::BufferRef *lastSegmentIndexBufferOut)
{ {
ASSERT(instanceCount == 1);
mLineLoopIndexBuffer.releaseInFlightBuffers(this); mLineLoopIndexBuffer.releaseInFlightBuffers(this);
mtl::BufferRef newBuffer; mtl::BufferRef newBuffer;
......
...@@ -572,9 +572,8 @@ void DisplayMtl::initializeExtensions() const ...@@ -572,9 +572,8 @@ void DisplayMtl::initializeExtensions() const
mNativeExtensions.semaphore = false; mNativeExtensions.semaphore = false;
mNativeExtensions.semaphoreFd = false; mNativeExtensions.semaphoreFd = false;
// TODO: Enable this always and emulate instanced draws if any divisor exceeds the maximum mNativeExtensions.instancedArraysANGLE = mFeatures.hasBaseVertexInstancedDraw.enabled;
// supported. http://anglebug.com/2672 mNativeExtensions.instancedArraysEXT = mNativeExtensions.instancedArraysANGLE;
mNativeExtensions.instancedArraysANGLE = false;
mNativeExtensions.robustBufferAccessBehavior = false; mNativeExtensions.robustBufferAccessBehavior = false;
......
...@@ -264,6 +264,7 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext, ...@@ -264,6 +264,7 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
mVertexArrayDirty = false; mVertexArrayDirty = false;
const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes(); const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
mtl::VertexDesc &desc = *vertexDescOut; mtl::VertexDesc &desc = *vertexDescOut;
...@@ -278,7 +279,8 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext, ...@@ -278,7 +279,8 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
for (uint32_t v = 0; v < mtl::kMaxVertexAttribs; ++v) for (uint32_t v = 0; v < mtl::kMaxVertexAttribs; ++v)
{ {
const auto &attrib = attribs[v]; const auto &attrib = attribs[v];
const gl::VertexBinding &binding = bindings[attrib.bindingIndex];
desc.attributes[v].offset = mCurrentArrayBufferOffsets[v]; desc.attributes[v].offset = mCurrentArrayBufferOffsets[v];
desc.attributes[v].format = mCurrentArrayBufferFormats[v]; desc.attributes[v].format = mCurrentArrayBufferFormats[v];
...@@ -296,9 +298,17 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext, ...@@ -296,9 +298,17 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
desc.attributes[v].bufferIndex = bufferIdx; desc.attributes[v].bufferIndex = bufferIdx;
ASSERT(bufferIdx < mtl::kMaxVertexAttribs); ASSERT(bufferIdx < mtl::kMaxVertexAttribs);
desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerVertex; if (binding.getDivisor() == 0)
desc.layouts[bufferIdx].stepRate = 1; {
desc.layouts[bufferIdx].stride = mCurrentArrayBufferStrides[v]; desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerVertex;
desc.layouts[bufferIdx].stepRate = 1;
}
else
{
desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerInstance;
desc.layouts[bufferIdx].stepRate = binding.getDivisor();
}
desc.layouts[bufferIdx].stride = mCurrentArrayBufferStrides[v];
cmdEncoder->setVertexBuffer(mCurrentArrayBuffers[v]->getCurrentBuffer(glContext), 0, cmdEncoder->setVertexBuffer(mCurrentArrayBuffers[v]->getCurrentBuffer(glContext), 0,
bufferIdx); bufferIdx);
...@@ -351,32 +361,35 @@ angle::Result VertexArrayMtl::updateClientAttribs(const gl::Context *context, ...@@ -351,32 +361,35 @@ angle::Result VertexArrayMtl::updateClientAttribs(const gl::Context *context,
const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer); const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
ASSERT(src); ASSERT(src);
if (binding.getDivisor() > 0) GLint startElement;
size_t elementCount;
if (binding.getDivisor() == 0)
{ {
ANGLE_UNUSED_VARIABLE(instanceCount); // Per vertex attribute
// NOTE(hqle): support ES 3.0. startElement = startVertex;
// instanced attrib elementCount = vertexCount;
UNREACHABLE();
} }
else else
{ {
// Allocate space for startVertex + vertexCount so indexing will work. If we don't // Per instance attribute
// start at zero all the indices will be off. startElement = 0;
// Only vertexCount vertices will be used by the upcoming draw so that is all we copy. elementCount = UnsignedCeilDivide(instanceCount, binding.getDivisor());
size_t bytesToAllocate = (startVertex + vertexCount) * stride;
src += startVertex * binding.getStride();
size_t destOffset = startVertex * stride;
ANGLE_TRY(StreamVertexData(contextMtl, &mDynamicVertexData, src, bytesToAllocate,
destOffset, vertexCount, binding.getStride(),
vertexFormat.vertexLoadFunction,
&mConvertedArrayBufferHolders[attribIndex],
&mCurrentArrayBufferOffsets[attribIndex]));
mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex];
mCurrentArrayBufferFormats[attribIndex] = vertexFormat.metalFormat;
mCurrentArrayBufferStrides[attribIndex] = stride;
} }
// Allocate space for startElement + elementCount so indexing will work. If we don't
// start at zero all the indices will be off.
// Only elementCount vertices will be used by the upcoming draw so that is all we copy.
size_t bytesToAllocate = (startElement + elementCount) * stride;
src += startElement * binding.getStride();
size_t destOffset = startElement * stride;
ANGLE_TRY(StreamVertexData(
contextMtl, &mDynamicVertexData, src, bytesToAllocate, destOffset, elementCount,
binding.getStride(), vertexFormat.vertexLoadFunction,
&mConvertedArrayBufferHolders[attribIndex], &mCurrentArrayBufferOffsets[attribIndex]));
mCurrentArrayBuffers[attribIndex] = &mConvertedArrayBufferHolders[attribIndex];
mCurrentArrayBufferFormats[attribIndex] = vertexFormat.metalFormat;
mCurrentArrayBufferStrides[attribIndex] = stride;
} }
mVertexArrayDirty = true; mVertexArrayDirty = true;
......
...@@ -218,17 +218,28 @@ class RenderCommandEncoder final : public CommandEncoder ...@@ -218,17 +218,28 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &draw(MTLPrimitiveType primitiveType, RenderCommandEncoder &draw(MTLPrimitiveType primitiveType,
uint32_t vertexStart, uint32_t vertexStart,
uint32_t vertexCount); uint32_t vertexCount);
RenderCommandEncoder &drawInstanced(MTLPrimitiveType primitiveType,
uint32_t vertexStart,
uint32_t vertexCount,
uint32_t instances);
RenderCommandEncoder &drawIndexed(MTLPrimitiveType primitiveType, RenderCommandEncoder &drawIndexed(MTLPrimitiveType primitiveType,
uint32_t indexCount, uint32_t indexCount,
MTLIndexType indexType, MTLIndexType indexType,
const BufferRef &indexBuffer, const BufferRef &indexBuffer,
size_t bufferOffset); size_t bufferOffset);
RenderCommandEncoder &drawIndexedBaseVertex(MTLPrimitiveType primitiveType, RenderCommandEncoder &drawIndexedInstanced(MTLPrimitiveType primitiveType,
uint32_t indexCount, uint32_t indexCount,
MTLIndexType indexType, MTLIndexType indexType,
const BufferRef &indexBuffer, const BufferRef &indexBuffer,
size_t bufferOffset, size_t bufferOffset,
uint32_t baseVertex); uint32_t instances);
RenderCommandEncoder &drawIndexedInstancedBaseVertex(MTLPrimitiveType primitiveType,
uint32_t indexCount,
MTLIndexType indexType,
const BufferRef &indexBuffer,
size_t bufferOffset,
uint32_t instances,
uint32_t baseVertex);
RenderCommandEncoder &setColorStoreAction(MTLStoreAction action, uint32_t colorAttachmentIndex); RenderCommandEncoder &setColorStoreAction(MTLStoreAction action, uint32_t colorAttachmentIndex);
// Set store action for every color attachment. // Set store action for every color attachment.
......
...@@ -673,6 +673,20 @@ RenderCommandEncoder &RenderCommandEncoder::draw(MTLPrimitiveType primitiveType, ...@@ -673,6 +673,20 @@ RenderCommandEncoder &RenderCommandEncoder::draw(MTLPrimitiveType primitiveType,
return *this; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::drawInstanced(MTLPrimitiveType primitiveType,
uint32_t vertexStart,
uint32_t vertexCount,
uint32_t instances)
{
[get() drawPrimitives:primitiveType
vertexStart:vertexStart
vertexCount:vertexCount
instanceCount:instances];
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primitiveType, RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primitiveType,
uint32_t indexCount, uint32_t indexCount,
MTLIndexType indexType, MTLIndexType indexType,
...@@ -694,12 +708,37 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primiti ...@@ -694,12 +708,37 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primiti
return *this; return *this;
} }
RenderCommandEncoder &RenderCommandEncoder::drawIndexedBaseVertex(MTLPrimitiveType primitiveType, RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstanced(MTLPrimitiveType primitiveType,
uint32_t indexCount, uint32_t indexCount,
MTLIndexType indexType, MTLIndexType indexType,
const BufferRef &indexBuffer, const BufferRef &indexBuffer,
size_t bufferOffset, size_t bufferOffset,
uint32_t baseVertex) uint32_t instances)
{
if (!indexBuffer)
{
return *this;
}
cmdBuffer().setReadDependency(indexBuffer);
[get() drawIndexedPrimitives:primitiveType
indexCount:indexCount
indexType:indexType
indexBuffer:indexBuffer->get()
indexBufferOffset:bufferOffset
instanceCount:instances];
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstancedBaseVertex(
MTLPrimitiveType primitiveType,
uint32_t indexCount,
MTLIndexType indexType,
const BufferRef &indexBuffer,
size_t bufferOffset,
uint32_t instances,
uint32_t baseVertex)
{ {
if (!indexBuffer) if (!indexBuffer)
{ {
...@@ -712,7 +751,7 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedBaseVertex(MTLPrimitiveTy ...@@ -712,7 +751,7 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedBaseVertex(MTLPrimitiveTy
indexType:indexType indexType:indexType
indexBuffer:indexBuffer->get() indexBuffer:indexBuffer->get()
indexBufferOffset:bufferOffset indexBufferOffset:bufferOffset
instanceCount:1 instanceCount:instances
baseVertex:baseVertex baseVertex:baseVertex
baseInstance:0]; baseInstance:0];
......
...@@ -407,6 +407,9 @@ angle::Result Buffer::reset(ContextMtl *context, size_t size, const uint8_t *dat ...@@ -407,6 +407,9 @@ angle::Result Buffer::reset(ContextMtl *context, size_t size, const uint8_t *dat
id<MTLDevice> metalDevice = context->getMetalDevice(); id<MTLDevice> metalDevice = context->getMetalDevice();
options = 0; options = 0;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
options |= MTLResourceStorageModeManaged;
#endif
if (data) if (data)
{ {
...@@ -439,7 +442,12 @@ uint8_t *Buffer::map(ContextMtl *context) ...@@ -439,7 +442,12 @@ uint8_t *Buffer::map(ContextMtl *context)
return reinterpret_cast<uint8_t *>([get() contents]); return reinterpret_cast<uint8_t *>([get() contents]);
} }
void Buffer::unmap(ContextMtl *context) {} void Buffer::unmap(ContextMtl *context)
{
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
[get() didModifyRange:NSMakeRange(0, size())];
#endif
}
size_t Buffer::size() const size_t Buffer::size() const
{ {
......
...@@ -14,7 +14,8 @@ namespace ...@@ -14,7 +14,8 @@ namespace
enum Geometry enum Geometry
{ {
Quad, Quad,
Point Point,
TriFan,
}; };
enum Storage enum Storage
{ {
...@@ -145,8 +146,20 @@ class InstancingTest : public ANGLETest ...@@ -145,8 +146,20 @@ class InstancingTest : public ANGLETest
glVertexAttribDivisorEXT(instanceAttrib, divisor); glVertexAttribDivisorEXT(instanceAttrib, divisor);
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, const void *vertices;
geometry == Point ? kPointVertices : kQuadVertices); switch (geometry)
{
case Point:
vertices = kPointVertices;
break;
case Quad:
vertices = kQuadVertices;
break;
case TriFan:
vertices = kTriFanVertices;
break;
}
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(positionAttrib); glEnableVertexAttribArray(positionAttrib);
if (vendor == Angle) if (vendor == Angle)
glVertexAttribDivisorANGLE(positionAttrib, 0); glVertexAttribDivisorANGLE(positionAttrib, 0);
...@@ -169,7 +182,7 @@ class InstancingTest : public ANGLETest ...@@ -169,7 +182,7 @@ class InstancingTest : public ANGLETest
else else
glDrawArraysInstancedEXT(GL_POINTS, offset, 4 /*vertices*/, numInstance); glDrawArraysInstancedEXT(GL_POINTS, offset, 4 /*vertices*/, numInstance);
} }
else else if (geometry == Quad)
{ {
if (draw == Indexed) if (draw == Indexed)
if (vendor == Angle) if (vendor == Angle)
...@@ -183,6 +196,39 @@ class InstancingTest : public ANGLETest ...@@ -183,6 +196,39 @@ class InstancingTest : public ANGLETest
else else
glDrawArraysInstancedEXT(GL_TRIANGLES, offset, 6 /*vertices*/, numInstance); glDrawArraysInstancedEXT(GL_TRIANGLES, offset, 6 /*vertices*/, numInstance);
} }
else if (geometry == TriFan)
{
if (draw == Indexed)
{
if (storage == Buffer)
{
GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kTriFanIndices), kTriFanIndices,
GL_STATIC_DRAW);
if (vendor == Angle)
glDrawElementsInstancedANGLE(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
GL_UNSIGNED_BYTE, 0, numInstance);
else
glDrawElementsInstancedEXT(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
GL_UNSIGNED_BYTE, 0, numInstance);
}
else
{
if (vendor == Angle)
glDrawElementsInstancedANGLE(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
GL_UNSIGNED_BYTE, kTriFanIndices, numInstance);
else
glDrawElementsInstancedEXT(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
GL_UNSIGNED_BYTE, kTriFanIndices, numInstance);
}
}
else if (vendor == Angle)
glDrawArraysInstancedANGLE(GL_TRIANGLE_FAN, offset, 8 /*vertices*/, numInstance);
else
glDrawArraysInstancedEXT(GL_TRIANGLE_FAN, offset, 8 /*vertices*/, numInstance);
}
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
checkDrawing(lastDrawn); checkDrawing(lastDrawn);
...@@ -228,6 +274,28 @@ class InstancingTest : public ANGLETest ...@@ -228,6 +274,28 @@ class InstancingTest : public ANGLETest
-1, -1, -1, -1,
}; };
// Vertices 0-7 form a quad (triangle fan) filling the first "slice" of the window.
// Vertices 8-15 are the same.
static constexpr GLfloat kTriFanVertices[] = {
-1, -1,
1, -1,
1, -1 + 0.5f * kDrawSize,
1, -1 + kDrawSize,
0.5f, -1 + kDrawSize,
0, -1 + kDrawSize,
-0.5f, -1 + kDrawSize,
-1, -1 + kDrawSize,
-1, -1,
1, -1,
1, -1 + 0.5f * kDrawSize,
1, -1 + kDrawSize,
0.5f, -1 + kDrawSize,
0, -1 + kDrawSize,
-0.5f, -1 + kDrawSize,
-1, -1 + kDrawSize,
};
// Points 0-3 are spread across the first "slice." // Points 0-3 are spread across the first "slice."
// Points 2-4 are the same. // Points 2-4 are the same.
static constexpr GLfloat kPointVertices[] = { static constexpr GLfloat kPointVertices[] = {
...@@ -243,6 +311,9 @@ class InstancingTest : public ANGLETest ...@@ -243,6 +311,9 @@ class InstancingTest : public ANGLETest
// Same two triangles as described above. // Same two triangles as described above.
static constexpr GLushort kQuadIndices[] = {2, 9, 7, 5, 6, 4}; static constexpr GLushort kQuadIndices[] = {2, 9, 7, 5, 6, 4};
// Same triangle fan as described above.
static constexpr GLubyte kTriFanIndices[] = {0, 9, 10, 3, 4, 5, 14, 7};
// Same four points as described above. // Same four points as described above.
static constexpr GLushort kPointIndices[] = {1, 5, 3, 2}; static constexpr GLushort kPointIndices[] = {1, 5, 3, 2};
}; };
...@@ -250,8 +321,10 @@ class InstancingTest : public ANGLETest ...@@ -250,8 +321,10 @@ class InstancingTest : public ANGLETest
constexpr unsigned InstancingTest::kMaxDrawn; constexpr unsigned InstancingTest::kMaxDrawn;
constexpr float InstancingTest::kDrawSize; constexpr float InstancingTest::kDrawSize;
constexpr GLfloat InstancingTest::kQuadVertices[]; constexpr GLfloat InstancingTest::kQuadVertices[];
constexpr GLfloat InstancingTest::kTriFanVertices[];
constexpr GLfloat InstancingTest::kPointVertices[]; constexpr GLfloat InstancingTest::kPointVertices[];
constexpr GLushort InstancingTest::kQuadIndices[]; constexpr GLushort InstancingTest::kQuadIndices[];
constexpr GLubyte InstancingTest::kTriFanIndices[];
constexpr GLushort InstancingTest::kPointIndices[]; constexpr GLushort InstancingTest::kPointIndices[];
#define TEST_INDEXED(attrib, geometry, storage, vendor) \ #define TEST_INDEXED(attrib, geometry, storage, vendor) \
...@@ -278,6 +351,14 @@ constexpr GLushort InstancingTest::kPointIndices[]; ...@@ -278,6 +351,14 @@ constexpr GLushort InstancingTest::kPointIndices[];
// //
// Tests with a non-zero 'offset' check that "first" parameter to glDrawArraysInstancedANGLE is only // Tests with a non-zero 'offset' check that "first" parameter to glDrawArraysInstancedANGLE is only
// an offset into the non-instanced vertex attributes. // an offset into the non-instanced vertex attributes.
TEST_INDEXED(0, TriFan, Buffer, Angle)
TEST_INDEXED(0, TriFan, Memory, Angle)
TEST_INDEXED(1, TriFan, Buffer, Angle)
TEST_INDEXED(1, TriFan, Memory, Angle)
TEST_INDEXED(0, TriFan, Buffer, Ext)
TEST_INDEXED(0, TriFan, Memory, Ext)
TEST_INDEXED(1, TriFan, Buffer, Ext)
TEST_INDEXED(1, TriFan, Memory, Ext)
TEST_INDEXED(0, Quad, Buffer, Angle) TEST_INDEXED(0, Quad, Buffer, Angle)
TEST_INDEXED(0, Quad, Memory, Angle) TEST_INDEXED(0, Quad, Memory, Angle)
TEST_INDEXED(1, Quad, Buffer, Angle) TEST_INDEXED(1, Quad, Buffer, Angle)
...@@ -295,7 +376,23 @@ TEST_INDEXED(0, Point, Memory, Ext) ...@@ -295,7 +376,23 @@ TEST_INDEXED(0, Point, Memory, Ext)
TEST_INDEXED(1, Point, Buffer, Ext) TEST_INDEXED(1, Point, Buffer, Ext)
TEST_INDEXED(1, Point, Memory, Ext) TEST_INDEXED(1, Point, Memory, Ext)
// offset should be 0 or 4 for quads // offset should be 0 or 4 for quads, 0 or 8 for triangle fan
TEST_NONINDEXED(0, TriFan, Buffer, Angle, 0)
TEST_NONINDEXED(0, TriFan, Buffer, Angle, 8)
TEST_NONINDEXED(0, TriFan, Memory, Angle, 0)
TEST_NONINDEXED(0, TriFan, Memory, Angle, 8)
TEST_NONINDEXED(1, TriFan, Buffer, Angle, 0)
TEST_NONINDEXED(1, TriFan, Buffer, Angle, 8)
TEST_NONINDEXED(1, TriFan, Memory, Angle, 0)
TEST_NONINDEXED(1, TriFan, Memory, Angle, 8)
TEST_NONINDEXED(0, TriFan, Buffer, Ext, 0)
TEST_NONINDEXED(0, TriFan, Buffer, Ext, 8)
TEST_NONINDEXED(0, TriFan, Memory, Ext, 0)
TEST_NONINDEXED(0, TriFan, Memory, Ext, 8)
TEST_NONINDEXED(1, TriFan, Buffer, Ext, 0)
TEST_NONINDEXED(1, TriFan, Buffer, Ext, 8)
TEST_NONINDEXED(1, TriFan, Memory, Ext, 0)
TEST_NONINDEXED(1, TriFan, Memory, Ext, 8)
TEST_NONINDEXED(0, Quad, Buffer, Angle, 0) TEST_NONINDEXED(0, Quad, Buffer, Angle, 0)
TEST_NONINDEXED(0, Quad, Buffer, Angle, 4) TEST_NONINDEXED(0, Quad, Buffer, Angle, 4)
TEST_NONINDEXED(0, Quad, Memory, Angle, 0) TEST_NONINDEXED(0, Quad, Memory, Angle, 0)
...@@ -384,6 +481,129 @@ TEST_DIVISOR(32, 8) ...@@ -384,6 +481,129 @@ TEST_DIVISOR(32, 8)
TEST_DIVISOR(34, 3) TEST_DIVISOR(34, 3)
TEST_DIVISOR(34, 30) TEST_DIVISOR(34, 30)
// Test line loop instanced draw
TEST_P(InstancingTest, LineLoop)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
// TODO(hqle): D3D9 seems to draw very slow here, probably due to invariant
ANGLE_SKIP_TEST_IF(IsD3D9());
constexpr char kVS[] = R"(
attribute vec2 a_position;
// x,y = offset, z = scale
attribute 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"(
precision highp float;
void main()
{
gl_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();
constexpr GLfloat vertices[] = {
0.1, 0.1, -0.1, 0.1, -0.1, -0.1, 0.1, -0.1,
};
constexpr GLfloat transform[] = {
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;
std::vector<GLColor> expectedPixels(getWindowWidth() * getWindowHeight());
// Draw in non-instanced way
glClear(GL_COLOR_BUFFER_BIT);
glEnableVertexAttribArray(0);
glDisableVertexAttribArray(1);
glVertexAttribDivisorANGLE(0, 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
for (size_t i = 0; i < instances; ++i)
{
glVertexAttrib3fv(1, transform + 3 * i);
glDrawElements(GL_LINE_STRIP, ArraySize(lineloopAsStripIndices), GL_UNSIGNED_SHORT,
lineloopAsStripIndices);
}
glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
expectedPixels.data());
ASSERT_GL_NO_ERROR();
// Draw in instanced way:
glClear(GL_COLOR_BUFFER_BIT);
GLBuffer vertexBuffer[2];
GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(lineloopAsStripIndices), lineloopAsStripIndices,
GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribDivisorANGLE(0, 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);
glVertexAttribDivisorANGLE(1, 1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArraysInstancedANGLE(GL_LINE_LOOP, 0, ArraySize(vertices) / 2, instances);
for (int y = 0; y < getWindowHeight(); ++y)
{
for (int x = 0; x < getWindowWidth(); ++x)
{
int idx = y * getWindowWidth() + x;
GLColor expectedColor = expectedPixels[idx];
EXPECT_PIXEL_COLOR_EQ(x, y, expectedColor) << std::endl;
}
}
glClear(GL_COLOR_BUFFER_BIT);
glDrawElementsInstancedANGLE(GL_LINE_LOOP, ArraySize(lineloopAsStripIndices) - 1,
GL_UNSIGNED_SHORT, 0, instances);
for (int y = 0; y < getWindowHeight(); ++y)
{
for (int x = 0; x < getWindowWidth(); ++x)
{
int idx = y * getWindowWidth() + x;
GLColor expectedColor = expectedPixels[idx];
EXPECT_PIXEL_COLOR_EQ(x, y, expectedColor) << std::endl;
}
}
}
class InstancingTestES3 : public InstancingTest class InstancingTestES3 : public InstancingTest
{ {
public: public:
......
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