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
const void *indices,
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,
GLint first,
GLsizei count);
angle::Result drawTriFanArraysLegacy(const gl::Context *context, GLint first, GLsizei count);
GLsizei count,
GLsizei instances);
angle::Result drawTriFanArraysLegacy(const gl::Context *context,
GLint first,
GLsizei count,
GLsizei instances);
angle::Result drawTriFanElements(const gl::Context *context,
GLsizei count,
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,
const gl::Rectangle &viewport,
......
......@@ -133,7 +133,8 @@ angle::Result ContextMtl::finish(const gl::Context *context)
// Drawing methods.
angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *context,
GLint first,
GLsizei count)
GLsizei count,
GLsizei instances)
{
uint32_t genIndicesCount;
ANGLE_TRY(GetTriangleFanIndicesCount(this, count, &genIndicesCount));
......@@ -150,20 +151,25 @@ angle::Result ContextMtl::drawTriFanArraysWithBaseVertex(const gl::Context *cont
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)));
// Draw with the zero starting index buffer, shift the vertex index using baseVertex instanced
// draw:
mRenderEncoder.drawIndexedBaseVertex(MTLPrimitiveTypeTriangle, genIndicesCount,
MTLIndexTypeUInt32, mTriFanArraysIndexBuffer, 0, first);
mRenderEncoder.drawIndexedInstancedBaseVertex(MTLPrimitiveTypeTriangle, genIndicesCount,
MTLIndexTypeUInt32, mTriFanArraysIndexBuffer, 0,
instances, first);
return angle::Result::Continue;
}
angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
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;
uint32_t genIdxBufferOffset;
uint32_t genIndicesCount;
......@@ -173,7 +179,7 @@ angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
context, {static_cast<uint32_t>(first), static_cast<uint32_t>(count), genIdxBuffer,
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)));
mRenderEncoder.drawIndexed(MTLPrimitiveTypeTriangle, genIndicesCount, MTLIndexTypeUInt32,
......@@ -181,23 +187,31 @@ angle::Result ContextMtl::drawTriFanArraysLegacy(const gl::Context *context,
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)
{
return drawArrays(context, gl::PrimitiveMode::Triangles, first, count);
return drawArraysInstanced(context, gl::PrimitiveMode::Triangles, first, count, instances);
}
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,
GLint first,
GLsizei count)
angle::Result ContextMtl::drawArraysImpl(const gl::Context *context,
gl::PrimitiveMode mode,
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))
{
return angle::Result::Continue;
......@@ -205,27 +219,46 @@ angle::Result ContextMtl::drawArrays(const gl::Context *context,
if (mode == gl::PrimitiveMode::TriangleFan)
{
return drawTriFanArrays(context, first, count);
return drawTriFanArrays(context, first, count, instanceCount);
}
MTLPrimitiveType mtlType = mtl::GetPrimitiveType(mode);
ANGLE_TRY(
setupDraw(context, mode, first, count, 1, gl::DrawElementsType::InvalidEnum, nullptr));
ANGLE_TRY(setupDraw(context, mode, first, count, instances, gl::DrawElementsType::InvalidEnum,
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;
}
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,
gl::PrimitiveMode mode,
GLint first,
GLsizei count,
GLsizei instanceCount)
GLsizei instances)
{
// NOTE(hqle): ES 3.0
UNIMPLEMENTED();
return angle::Result::Stop;
if (instances == 0)
{
return angle::Result::Continue;
}
return drawArraysImpl(context, mode, first, count, instances);
}
angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *context,
......@@ -242,7 +275,8 @@ angle::Result ContextMtl::drawArraysInstancedBaseInstance(const gl::Context *con
angle::Result ContextMtl::drawTriFanElements(const gl::Context *context,
GLsizei count,
gl::DrawElementsType type,
const void *indices)
const void *indices,
GLsizei instances)
{
if (count > 3)
{
......@@ -257,21 +291,29 @@ angle::Result ContextMtl::drawTriFanElements(const gl::Context *context,
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,
genIdxBuffer, genIdxBufferOffset);
mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeTriangle, genIndicesCount,
MTLIndexTypeUInt32, genIdxBuffer, genIdxBufferOffset,
instances);
return angle::Result::Continue;
} // 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,
GLsizei count,
gl::DrawElementsType type,
const void *indices)
angle::Result ContextMtl::drawElementsImpl(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
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))
{
return angle::Result::Continue;
......@@ -279,7 +321,7 @@ angle::Result ContextMtl::drawElements(const gl::Context *context,
if (mode == gl::PrimitiveMode::TriangleFan)
{
return drawTriFanElements(context, count, type, indices);
return drawTriFanElements(context, count, type, indices, instanceCount);
}
mtl::BufferRef idxBuffer;
......@@ -292,16 +334,36 @@ angle::Result ContextMtl::drawElements(const gl::Context *context,
ASSERT(idxBuffer);
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);
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;
}
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,
gl::PrimitiveMode mode,
GLsizei count,
......@@ -309,10 +371,13 @@ angle::Result ContextMtl::drawElementsInstanced(const gl::Context *context,
const void *indices,
GLsizei instanceCount)
{
// NOTE(hqle): ES 3.0
UNIMPLEMENTED();
return angle::Result::Stop;
if (instanceCount == 0)
{
return angle::Result::Continue;
}
return drawElementsImpl(context, mode, count, type, indices, instanceCount);
}
angle::Result ContextMtl::drawElementsInstancedBaseVertexBaseInstance(const gl::Context *context,
gl::PrimitiveMode mode,
GLsizei count,
......@@ -1242,12 +1307,15 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
gl::PrimitiveMode mode,
GLint firstVertex,
GLsizei vertexOrIndexCount,
GLsizei instanceCount,
GLsizei instances,
gl::DrawElementsType indexTypeOrNone,
const void *indices)
{
ASSERT(mProgram);
// instances=0 means no instanced draw.
GLsizei instanceCount = instances ? instances : 1;
mtl::BufferRef lineLoopLastSegmentIndexBuffer;
if (mode == gl::PrimitiveMode::LineLoop)
{
......@@ -1349,8 +1417,16 @@ angle::Result ContextMtl::setupDraw(const gl::Context *context,
if (mode == gl::PrimitiveMode::LineLoop)
{
// Draw last segment of line loop here
mRenderEncoder.drawIndexed(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32,
lineLoopLastSegmentIndexBuffer, 0);
if (instances == 0)
{
mRenderEncoder.drawIndexed(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32,
lineLoopLastSegmentIndexBuffer, 0);
}
else
{
mRenderEncoder.drawIndexedInstanced(MTLPrimitiveTypeLine, 2, MTLIndexTypeUInt32,
lineLoopLastSegmentIndexBuffer, 0, instanceCount);
}
}
return angle::Result::Continue;
......@@ -1364,8 +1440,6 @@ angle::Result ContextMtl::genLineLoopLastSegment(const gl::Context *context,
const void *indices,
mtl::BufferRef *lastSegmentIndexBufferOut)
{
ASSERT(instanceCount == 1);
mLineLoopIndexBuffer.releaseInFlightBuffers(this);
mtl::BufferRef newBuffer;
......
......@@ -572,9 +572,8 @@ void DisplayMtl::initializeExtensions() const
mNativeExtensions.semaphore = false;
mNativeExtensions.semaphoreFd = false;
// TODO: Enable this always and emulate instanced draws if any divisor exceeds the maximum
// supported. http://anglebug.com/2672
mNativeExtensions.instancedArraysANGLE = false;
mNativeExtensions.instancedArraysANGLE = mFeatures.hasBaseVertexInstancedDraw.enabled;
mNativeExtensions.instancedArraysEXT = mNativeExtensions.instancedArraysANGLE;
mNativeExtensions.robustBufferAccessBehavior = false;
......
......@@ -264,6 +264,7 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
mVertexArrayDirty = false;
const std::vector<gl::VertexAttribute> &attribs = mState.getVertexAttributes();
const std::vector<gl::VertexBinding> &bindings = mState.getVertexBindings();
mtl::VertexDesc &desc = *vertexDescOut;
......@@ -278,7 +279,8 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
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].format = mCurrentArrayBufferFormats[v];
......@@ -296,9 +298,17 @@ angle::Result VertexArrayMtl::setupDraw(const gl::Context *glContext,
desc.attributes[v].bufferIndex = bufferIdx;
ASSERT(bufferIdx < mtl::kMaxVertexAttribs);
desc.layouts[bufferIdx].stepFunction = MTLVertexStepFunctionPerVertex;
desc.layouts[bufferIdx].stepRate = 1;
desc.layouts[bufferIdx].stride = mCurrentArrayBufferStrides[v];
if (binding.getDivisor() == 0)
{
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,
bufferIdx);
......@@ -351,32 +361,35 @@ angle::Result VertexArrayMtl::updateClientAttribs(const gl::Context *context,
const uint8_t *src = static_cast<const uint8_t *>(attrib.pointer);
ASSERT(src);
if (binding.getDivisor() > 0)
GLint startElement;
size_t elementCount;
if (binding.getDivisor() == 0)
{
ANGLE_UNUSED_VARIABLE(instanceCount);
// NOTE(hqle): support ES 3.0.
// instanced attrib
UNREACHABLE();
// Per vertex attribute
startElement = startVertex;
elementCount = vertexCount;
}
else
{
// Allocate space for startVertex + vertexCount so indexing will work. If we don't
// start at zero all the indices will be off.
// Only vertexCount vertices will be used by the upcoming draw so that is all we copy.
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;
// Per instance attribute
startElement = 0;
elementCount = UnsignedCeilDivide(instanceCount, binding.getDivisor());
}
// 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;
......
......@@ -218,17 +218,28 @@ class RenderCommandEncoder final : public CommandEncoder
RenderCommandEncoder &draw(MTLPrimitiveType primitiveType,
uint32_t vertexStart,
uint32_t vertexCount);
RenderCommandEncoder &drawInstanced(MTLPrimitiveType primitiveType,
uint32_t vertexStart,
uint32_t vertexCount,
uint32_t instances);
RenderCommandEncoder &drawIndexed(MTLPrimitiveType primitiveType,
uint32_t indexCount,
MTLIndexType indexType,
const BufferRef &indexBuffer,
size_t bufferOffset);
RenderCommandEncoder &drawIndexedBaseVertex(MTLPrimitiveType primitiveType,
uint32_t indexCount,
MTLIndexType indexType,
const BufferRef &indexBuffer,
size_t bufferOffset,
uint32_t baseVertex);
RenderCommandEncoder &drawIndexedInstanced(MTLPrimitiveType primitiveType,
uint32_t indexCount,
MTLIndexType indexType,
const BufferRef &indexBuffer,
size_t bufferOffset,
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);
// Set store action for every color attachment.
......
......@@ -673,6 +673,20 @@ RenderCommandEncoder &RenderCommandEncoder::draw(MTLPrimitiveType primitiveType,
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,
uint32_t indexCount,
MTLIndexType indexType,
......@@ -694,12 +708,37 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexed(MTLPrimitiveType primiti
return *this;
}
RenderCommandEncoder &RenderCommandEncoder::drawIndexedBaseVertex(MTLPrimitiveType primitiveType,
uint32_t indexCount,
MTLIndexType indexType,
const BufferRef &indexBuffer,
size_t bufferOffset,
uint32_t baseVertex)
RenderCommandEncoder &RenderCommandEncoder::drawIndexedInstanced(MTLPrimitiveType primitiveType,
uint32_t indexCount,
MTLIndexType indexType,
const BufferRef &indexBuffer,
size_t bufferOffset,
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)
{
......@@ -712,7 +751,7 @@ RenderCommandEncoder &RenderCommandEncoder::drawIndexedBaseVertex(MTLPrimitiveTy
indexType:indexType
indexBuffer:indexBuffer->get()
indexBufferOffset:bufferOffset
instanceCount:1
instanceCount:instances
baseVertex:baseVertex
baseInstance:0];
......
......@@ -407,6 +407,9 @@ angle::Result Buffer::reset(ContextMtl *context, size_t size, const uint8_t *dat
id<MTLDevice> metalDevice = context->getMetalDevice();
options = 0;
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
options |= MTLResourceStorageModeManaged;
#endif
if (data)
{
......@@ -439,7 +442,12 @@ uint8_t *Buffer::map(ContextMtl *context)
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
{
......
......@@ -14,7 +14,8 @@ namespace
enum Geometry
{
Quad,
Point
Point,
TriFan,
};
enum Storage
{
......@@ -145,8 +146,20 @@ class InstancingTest : public ANGLETest
glVertexAttribDivisorEXT(instanceAttrib, divisor);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0,
geometry == Point ? kPointVertices : kQuadVertices);
const void *vertices;
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);
if (vendor == Angle)
glVertexAttribDivisorANGLE(positionAttrib, 0);
......@@ -169,7 +182,7 @@ class InstancingTest : public ANGLETest
else
glDrawArraysInstancedEXT(GL_POINTS, offset, 4 /*vertices*/, numInstance);
}
else
else if (geometry == Quad)
{
if (draw == Indexed)
if (vendor == Angle)
......@@ -183,6 +196,39 @@ class InstancingTest : public ANGLETest
else
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();
checkDrawing(lastDrawn);
......@@ -228,6 +274,28 @@ class InstancingTest : public ANGLETest
-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 2-4 are the same.
static constexpr GLfloat kPointVertices[] = {
......@@ -243,6 +311,9 @@ class InstancingTest : public ANGLETest
// Same two triangles as described above.
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.
static constexpr GLushort kPointIndices[] = {1, 5, 3, 2};
};
......@@ -250,8 +321,10 @@ class InstancingTest : public ANGLETest
constexpr unsigned InstancingTest::kMaxDrawn;
constexpr float InstancingTest::kDrawSize;
constexpr GLfloat InstancingTest::kQuadVertices[];
constexpr GLfloat InstancingTest::kTriFanVertices[];
constexpr GLfloat InstancingTest::kPointVertices[];
constexpr GLushort InstancingTest::kQuadIndices[];
constexpr GLubyte InstancingTest::kTriFanIndices[];
constexpr GLushort InstancingTest::kPointIndices[];
#define TEST_INDEXED(attrib, geometry, storage, vendor) \
......@@ -278,6 +351,14 @@ constexpr GLushort InstancingTest::kPointIndices[];
//
// Tests with a non-zero 'offset' check that "first" parameter to glDrawArraysInstancedANGLE is only
// 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, Memory, Angle)
TEST_INDEXED(1, Quad, Buffer, Angle)
......@@ -295,7 +376,23 @@ TEST_INDEXED(0, Point, Memory, Ext)
TEST_INDEXED(1, Point, Buffer, 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, 4)
TEST_NONINDEXED(0, Quad, Memory, Angle, 0)
......@@ -384,6 +481,129 @@ TEST_DIVISOR(32, 8)
TEST_DIVISOR(34, 3)
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
{
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