Commit d69a5f12 by Jamie Madill Committed by Commit Bot

Cache VertexArray::hasMappedBuffer.

This can be updated in several places. Also adds a test which covers some of the paths. Bug: angleproject:2746 Change-Id: Id119e527fd0064998d7ad5011a9d8376e7b9dab0 Reviewed-on: https://chromium-review.googlesource.com/1153569 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 81f891d0
...@@ -145,6 +145,9 @@ Error Buffer::map(const Context *context, GLenum access) ...@@ -145,6 +145,9 @@ Error Buffer::map(const Context *context, GLenum access)
mState.mAccessFlags = GL_MAP_WRITE_BIT; mState.mAccessFlags = GL_MAP_WRITE_BIT;
mIndexRangeCache.clear(); mIndexRangeCache.clear();
// Notify when state changes.
mImpl->onStateChange(context, angle::SubjectMessage::RESOURCE_MAPPED);
return NoError(); return NoError();
} }
...@@ -175,6 +178,9 @@ Error Buffer::mapRange(const Context *context, ...@@ -175,6 +178,9 @@ Error Buffer::mapRange(const Context *context,
mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset), static_cast<unsigned int>(length)); mIndexRangeCache.invalidateRange(static_cast<unsigned int>(offset), static_cast<unsigned int>(length));
} }
// Notify when state changes.
mImpl->onStateChange(context, angle::SubjectMessage::RESOURCE_MAPPED);
return NoError(); return NoError();
} }
...@@ -193,7 +199,7 @@ Error Buffer::unmap(const Context *context, GLboolean *result) ...@@ -193,7 +199,7 @@ Error Buffer::unmap(const Context *context, GLboolean *result)
mState.mAccessFlags = 0; mState.mAccessFlags = 0;
// Notify when data changes. // Notify when data changes.
mImpl->onStateChange(context, angle::SubjectMessage::CONTENTS_CHANGED); mImpl->onStateChange(context, angle::SubjectMessage::RESOURCE_UNMAPPED);
return NoError(); return NoError();
} }
......
...@@ -32,6 +32,8 @@ enum class SubjectMessage ...@@ -32,6 +32,8 @@ enum class SubjectMessage
STORAGE_CHANGED, STORAGE_CHANGED,
BINDING_CHANGED, BINDING_CHANGED,
DEPENDENT_DIRTY_BITS, DEPENDENT_DIRTY_BITS,
RESOURCE_MAPPED,
RESOURCE_UNMAPPED,
}; };
// The observing class inherits from this interface class. // The observing class inherits from this interface class.
......
...@@ -15,6 +15,14 @@ ...@@ -15,6 +15,14 @@
namespace gl namespace gl
{ {
namespace
{
bool IsElementArrayBufferSubjectIndex(angle::SubjectIndex subjectIndex)
{
return (subjectIndex == MAX_VERTEX_ATTRIBS);
}
} // anonymous namespce
// VertexArrayState implementation. // VertexArrayState implementation.
VertexArrayState::VertexArrayState(size_t maxAttribs, size_t maxAttribBindings) VertexArrayState::VertexArrayState(size_t maxAttribs, size_t maxAttribBindings)
: mLabel(), mVertexBindings() : mLabel(), mVertexBindings()
...@@ -57,15 +65,22 @@ void VertexArrayState::setAttribBinding(size_t attribIndex, GLuint newBindingInd ...@@ -57,15 +65,22 @@ void VertexArrayState::setAttribBinding(size_t attribIndex, GLuint newBindingInd
const GLuint oldBindingIndex = attrib.bindingIndex; const GLuint oldBindingIndex = attrib.bindingIndex;
ASSERT(oldBindingIndex != newBindingIndex); ASSERT(oldBindingIndex != newBindingIndex);
ASSERT(mVertexBindings[oldBindingIndex].getBoundAttributesMask().test(attribIndex) && VertexBinding &oldBinding = mVertexBindings[oldBindingIndex];
!mVertexBindings[newBindingIndex].getBoundAttributesMask().test(attribIndex)); VertexBinding &newBinding = mVertexBindings[newBindingIndex];
ASSERT(oldBinding.getBoundAttributesMask().test(attribIndex) &&
!newBinding.getBoundAttributesMask().test(attribIndex));
mVertexBindings[oldBindingIndex].resetBoundAttribute(attribIndex); oldBinding.resetBoundAttribute(attribIndex);
mVertexBindings[newBindingIndex].setBoundAttribute(attribIndex); newBinding.setBoundAttribute(attribIndex);
// Set the attribute using the new binding. // Set the attribute using the new binding.
attrib.bindingIndex = newBindingIndex; attrib.bindingIndex = newBindingIndex;
attrib.updateCachedElementLimit(mVertexBindings[newBindingIndex]); attrib.updateCachedElementLimit(newBinding);
bool isMapped = newBinding.getBuffer().get() && newBinding.getBuffer()->isMapped();
mCachedMappedArrayBuffers.set(attribIndex, isMapped);
mCachedEnabledMappedArrayBuffers.set(attribIndex, isMapped && attrib.enabled);
} }
// VertexArray implementation. // VertexArray implementation.
...@@ -76,7 +91,7 @@ VertexArray::VertexArray(rx::GLImplFactory *factory, ...@@ -76,7 +91,7 @@ VertexArray::VertexArray(rx::GLImplFactory *factory,
: mId(id), : mId(id),
mState(maxAttribs, maxAttribBindings), mState(maxAttribs, maxAttribBindings),
mVertexArray(factory->createVertexArray(mState)), mVertexArray(factory->createVertexArray(mState)),
mElementArrayBufferObserverBinding(this, maxAttribBindings) mElementArrayBufferObserverBinding(this, MAX_VERTEX_ATTRIBS)
{ {
for (size_t attribIndex = 0; attribIndex < maxAttribBindings; ++attribIndex) for (size_t attribIndex = 0; attribIndex < maxAttribBindings; ++attribIndex)
{ {
...@@ -190,6 +205,7 @@ void VertexArray::bindVertexBufferImpl(const Context *context, ...@@ -190,6 +205,7 @@ void VertexArray::bindVertexBufferImpl(const Context *context,
updateObserverBinding(bindingIndex); updateObserverBinding(bindingIndex);
updateCachedBufferBindingSize(binding); updateCachedBufferBindingSize(binding);
updateCachedTransformFeedbackBindingValidation(bindingIndex, boundBuffer); updateCachedTransformFeedbackBindingValidation(bindingIndex, boundBuffer);
updateCachedMappedArrayBuffers(binding);
// Update client memory attribute pointers. Affects all bound attributes. // Update client memory attribute pointers. Affects all bound attributes.
if (boundBuffer) if (boundBuffer)
...@@ -303,6 +319,8 @@ void VertexArray::enableAttribute(size_t attribIndex, bool enabledState) ...@@ -303,6 +319,8 @@ void VertexArray::enableAttribute(size_t attribIndex, bool enabledState)
// Update state cache // Update state cache
mState.mEnabledAttributesMask.set(attribIndex, enabledState); mState.mEnabledAttributesMask.set(attribIndex, enabledState);
mState.mCachedEnabledMappedArrayBuffers =
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
} }
void VertexArray::setVertexAttribPointer(const Context *context, void VertexArray::setVertexAttribPointer(const Context *context,
...@@ -385,7 +403,7 @@ void VertexArray::onBindingChanged(const Context *context, bool bound) ...@@ -385,7 +403,7 @@ void VertexArray::onBindingChanged(const Context *context, bool bound)
VertexArray::DirtyBitType VertexArray::getDirtyBitFromIndex(bool contentsChanged, VertexArray::DirtyBitType VertexArray::getDirtyBitFromIndex(bool contentsChanged,
angle::SubjectIndex index) const angle::SubjectIndex index) const
{ {
if (index == mArrayBufferObserverBindings.size()) if (IsElementArrayBufferSubjectIndex(index))
{ {
return contentsChanged ? DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA return contentsChanged ? DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA
: DIRTY_BIT_ELEMENT_ARRAY_BUFFER; : DIRTY_BIT_ELEMENT_ARRAY_BUFFER;
...@@ -410,7 +428,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context, ...@@ -410,7 +428,7 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
break; break;
case angle::SubjectMessage::STORAGE_CHANGED: case angle::SubjectMessage::STORAGE_CHANGED:
if (index < mArrayBufferObserverBindings.size()) if (!IsElementArrayBufferSubjectIndex(index))
{ {
updateCachedBufferBindingSize(&mState.mVertexBindings[index]); updateCachedBufferBindingSize(&mState.mVertexBindings[index]);
} }
...@@ -418,13 +436,29 @@ void VertexArray::onSubjectStateChange(const gl::Context *context, ...@@ -418,13 +436,29 @@ void VertexArray::onSubjectStateChange(const gl::Context *context,
break; break;
case angle::SubjectMessage::BINDING_CHANGED: case angle::SubjectMessage::BINDING_CHANGED:
if (index < mArrayBufferObserverBindings.size()) if (!IsElementArrayBufferSubjectIndex(index))
{ {
const Buffer *buffer = mState.mVertexBindings[index].getBuffer().get(); const Buffer *buffer = mState.mVertexBindings[index].getBuffer().get();
updateCachedTransformFeedbackBindingValidation(index, buffer); updateCachedTransformFeedbackBindingValidation(index, buffer);
} }
break; break;
case angle::SubjectMessage::RESOURCE_MAPPED:
if (!IsElementArrayBufferSubjectIndex(index))
{
updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]);
}
break;
case angle::SubjectMessage::RESOURCE_UNMAPPED:
setDependentDirtyBit(context, true, index);
if (!IsElementArrayBufferSubjectIndex(index))
{
updateCachedMappedArrayBuffers(&mState.mVertexBindings[index]);
}
break;
default: default:
UNREACHABLE(); UNREACHABLE();
break; break;
...@@ -486,21 +520,19 @@ bool VertexArray::hasTransformFeedbackBindingConflict(const gl::Context *context ...@@ -486,21 +520,19 @@ bool VertexArray::hasTransformFeedbackBindingConflict(const gl::Context *context
return false; return false;
} }
bool VertexArray::hasMappedEnabledArrayBuffer() const void VertexArray::updateCachedMappedArrayBuffers(VertexBinding *binding)
{ {
// TODO(jmadill): Cache this. http://anglebug.com/2746 Buffer *buffer = binding->getBuffer().get();
for (size_t attribIndex : mState.mEnabledAttributesMask) if (buffer && buffer->isMapped())
{ {
const VertexAttribute &vertexAttrib = mState.mVertexAttributes[attribIndex]; mState.mCachedMappedArrayBuffers |= binding->getBoundAttributesMask();
ASSERT(vertexAttrib.enabled); }
const VertexBinding &vertexBinding = mState.mVertexBindings[vertexAttrib.bindingIndex]; else
gl::Buffer *boundBuffer = vertexBinding.getBuffer().get(); {
if (boundBuffer && boundBuffer->isMapped()) mState.mCachedMappedArrayBuffers &= ~binding->getBoundAttributesMask();
{
return true;
}
} }
return false; mState.mCachedEnabledMappedArrayBuffers =
mState.mCachedMappedArrayBuffers & mState.mEnabledAttributesMask;
} }
} // namespace gl } // namespace gl
...@@ -86,6 +86,10 @@ class VertexArrayState final : angle::NonCopyable ...@@ -86,6 +86,10 @@ class VertexArrayState final : angle::NonCopyable
// attribs. // attribs.
gl::AttributesMask mClientMemoryAttribsMask; gl::AttributesMask mClientMemoryAttribsMask;
gl::AttributesMask mNullPointerClientMemoryAttribsMask; gl::AttributesMask mNullPointerClientMemoryAttribsMask;
// Used for validation cache. Indexed by attribute.
AttributesMask mCachedMappedArrayBuffers;
AttributesMask mCachedEnabledMappedArrayBuffers;
}; };
class VertexArray final : public angle::ObserverInterface, class VertexArray final : public angle::ObserverInterface,
...@@ -178,7 +182,10 @@ class VertexArray final : public angle::ObserverInterface, ...@@ -178,7 +182,10 @@ class VertexArray final : public angle::ObserverInterface,
return mState.hasEnabledNullPointerClientArray(); return mState.hasEnabledNullPointerClientArray();
} }
bool hasMappedEnabledArrayBuffer() const; bool hasMappedEnabledArrayBuffer() const
{
return mState.mCachedEnabledMappedArrayBuffers.any();
}
// Observer implementation // Observer implementation
void onSubjectStateChange(const gl::Context *context, void onSubjectStateChange(const gl::Context *context,
...@@ -265,6 +272,7 @@ class VertexArray final : public angle::ObserverInterface, ...@@ -265,6 +272,7 @@ class VertexArray final : public angle::ObserverInterface,
// These are used to optimize draw call validation. // These are used to optimize draw call validation.
void updateCachedBufferBindingSize(VertexBinding *binding); void updateCachedBufferBindingSize(VertexBinding *binding);
void updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer); void updateCachedTransformFeedbackBindingValidation(size_t bindingIndex, const Buffer *buffer);
void updateCachedMappedArrayBuffers(VertexBinding *binding);
GLuint mId; GLuint mId;
......
...@@ -2062,6 +2062,254 @@ TEST_P(SimpleStateChangeTest, ReleaseShaderInUseThatReadsFromUniforms) ...@@ -2062,6 +2062,254 @@ TEST_P(SimpleStateChangeTest, ReleaseShaderInUseThatReadsFromUniforms)
EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, 0, GLColor::red); EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, 0, GLColor::red);
} }
static constexpr char kColorVS[] = R"(attribute vec2 position;
attribute vec4 color;
varying vec4 vColor;
void main()
{
gl_Position = vec4(position, 0, 1);
vColor = color;
})";
static constexpr char kColorFS[] = R"(precision mediump float;
varying vec4 vColor;
void main()
{
gl_FragColor = vColor;
})";
class ValidationStateChangeTest : public ANGLETest
{
protected:
ValidationStateChangeTest()
{
setWindowWidth(64);
setWindowHeight(64);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
class ValidationStateChangeTestES31 : public ANGLETest
{
};
// Tests that mapping and unmapping an array buffer in various ways causes rendering to fail.
// This isn't guaranteed to produce an error by GL. But we assume ANGLE always errors.
TEST_P(ValidationStateChangeTest, MapBufferAndDraw)
{
// Initialize program and set up state.
ANGLE_GL_PROGRAM(program, kColorVS, kColorFS);
glUseProgram(program);
GLint positionLoc = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLoc);
GLint colorLoc = glGetAttribLocation(program, "color");
ASSERT_NE(-1, colorLoc);
const std::array<Vector3, 6> &quadVertices = GetQuadVertices();
const size_t posBufferSize = quadVertices.size() * sizeof(Vector3);
GLBuffer posBuffer;
glBindBuffer(GL_ARRAY_BUFFER, posBuffer);
glBufferData(GL_ARRAY_BUFFER, posBufferSize, quadVertices.data(), GL_STATIC_DRAW);
// Start with position enabled.
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLoc);
std::vector<GLColor> colorVertices(6, GLColor::blue);
const size_t colorBufferSize = sizeof(GLColor) * 6;
GLBuffer colorBuffer;
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glBufferData(GL_ARRAY_BUFFER, colorBufferSize, colorVertices.data(), GL_STATIC_DRAW);
// Start with color disabled.
glVertexAttribPointer(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, nullptr);
glDisableVertexAttribArray(colorLoc);
ASSERT_GL_NO_ERROR();
// Draw without a mapped buffer. Should succeed.
glVertexAttrib4f(colorLoc, 0, 1, 0, 1);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
// Map position buffer and draw. Should fail.
glBindBuffer(GL_ARRAY_BUFFER, posBuffer);
glMapBufferRange(GL_ARRAY_BUFFER, 0, posBufferSize, GL_MAP_READ_BIT);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Map position buffer and draw should fail.";
glUnmapBuffer(GL_ARRAY_BUFFER);
// Map then enable color buffer. Should fail.
glBindBuffer(GL_ARRAY_BUFFER, colorBuffer);
glMapBufferRange(GL_ARRAY_BUFFER, 0, colorBufferSize, GL_MAP_READ_BIT);
glEnableVertexAttribArray(colorLoc);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "Map then enable color buffer should fail.";
// Unmap then draw. Should succeed.
glUnmapBuffer(GL_ARRAY_BUFFER);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
}
// Tests that changing a vertex binding with glVertexAttribDivisor updates the mapped buffer check.
TEST_P(ValidationStateChangeTestES31, MapBufferAndDrawWithDivisor)
{
// Seems to trigger a GL error in some edge cases. http://anglebug.com/2755
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsNVIDIA());
// Initialize program and set up state.
ANGLE_GL_PROGRAM(program, kColorVS, kColorFS);
glUseProgram(program);
GLint positionLoc = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLoc);
GLint colorLoc = glGetAttribLocation(program, "color");
ASSERT_NE(-1, colorLoc);
// Create a user vertex array.
GLVertexArray vao;
glBindVertexArray(vao);
const std::array<Vector3, 6> &quadVertices = GetQuadVertices();
const size_t posBufferSize = quadVertices.size() * sizeof(Vector3);
GLBuffer posBuffer;
glBindBuffer(GL_ARRAY_BUFFER, posBuffer);
glBufferData(GL_ARRAY_BUFFER, posBufferSize, quadVertices.data(), GL_STATIC_DRAW);
// Start with position enabled.
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLoc);
std::vector<GLColor> blueVertices(6, GLColor::blue);
const size_t blueBufferSize = sizeof(GLColor) * 6;
GLBuffer blueBuffer;
glBindBuffer(GL_ARRAY_BUFFER, blueBuffer);
glBufferData(GL_ARRAY_BUFFER, blueBufferSize, blueVertices.data(), GL_STATIC_DRAW);
// Start with color enabled at an unused binding.
constexpr GLint kUnusedBinding = 3;
ASSERT_NE(colorLoc, kUnusedBinding);
ASSERT_NE(positionLoc, kUnusedBinding);
glVertexAttribFormat(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0);
glVertexAttribBinding(colorLoc, kUnusedBinding);
glBindVertexBuffer(kUnusedBinding, blueBuffer, 0, sizeof(GLColor));
glEnableVertexAttribArray(colorLoc);
// Make binding 'colorLoc' use a mapped buffer.
std::vector<GLColor> greenVertices(6, GLColor::green);
const size_t greenBufferSize = sizeof(GLColor) * 6;
GLBuffer greenBuffer;
glBindBuffer(GL_ARRAY_BUFFER, greenBuffer);
glBufferData(GL_ARRAY_BUFFER, greenBufferSize, greenVertices.data(), GL_STATIC_DRAW);
glMapBufferRange(GL_ARRAY_BUFFER, 0, greenBufferSize, GL_MAP_READ_BIT);
glBindVertexBuffer(colorLoc, greenBuffer, 0, sizeof(GLColor));
ASSERT_GL_NO_ERROR();
// Draw without a mapped buffer. Should succeed.
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
// Change divisor with VertexAttribDivisor. Should fail.
glVertexAttribDivisor(colorLoc, 0);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "draw with mapped buffer should fail.";
// Unmap the buffer. Should succeed.
glUnmapBuffer(GL_ARRAY_BUFFER);
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Tests that changing a vertex binding with glVertexAttribDivisor updates the buffer size check.
TEST_P(ValidationStateChangeTestES31, DrawPastEndOfBufferWithDivisor)
{
// Initialize program and set up state.
ANGLE_GL_PROGRAM(program, kColorVS, kColorFS);
glUseProgram(program);
GLint positionLoc = glGetAttribLocation(program, "position");
ASSERT_NE(-1, positionLoc);
GLint colorLoc = glGetAttribLocation(program, "color");
ASSERT_NE(-1, colorLoc);
// Create a user vertex array.
GLVertexArray vao;
glBindVertexArray(vao);
const std::array<Vector3, 6> &quadVertices = GetQuadVertices();
const size_t posBufferSize = quadVertices.size() * sizeof(Vector3);
GLBuffer posBuffer;
glBindBuffer(GL_ARRAY_BUFFER, posBuffer);
glBufferData(GL_ARRAY_BUFFER, posBufferSize, quadVertices.data(), GL_STATIC_DRAW);
// Start with position enabled.
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLoc);
std::vector<GLColor> blueVertices(6, GLColor::blue);
const size_t blueBufferSize = sizeof(GLColor) * 6;
GLBuffer blueBuffer;
glBindBuffer(GL_ARRAY_BUFFER, blueBuffer);
glBufferData(GL_ARRAY_BUFFER, blueBufferSize, blueVertices.data(), GL_STATIC_DRAW);
// Start with color enabled at an unused binding.
constexpr GLint kUnusedBinding = 3;
ASSERT_NE(colorLoc, kUnusedBinding);
ASSERT_NE(positionLoc, kUnusedBinding);
glVertexAttribFormat(colorLoc, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0);
glVertexAttribBinding(colorLoc, kUnusedBinding);
glBindVertexBuffer(kUnusedBinding, blueBuffer, 0, sizeof(GLColor));
glEnableVertexAttribArray(colorLoc);
// Make binding 'colorLoc' use a small buffer.
std::vector<GLColor> greenVertices(6, GLColor::green);
const size_t greenBufferSize = sizeof(GLColor) * 3;
GLBuffer greenBuffer;
glBindBuffer(GL_ARRAY_BUFFER, greenBuffer);
glBufferData(GL_ARRAY_BUFFER, greenBufferSize, greenVertices.data(), GL_STATIC_DRAW);
glBindVertexBuffer(colorLoc, greenBuffer, 0, sizeof(GLColor));
ASSERT_GL_NO_ERROR();
// Draw without a mapped buffer. Should succeed.
glDrawArrays(GL_TRIANGLES, 0, 6);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
// Change divisor with VertexAttribDivisor. Should fail.
glVertexAttribDivisor(colorLoc, 0);
ASSERT_GL_NO_ERROR();
glDrawArrays(GL_TRIANGLES, 0, 6);
EXPECT_GL_ERROR(GL_INVALID_OPERATION) << "draw with small buffer should fail.";
// Do a small draw. Should succeed.
glDrawArrays(GL_TRIANGLES, 0, 3);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
} // anonymous namespace } // anonymous namespace
ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_VULKAN()); ANGLE_INSTANTIATE_TEST(StateChangeTest, ES2_D3D9(), ES2_D3D11(), ES2_OPENGL(), ES2_VULKAN());
...@@ -2078,3 +2326,5 @@ ANGLE_INSTANTIATE_TEST(StateChangeRenderTest, ...@@ -2078,3 +2326,5 @@ ANGLE_INSTANTIATE_TEST(StateChangeRenderTest,
ES2_VULKAN()); ES2_VULKAN());
ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL()); ANGLE_INSTANTIATE_TEST(StateChangeTestES3, ES3_D3D11(), ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(SimpleStateChangeTest, ES2_VULKAN(), ES2_OPENGL()); ANGLE_INSTANTIATE_TEST(SimpleStateChangeTest, ES2_VULKAN(), ES2_OPENGL());
ANGLE_INSTANTIATE_TEST(ValidationStateChangeTest, ES3_D3D11(), ES3_OPENGL());
ANGLE_INSTANTIATE_TEST(ValidationStateChangeTestES31, ES31_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