Commit 553590a5 by Martin Radev Committed by Commit Bot

Modify attribute divisor for multiview instanced rendering

If the ANGLE_multiview extension is used in a program, the number of geometry instances is the number of instances passed to glDraw*Instanced times the number of views in the program. The attribute divisor has to be multiplied by the number of views so that the correct attributes are gathered in the input assembly stage. BUG=angleproject:2062 TEST=angle_end2end_tests Change-Id: I960d6313c02e3eb83f7a07e72b9bcac072c736f4 Reviewed-on: https://chromium-review.googlesource.com/593953 Commit-Queue: Martin Radev <mradev@nvidia.com> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 0f7714ec
...@@ -110,16 +110,13 @@ GLintptr ComputeVertexAttributeOffset(const VertexAttribute &attrib, const Verte ...@@ -110,16 +110,13 @@ GLintptr ComputeVertexAttributeOffset(const VertexAttribute &attrib, const Verte
return attrib.relativeOffset + binding.getOffset(); return attrib.relativeOffset + binding.getOffset();
} }
size_t ComputeVertexBindingElementCount(const VertexBinding &binding, size_t ComputeVertexBindingElementCount(GLuint divisor, size_t drawCount, size_t instanceCount)
size_t drawCount,
size_t instanceCount)
{ {
// For instanced rendering, we draw "instanceDrawCount" sets of "vertexDrawCount" vertices. // For instanced rendering, we draw "instanceDrawCount" sets of "vertexDrawCount" vertices.
// //
// A vertex attribute with a positive divisor loads one instanced vertex for every set of // A vertex attribute with a positive divisor loads one instanced vertex for every set of
// non-instanced vertices, and the instanced vertex index advances once every "mDivisor" // non-instanced vertices, and the instanced vertex index advances once every "mDivisor"
// instances. // instances.
GLuint divisor = binding.getDivisor();
if (instanceCount > 0 && divisor > 0) if (instanceCount > 0 && divisor > 0)
{ {
// When instanceDrawCount is not a multiple attrib.divisor, the division must round up. // When instanceDrawCount is not a multiple attrib.divisor, the division must round up.
......
...@@ -76,9 +76,7 @@ size_t ComputeVertexAttributeStride(const VertexAttribute &attrib, const VertexB ...@@ -76,9 +76,7 @@ size_t ComputeVertexAttributeStride(const VertexAttribute &attrib, const VertexB
// Warning: you should ensure binding really matches attrib.bindingIndex before using this function. // Warning: you should ensure binding really matches attrib.bindingIndex before using this function.
GLintptr ComputeVertexAttributeOffset(const VertexAttribute &attrib, const VertexBinding &binding); GLintptr ComputeVertexAttributeOffset(const VertexAttribute &attrib, const VertexBinding &binding);
size_t ComputeVertexBindingElementCount(const VertexBinding &binding, size_t ComputeVertexBindingElementCount(GLuint divisor, size_t drawCount, size_t instanceCount);
size_t drawCount,
size_t instanceCount);
GLenum GetVertexAttributeBaseType(const VertexAttribute &attrib); GLenum GetVertexAttributeBaseType(const VertexAttribute &attrib);
......
...@@ -452,7 +452,8 @@ gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &tr ...@@ -452,7 +452,8 @@ gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &tr
BufferD3D *bufferD3D = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr; BufferD3D *bufferD3D = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr;
ASSERT(!bufferD3D || bufferD3D->getStaticVertexBuffer(attrib, binding) == nullptr); ASSERT(!bufferD3D || bufferD3D->getStaticVertexBuffer(attrib, binding) == nullptr);
size_t totalCount = ComputeVertexBindingElementCount(binding, count, instances); size_t totalCount = gl::ComputeVertexBindingElementCount(
binding.getDivisor(), static_cast<size_t>(count), static_cast<size_t>(instances));
ASSERT(!bufferD3D || ASSERT(!bufferD3D ||
ElementsInBuffer(attrib, binding, static_cast<unsigned int>(bufferD3D->getSize())) >= ElementsInBuffer(attrib, binding, static_cast<unsigned int>(bufferD3D->getSize())) >=
static_cast<int>(totalCount)); static_cast<int>(totalCount));
...@@ -499,7 +500,8 @@ gl::Error VertexDataManager::storeDynamicAttrib(TranslatedAttribute *translated, ...@@ -499,7 +500,8 @@ gl::Error VertexDataManager::storeDynamicAttrib(TranslatedAttribute *translated,
translated->storage = nullptr; translated->storage = nullptr;
ANGLE_TRY_RESULT(mFactory->getVertexSpaceRequired(attrib, binding, 1, 0), translated->stride); ANGLE_TRY_RESULT(mFactory->getVertexSpaceRequired(attrib, binding, 1, 0), translated->stride);
size_t totalCount = ComputeVertexBindingElementCount(binding, count, instances); size_t totalCount = gl::ComputeVertexBindingElementCount(
binding.getDivisor(), static_cast<size_t>(count), static_cast<size_t>(instances));
ANGLE_TRY(mStreamingBuffer->storeDynamicAttribute( ANGLE_TRY(mStreamingBuffer->storeDynamicAttribute(
attrib, binding, translated->currentValueType, firstVertexIndex, attrib, binding, translated->currentValueType, firstVertexIndex,
......
...@@ -389,6 +389,8 @@ void StateManagerGL::bindVertexArray(GLuint vao, GLuint elementArrayBuffer) ...@@ -389,6 +389,8 @@ void StateManagerGL::bindVertexArray(GLuint vao, GLuint elementArrayBuffer)
mVAO = vao; mVAO = vao;
mBuffers[GL_ELEMENT_ARRAY_BUFFER] = elementArrayBuffer; mBuffers[GL_ELEMENT_ARRAY_BUFFER] = elementArrayBuffer;
mFunctions->bindVertexArray(vao); mFunctions->bindVertexArray(vao);
mLocalDirtyBits.set(gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING);
} }
} }
...@@ -1892,12 +1894,16 @@ void StateManagerGL::syncState(const gl::Context *context, const gl::State::Dirt ...@@ -1892,12 +1894,16 @@ void StateManagerGL::syncState(const gl::Context *context, const gl::State::Dirt
break; break;
case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING: case gl::State::DIRTY_BIT_VERTEX_ARRAY_BINDING:
// TODO(jmadill): implement this // TODO(jmadill): implement this
propagateNumViewsToVAO(state.getProgram(),
GetImplAs<VertexArrayGL>(state.getVertexArray()));
break; break;
case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING: case gl::State::DIRTY_BIT_DRAW_INDIRECT_BUFFER_BINDING:
// TODO: implement this // TODO: implement this
break; break;
case gl::State::DIRTY_BIT_PROGRAM_BINDING: case gl::State::DIRTY_BIT_PROGRAM_BINDING:
// TODO(jmadill): implement this // TODO(jmadill): implement this
propagateNumViewsToVAO(state.getProgram(),
GetImplAs<VertexArrayGL>(state.getVertexArray()));
break; break;
case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE: case gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE:
// TODO(jmadill): implement this // TODO(jmadill): implement this
...@@ -2142,4 +2148,17 @@ void StateManagerGL::applyViewportOffsetsAndSetViewports(const gl::Rectangle &vi ...@@ -2142,4 +2148,17 @@ void StateManagerGL::applyViewportOffsetsAndSetViewports(const gl::Rectangle &vi
mMultiviewDirtyBits.set(MULTIVIEW_DIRTY_BIT_VIEWPORT_OFFSETS); mMultiviewDirtyBits.set(MULTIVIEW_DIRTY_BIT_VIEWPORT_OFFSETS);
} }
} }
void StateManagerGL::propagateNumViewsToVAO(const gl::Program *program, VertexArrayGL *vao)
{
if (mIsMultiviewEnabled && vao != nullptr)
{
int programNumViews = 1;
if (program && program->usesMultiview())
{
programNumViews = program->getNumViews();
}
vao->applyNumViewsToDivisor(programNumViews);
}
}
} }
...@@ -30,6 +30,7 @@ namespace rx ...@@ -30,6 +30,7 @@ namespace rx
class FramebufferGL; class FramebufferGL;
class FunctionsGL; class FunctionsGL;
class TransformFeedbackGL; class TransformFeedbackGL;
class VertexArrayGL;
class QueryGL; class QueryGL;
class StateManagerGL final : angle::NonCopyable class StateManagerGL final : angle::NonCopyable
...@@ -192,6 +193,7 @@ class StateManagerGL final : angle::NonCopyable ...@@ -192,6 +193,7 @@ class StateManagerGL final : angle::NonCopyable
const gl::Framebuffer &drawFramebuffer); const gl::Framebuffer &drawFramebuffer);
void applyViewportOffsetsAndSetViewports(const gl::Rectangle &viewport, void applyViewportOffsetsAndSetViewports(const gl::Rectangle &viewport,
const gl::Framebuffer &drawFramebuffer); const gl::Framebuffer &drawFramebuffer);
void propagateNumViewsToVAO(const gl::Program *program, VertexArrayGL *vao);
enum MultiviewDirtyBitType enum MultiviewDirtyBitType
{ {
......
...@@ -48,6 +48,12 @@ bool IsVertexAttribPointerSupported(size_t attribIndex, const VertexAttribute &a ...@@ -48,6 +48,12 @@ bool IsVertexAttribPointerSupported(size_t attribIndex, const VertexAttribute &a
{ {
return (attribIndex == attrib.bindingIndex && attrib.relativeOffset == 0); return (attribIndex == attrib.bindingIndex && attrib.relativeOffset == 0);
} }
GLuint GetAdjustedDivisor(GLuint numViews, GLuint divisor)
{
return numViews * divisor;
}
} // anonymous namespace } // anonymous namespace
VertexArrayGL::VertexArrayGL(const VertexArrayState &state, VertexArrayGL::VertexArrayGL(const VertexArrayState &state,
...@@ -57,6 +63,7 @@ VertexArrayGL::VertexArrayGL(const VertexArrayState &state, ...@@ -57,6 +63,7 @@ VertexArrayGL::VertexArrayGL(const VertexArrayState &state,
mFunctions(functions), mFunctions(functions),
mStateManager(stateManager), mStateManager(stateManager),
mVertexArrayID(0), mVertexArrayID(0),
mAppliedNumViews(1),
mAppliedElementArrayBuffer(), mAppliedElementArrayBuffer(),
mAppliedBindings(state.getMaxBindings()), mAppliedBindings(state.getMaxBindings()),
mStreamingElementArrayBufferSize(0), mStreamingElementArrayBufferSize(0),
...@@ -80,6 +87,7 @@ void VertexArrayGL::destroy(const gl::Context *context) ...@@ -80,6 +87,7 @@ void VertexArrayGL::destroy(const gl::Context *context)
{ {
mStateManager->deleteVertexArray(mVertexArrayID); mStateManager->deleteVertexArray(mVertexArrayID);
mVertexArrayID = 0; mVertexArrayID = 0;
mAppliedNumViews = 1;
mStateManager->deleteBuffer(mStreamingElementArrayBuffer); mStateManager->deleteBuffer(mStreamingElementArrayBuffer);
mStreamingElementArrayBufferSize = 0; mStreamingElementArrayBufferSize = 0;
...@@ -283,8 +291,10 @@ void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &act ...@@ -283,8 +291,10 @@ void VertexArrayGL::computeStreamingAttributeSizes(const gl::AttributesMask &act
// and how much slack space at the beginning of the buffer will be required by determining // and how much slack space at the beginning of the buffer will be required by determining
// the attribute with the largest data size. // the attribute with the largest data size.
size_t typeSize = ComputeVertexAttributeTypeSize(attrib); size_t typeSize = ComputeVertexAttributeTypeSize(attrib);
*outStreamingDataSize += typeSize * ComputeVertexBindingElementCount( GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
binding, indexRange.vertexCount(), instanceCount); *outStreamingDataSize +=
typeSize * ComputeVertexBindingElementCount(adjustedDivisor, indexRange.vertexCount(),
instanceCount);
*outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize); *outMaxAttributeDataSize = std::max(*outMaxAttributeDataSize, typeSize);
} }
} }
...@@ -347,15 +357,16 @@ gl::Error VertexArrayGL::streamAttributes(const gl::AttributesMask &activeAttrib ...@@ -347,15 +357,16 @@ gl::Error VertexArrayGL::streamAttributes(const gl::AttributesMask &activeAttrib
const auto &binding = bindings[attrib.bindingIndex]; const auto &binding = bindings[attrib.bindingIndex];
ASSERT(AttributeNeedsStreaming(attrib, binding)); ASSERT(AttributeNeedsStreaming(attrib, binding));
const size_t streamedVertexCount = GLuint adjustedDivisor = GetAdjustedDivisor(mAppliedNumViews, binding.getDivisor());
ComputeVertexBindingElementCount(binding, indexRange.vertexCount(), instanceCount); const size_t streamedVertexCount = ComputeVertexBindingElementCount(
adjustedDivisor, indexRange.vertexCount(), instanceCount);
const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding); const size_t sourceStride = ComputeVertexAttributeStride(attrib, binding);
const size_t destStride = ComputeVertexAttributeTypeSize(attrib); const size_t destStride = ComputeVertexAttributeTypeSize(attrib);
// Vertices do not apply the 'start' offset when the divisor is non-zero even when doing // Vertices do not apply the 'start' offset when the divisor is non-zero even when doing
// a non-instanced draw call // a non-instanced draw call
const size_t firstIndex = binding.getDivisor() == 0 ? indexRange.start : 0; const size_t firstIndex = adjustedDivisor == 0 ? indexRange.start : 0;
// Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state. // Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state.
// https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt // https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding.txt
...@@ -610,24 +621,25 @@ void VertexArrayGL::updateBindingBuffer(const gl::Context *context, size_t bindi ...@@ -610,24 +621,25 @@ void VertexArrayGL::updateBindingBuffer(const gl::Context *context, size_t bindi
void VertexArrayGL::updateBindingDivisor(size_t bindingIndex) void VertexArrayGL::updateBindingDivisor(size_t bindingIndex)
{ {
GLuint newDivisor = mData.getVertexBinding(bindingIndex).getDivisor(); GLuint adjustedDivisor =
if (mAppliedBindings[bindingIndex].getDivisor() == newDivisor) GetAdjustedDivisor(mAppliedNumViews, mData.getVertexBinding(bindingIndex).getDivisor());
if (mAppliedBindings[bindingIndex].getDivisor() == adjustedDivisor)
{ {
return; return;
} }
if (supportVertexAttribBinding()) if (supportVertexAttribBinding())
{ {
mFunctions->vertexBindingDivisor(static_cast<GLuint>(bindingIndex), newDivisor); mFunctions->vertexBindingDivisor(static_cast<GLuint>(bindingIndex), adjustedDivisor);
} }
else else
{ {
// We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib // We can only use VertexAttribDivisor on platforms that don't support Vertex Attrib
// Binding. // Binding.
mFunctions->vertexAttribDivisor(static_cast<GLuint>(bindingIndex), newDivisor); mFunctions->vertexAttribDivisor(static_cast<GLuint>(bindingIndex), adjustedDivisor);
} }
mAppliedBindings[bindingIndex].setDivisor(newDivisor); mAppliedBindings[bindingIndex].setDivisor(adjustedDivisor);
} }
void VertexArrayGL::syncState(const gl::Context *context, const VertexArray::DirtyBits &dirtyBits) void VertexArrayGL::syncState(const gl::Context *context, const VertexArray::DirtyBits &dirtyBits)
...@@ -683,4 +695,17 @@ void VertexArrayGL::syncState(const gl::Context *context, const VertexArray::Dir ...@@ -683,4 +695,17 @@ void VertexArrayGL::syncState(const gl::Context *context, const VertexArray::Dir
} }
} }
void VertexArrayGL::applyNumViewsToDivisor(int numViews)
{
if (numViews != mAppliedNumViews)
{
mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
mAppliedNumViews = numViews;
for (size_t index = 0u; index < mAppliedBindings.size(); ++index)
{
updateBindingDivisor(index);
}
}
}
} // rx } // rx
...@@ -45,6 +45,7 @@ class VertexArrayGL : public VertexArrayImpl ...@@ -45,6 +45,7 @@ class VertexArrayGL : public VertexArrayImpl
void syncState(const gl::Context *context, void syncState(const gl::Context *context,
const gl::VertexArray::DirtyBits &dirtyBits) override; const gl::VertexArray::DirtyBits &dirtyBits) override;
void applyNumViewsToDivisor(int numViews);
private: private:
gl::Error syncDrawState(const gl::Context *context, gl::Error syncDrawState(const gl::Context *context,
...@@ -100,6 +101,7 @@ class VertexArrayGL : public VertexArrayImpl ...@@ -100,6 +101,7 @@ class VertexArrayGL : public VertexArrayImpl
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
GLuint mVertexArrayID; GLuint mVertexArrayID;
int mAppliedNumViews;
mutable gl::BindingPointer<gl::Buffer> mAppliedElementArrayBuffer; mutable gl::BindingPointer<gl::Buffer> mAppliedElementArrayBuffer;
......
...@@ -633,6 +633,231 @@ TEST_P(MultiviewSideBySideRenderTest, DrawArraysInstanced) ...@@ -633,6 +633,231 @@ TEST_P(MultiviewSideBySideRenderTest, DrawArraysInstanced)
} }
} }
// The test verifies that the attribute divisor is correctly adjusted when drawing with a multi-view
// program. The test draws 4 instances of a quad each of which covers a single pixel. The x and y
// offset of each quad are passed as separate attributes which are indexed based on the
// corresponding attribute divisors. A divisor of 1 is used for the y offset to have all quads
// drawn vertically next to each other. A divisor of 3 is used for the x offset to have the last
// quad offsetted by one pixel to the right. Note that the number of views is divisible by 1, but
// not by 3.
TEST_P(MultiviewSideBySideRenderTest, AttribDivisor)
{
if (!requestMultiviewExtension())
{
return;
}
const std::string &vsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"layout(num_views = 2) in;\n"
"in vec3 vPosition;\n"
"in float offsetX;\n"
"in float offsetY;\n"
"void main()\n"
"{\n"
" vec4 p = vec4(vPosition, 1.);\n"
" p.xy = p.xy * 0.25 - 0.75 + vec2(offsetX, offsetY);\n"
" gl_Position.x = (gl_ViewID_OVR == 0u ? p.x : p.x + 1.0);\n"
" gl_Position.yzw = p.yzw;\n"
"}\n";
const std::string &fsSource =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(1,0,0,0);\n"
"}\n";
createFBO(8, 4, 2);
ANGLE_GL_PROGRAM(program, vsSource, fsSource);
glUseProgram(program);
GLBuffer xOffsetVBO;
glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
const GLfloat xOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.0f};
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, xOffsetData, GL_STATIC_DRAW);
GLint xOffsetLoc = glGetAttribLocation(program, "offsetX");
glVertexAttribPointer(xOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
glVertexAttribDivisor(xOffsetLoc, 3);
glEnableVertexAttribArray(xOffsetLoc);
GLBuffer yOffsetVBO;
glBindBuffer(GL_ARRAY_BUFFER, yOffsetVBO);
const GLfloat yOffsetData[4] = {0.0f, 0.5f, 1.0f, 1.5f};
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4, yOffsetData, GL_STATIC_DRAW);
GLint yOffsetLoc = glGetAttribLocation(program, "offsetY");
glVertexAttribDivisor(yOffsetLoc, 1);
glVertexAttribPointer(yOffsetLoc, 1, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(yOffsetLoc);
drawQuad(program, "vPosition", 0.0f, 1.0f, true, true, 4);
ASSERT_GL_NO_ERROR();
const GLubyte expectedRedChannel[4][8] = {{255, 0, 0, 0, 0, 0, 255, 0},
{255, 0, 0, 0, 0, 0, 255, 0},
{255, 0, 0, 0, 0, 0, 255, 0},
{0, 255, 0, 0, 0, 0, 0, 255}};
for (int row = 0; row < 4; ++row)
{
for (int col = 0; col < 8; ++col)
{
EXPECT_PIXEL_EQ(col, row, expectedRedChannel[row][col], 0, 0, 0);
}
}
}
// Test that different sequences of vertexAttribDivisor, useProgram and bindVertexArray in a
// multi-view context propagate the correct divisor to the driver.
TEST_P(MultiviewSideBySideRenderTest, DivisorOrderOfOperation)
{
if (!requestMultiviewExtension())
{
return;
}
createFBO(2, 1, 2);
// Create multiview program.
const std::string &vs =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"layout(num_views = 2) in;\n"
"layout(location = 0) in vec2 vPosition;\n"
"layout(location = 1) in float offsetX;\n"
"void main()\n"
"{\n"
" vec4 p = vec4(vPosition, 0.0, 1.0);\n"
" p.x += offsetX;\n"
" gl_Position = p;\n"
"}\n";
const std::string &fs =
"#version 300 es\n"
"#extension GL_OVR_multiview2 : require\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(1,0,0,0);\n"
"}\n";
ANGLE_GL_PROGRAM(program, vs, fs);
const std::string &dummyVS =
"#version 300 es\n"
"layout(location = 0) in vec2 vPosition;\n"
"layout(location = 1) in float offsetX;\n"
"void main()\n"
"{\n"
" gl_Position = vec4(vPosition, 0.0, 1.0);\n"
"}\n";
const std::string &dummyFS =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 col;\n"
"void main()\n"
"{\n"
" col = vec4(0,0,0,0);\n"
"}\n";
ANGLE_GL_PROGRAM(dummyProgram, dummyVS, dummyFS);
GLBuffer xOffsetVBO;
glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
const GLfloat xOffsetData[12] = {0.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f,
4.0f, 4.0f, 4.0f, 4.0f, 4.0f, 4.0f};
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 12, xOffsetData, GL_STATIC_DRAW);
GLBuffer vertexVBO;
glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
Vector2 kQuadVertices[6] = {Vector2(-1.f, -1.f), Vector2(1.f, -1.f), Vector2(1.f, 1.f),
Vector2(-1.f, -1.f), Vector2(1.f, 1.f), Vector2(-1.f, 1.f)};
glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
GLVertexArray vao[2];
for (size_t i = 0u; i < 2u; ++i)
{
glBindVertexArray(vao[i]);
glBindBuffer(GL_ARRAY_BUFFER, vertexVBO);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, xOffsetVBO);
glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(1);
}
ASSERT_GL_NO_ERROR();
glViewport(0, 0, 1, 1);
glScissor(0, 0, 1, 1);
glClearColor(0, 0, 0, 0);
// Clear the buffers, propagate divisor to the driver, bind the vao and keep it active.
// It is necessary to call draw, so that the divisor is propagated and to guarantee that dirty
// bits are cleared.
glUseProgram(dummyProgram);
glBindVertexArray(vao[0]);
glVertexAttribDivisor(1, 0);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
glUseProgram(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Check that vertexAttribDivisor uses the number of views to update the divisor.
glUseProgram(program);
glVertexAttribDivisor(1, 1);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
// Clear the buffers and propagate divisor to the driver.
// We keep the vao active and propagate the divisor to guarantee that there are no unresolved
// dirty bits when useProgram is called.
glUseProgram(dummyProgram);
glVertexAttribDivisor(1, 1);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
glUseProgram(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Check that useProgram uses the number of views to update the divisor.
glUseProgram(program);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
// We go through similar steps as before.
glUseProgram(dummyProgram);
glVertexAttribDivisor(1, 1);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
glUseProgram(0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
// Check that bindVertexArray uses the number of views to update the divisor.
{
// Call useProgram with vao[1] being active to guarantee that useProgram will adjust the
// divisor for vao[1] only.
glBindVertexArray(vao[1]);
glUseProgram(program);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(0);
ASSERT_GL_NO_ERROR();
}
// Bind vao[0] after useProgram is called to ensure that bindVertexArray is the call which
// adjusts the divisor.
glBindVertexArray(vao[0]);
glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 1);
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 0);
EXPECT_PIXEL_EQ(1, 0, 255, 0, 0, 0);
}
ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL()); ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderDualViewTest, ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderDualViewTest, ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(MultiviewSideBySideRenderTest, ES3_OPENGL());
\ No newline at end of file
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