Commit 4181bc97 by Shao Committed by Commit Bot

Fix bug in updating vertex attribs with client memory pointers

In DEBUG version, when using OpenGL back-ends, ANGLE may meet an INVALID_OPERATION error from driver when attemping to update an attribute which is disabled and using a client memory pointer. This patch fixes this bug by skipping such vertex attributes when updating them to driver. With this patch the process to update vertex attributes should be: (1) For enabled attributes using client memory pointer: update by streaming mode in streamAttributes() (2) For disabled attributes using client memory pointer: just label them dirty and skip them (3) For attributes using buffer objects: update with vertexAttrib*Pointer BUG=angleproject:1942 Change-Id: I57043e5904eb4a342fa22d449d98a957010170d6 Reviewed-on: https://chromium-review.googlesource.com/456747 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent ca27139e
...@@ -467,32 +467,30 @@ void VertexArrayGL::updateAttribPointer(size_t attribIndex) ...@@ -467,32 +467,30 @@ void VertexArrayGL::updateAttribPointer(size_t attribIndex)
return; return;
} }
mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID()); // Skip the attribute that is disabled and uses a client memory pointer.
const Buffer *arrayBuffer = binding.buffer.get(); const Buffer *arrayBuffer = binding.buffer.get();
if (arrayBuffer != nullptr) if (arrayBuffer == nullptr)
{
const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(arrayBuffer);
mStateManager->bindBuffer(GL_ARRAY_BUFFER, arrayBufferGL->getBufferID());
}
else
{ {
mStateManager->bindBuffer(GL_ARRAY_BUFFER, 0); ASSERT(!attrib.enabled);
// Mark the applied attribute as dirty by setting an invalid size so that if it doesn't
// use a client memory pointer later, there is no chance that the caching will skip it.
mAppliedAttributes[attribIndex].size = static_cast<GLuint>(-1);
return;
} }
mAppliedBindings[bindingIndex].buffer = binding.buffer; mStateManager->bindVertexArray(mVertexArrayID, getAppliedElementArrayBufferID());
const GLvoid *inputPointer = nullptr; // Since ANGLE always uses a non-zero VAO, we cannot use a client memory pointer on it:
if (arrayBuffer != nullptr) // [OpenGL ES 3.0.2] Section 2.8 page 24:
{ // An INVALID_OPERATION error is generated when a non-zero vertex array object is bound,
inputPointer = static_cast<const GLvoid *>( // zero is bound to the ARRAY_BUFFER buffer object binding point, and the pointer argument
reinterpret_cast<const uint8_t *>(binding.offset + attrib.relativeOffset)); // is not NULL.
} ASSERT(arrayBuffer != nullptr);
else const BufferGL *arrayBufferGL = GetImplAs<BufferGL>(arrayBuffer);
{ mStateManager->bindBuffer(GL_ARRAY_BUFFER, arrayBufferGL->getBufferID());
// Attributes using client memory ignore the VERTEX_ATTRIB_BINDING state. const GLvoid *inputPointer =
// https://www.opengl.org/registry/specs/ARB/vertex_attrib_binding reinterpret_cast<const uint8_t *>(binding.offset + attrib.relativeOffset);
inputPointer = attrib.pointer;
}
if (attrib.pureInteger) if (attrib.pureInteger)
{ {
...@@ -514,6 +512,7 @@ void VertexArrayGL::updateAttribPointer(size_t attribIndex) ...@@ -514,6 +512,7 @@ void VertexArrayGL::updateAttribPointer(size_t attribIndex)
mAppliedBindings[bindingIndex].stride = binding.stride; mAppliedBindings[bindingIndex].stride = binding.stride;
mAppliedBindings[bindingIndex].offset = binding.offset; mAppliedBindings[bindingIndex].offset = binding.offset;
mAppliedBindings[bindingIndex].buffer = binding.buffer;
} }
void VertexArrayGL::updateAttribDivisor(size_t attribIndex) void VertexArrayGL::updateAttribDivisor(size_t attribIndex)
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// //
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
using namespace angle; using namespace angle;
...@@ -281,7 +282,6 @@ class VertexAttributeTest : public ANGLETest ...@@ -281,7 +282,6 @@ class VertexAttributeTest : public ANGLETest
TEST_P(VertexAttributeTest, UnsignedByteUnnormalized) TEST_P(VertexAttributeTest, UnsignedByteUnnormalized)
{ {
std::array<GLubyte, kVertexCount> inputData = { std::array<GLubyte, kVertexCount> inputData = {
{0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255}}; {0, 1, 2, 3, 4, 5, 6, 7, 125, 126, 127, 128, 129, 250, 251, 252, 253, 254, 255}};
std::array<GLfloat, kVertexCount> expectedData; std::array<GLfloat, kVertexCount> expectedData;
...@@ -626,6 +626,84 @@ TEST_P(VertexAttributeTest, DrawArraysWithBufferOffset) ...@@ -626,6 +626,84 @@ TEST_P(VertexAttributeTest, DrawArraysWithBufferOffset)
EXPECT_GL_NO_ERROR(); EXPECT_GL_NO_ERROR();
} }
// Verify that when we pass a client memory pointer to a disabled attribute the draw is still
// correct.
TEST_P(VertexAttributeTest, DrawArraysWithDisabledAttribute)
{
initBasicProgram();
std::array<GLfloat, kVertexCount> inputData;
std::array<GLfloat, kVertexCount> expectedData;
InitTestData(inputData, expectedData);
auto quadVertices = GetQuadVertices();
GLsizei quadVerticesSize = static_cast<GLsizei>(quadVertices.size() * sizeof(quadVertices[0]));
glGenBuffers(1, &mQuadBuffer);
glBindBuffer(GL_ARRAY_BUFFER, mQuadBuffer);
glBufferData(GL_ARRAY_BUFFER, quadVerticesSize, quadVertices.data(), GL_STATIC_DRAW);
GLint positionLocation = glGetAttribLocation(mProgram, "position");
ASSERT_NE(-1, positionLocation);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(inputData), inputData.data(), GL_STATIC_DRAW);
glVertexAttribPointer(mTestAttrib, 1, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(mTestAttrib);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(mExpectedAttrib, 1, GL_FLOAT, GL_FALSE, 0, expectedData.data());
glEnableVertexAttribArray(mExpectedAttrib);
// mProgram2 adds an attribute 'disabled' on the basis of mProgram.
const std::string testVertexShaderSource2 =
"attribute mediump vec4 position;\n"
"attribute mediump vec4 test;\n"
"attribute mediump vec4 expected;\n"
"attribute mediump vec4 disabled;\n"
"varying mediump vec4 color;\n"
"void main(void)\n"
"{\n"
" gl_Position = position;\n"
" vec4 threshold = max(abs(expected + disabled) * 0.005, 1.0 / 64.0);\n"
" color = vec4(lessThanEqual(abs(test - expected), threshold));\n"
"}\n";
const std::string testFragmentShaderSource =
"varying mediump vec4 color;\n"
"void main(void)\n"
"{\n"
" gl_FragColor = color;\n"
"}\n";
ANGLE_GL_PROGRAM(program, testVertexShaderSource2, testFragmentShaderSource);
GLuint mProgram2 = program.get();
ASSERT_EQ(positionLocation, glGetAttribLocation(mProgram2, "position"));
ASSERT_EQ(mTestAttrib, glGetAttribLocation(mProgram2, "test"));
ASSERT_EQ(mExpectedAttrib, glGetAttribLocation(mProgram2, "expected"));
// Pass a client memory pointer to disabledAttribute and disable it.
GLint disabledAttribute = glGetAttribLocation(mProgram2, "disabled");
ASSERT_EQ(-1, glGetAttribLocation(mProgram, "disabled"));
glVertexAttribPointer(disabledAttribute, 1, GL_FLOAT, GL_FALSE, 0, expectedData.data());
glDisableVertexAttribArray(disabledAttribute);
glUseProgram(mProgram);
glDrawArrays(GL_TRIANGLES, 0, 6);
checkPixels();
// Now enable disabledAttribute which should be used in mProgram2.
glEnableVertexAttribArray(disabledAttribute);
glUseProgram(mProgram2);
glDrawArrays(GL_TRIANGLES, 0, 6);
checkPixels();
EXPECT_GL_NO_ERROR();
}
class VertexAttributeTestES31 : public VertexAttributeTestES3 class VertexAttributeTestES31 : public VertexAttributeTestES3
{ {
protected: protected:
...@@ -874,7 +952,7 @@ void VertexAttributeCachingTest::SetUp() ...@@ -874,7 +952,7 @@ void VertexAttributeCachingTest::SetUp()
mExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_FALSE); mExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_FALSE);
mExpectedData[GL_UNSIGNED_BYTE] = GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_FALSE); mExpectedData[GL_UNSIGNED_BYTE] = GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_FALSE);
mExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_FALSE); mExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_FALSE);
mExpectedData[GL_UNSIGNED_SHORT] = mExpectedData[GL_UNSIGNED_SHORT] =
GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_FALSE); GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_FALSE);
mExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_FALSE); mExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_FALSE);
...@@ -981,8 +1059,10 @@ TEST_P(VertexAttributeCachingTest, BufferMulticachingWithOneUnchangedAttrib) ...@@ -981,8 +1059,10 @@ TEST_P(VertexAttributeCachingTest, BufferMulticachingWithOneUnchangedAttrib)
} }
} }
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// D3D11 Feature Level 9_3 uses different D3D formats for vertex attribs compared to Feature Levels 10_0+, so we should test them separately. // tests should be run against.
// D3D11 Feature Level 9_3 uses different D3D formats for vertex attribs compared to Feature Levels
// 10_0+, so we should test them separately.
ANGLE_INSTANTIATE_TEST(VertexAttributeTest, ANGLE_INSTANTIATE_TEST(VertexAttributeTest,
ES2_D3D9(), ES2_D3D9(),
ES2_D3D11(), ES2_D3D11(),
......
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