Vulkan: Fix dirty element array buffer updates.

The issue occurs that binds the same buffer and calls glDrawElements
with the same indices. The offset has been reset in the
VertexArrayVk::syncState(), but it doesn't check the actual value
in the ContextVk::setupIndexedDraw().
Also corrected case where update via BufferSubData wasn't being
sent to the HW.

Bug: angleproject:3362
Change-Id: I0f7d2fc162bc8f1c36cb09ba689fd27b482b9035
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1666345
Commit-Queue: Courtney Goeltzenleuchter <courtneygo@google.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Courtney Goeltzenleuchter
2019-06-19 12:11:13 -06:00
committed by Commit Bot
parent 661ed41972
commit b867bc6f10
5 changed files with 349 additions and 4 deletions

View File

@@ -25,6 +25,7 @@ Google Inc.
Eric Boren
Henry Bridge
Nat Duca
Courtney Goeltzenleuchter
Peter Kasting
Vangelis Kokkevis
Zhenyao Mo

View File

@@ -481,10 +481,12 @@ angle::Result ContextVk::setupIndexedDraw(const gl::Context *context,
const void *indices,
vk::CommandBuffer **commandBufferOut)
{
ASSERT(mode != gl::PrimitiveMode::LineLoop);
if (indexType != mCurrentDrawElementsType)
{
mDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
mCurrentDrawElementsType = indexType;
setIndexBufferDirty();
}
const gl::Buffer *elementArrayBuffer = mVertexArray->getState().getElementArrayBuffer();
@@ -524,7 +526,7 @@ angle::Result ContextVk::setupLineLoopDraw(const gl::Context *context,
{
ANGLE_TRY(mVertexArray->handleLineLoop(this, firstVertex, vertexOrIndexCount,
indexTypeOrInvalid, indices, numIndicesOut));
mDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
setIndexBufferDirty();
mCurrentDrawElementsType = indexTypeOrInvalid != gl::DrawElementsType::InvalidEnum
? indexTypeOrInvalid
: gl::DrawElementsType::UnsignedInt;

View File

@@ -232,7 +232,11 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff
unsigned int line) override;
const gl::ActiveTextureArray<TextureVk *> &getActiveTextures() const;
void setIndexBufferDirty() { mDirtyBits.set(DIRTY_BIT_INDEX_BUFFER); }
void setIndexBufferDirty()
{
mDirtyBits.set(DIRTY_BIT_INDEX_BUFFER);
mLastIndexBufferOffset = reinterpret_cast<const void *>(angle::DirtyPointer);
}
angle::Result flushImpl(const gl::Semaphore *semaphore);
angle::Result finishImpl();

View File

@@ -345,7 +345,6 @@ angle::Result VertexArrayVk::syncState(const gl::Context *context,
mCurrentElementArrayBuffer = nullptr;
}
mCurrentElementArrayBufferOffset = 0;
mLineLoopBufferFirstIndex.reset();
mLineLoopBufferLastIndex.reset();
contextVk->setIndexBufferDirty();
@@ -356,6 +355,7 @@ angle::Result VertexArrayVk::syncState(const gl::Context *context,
case gl::VertexArray::DIRTY_BIT_ELEMENT_ARRAY_BUFFER_DATA:
mLineLoopBufferFirstIndex.reset();
mLineLoopBufferLastIndex.reset();
contextVk->setIndexBufferDirty();
mDirtyLineLoopTranslation = true;
break;

View File

@@ -1248,6 +1248,68 @@ TEST_P(LineLoopStateChangeTest, DrawArraysThenDrawElements)
validateSquareAndHourglass();
}
// Draw a triangle with a drawElements call and a non-zero offset and draw the same
// triangle with the same offset again followed by a line loop with drawElements.
TEST_P(LineLoopStateChangeTest, DrawElementsThenDrawElements)
{
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
glUseProgram(program);
// Background Red color
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// We expect to draw a triangle with the last three points on the bottom right,
// draw with LineLoop, and then draw a triangle with the same non-zero offset.
auto vertices = std::vector<Vector3>{
{-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}};
auto indices = std::vector<GLushort>{0, 1, 2, 1, 2, 3};
GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0],
GL_STATIC_DRAW);
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
// Draw a triangle with a non-zero offset on the bottom right.
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort)));
// Draw with LineLoop.
glDrawElements(GL_LINE_LOOP, 3, GL_UNSIGNED_SHORT, nullptr);
// Draw the triangle again with the same offset.
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort)));
glDisableVertexAttribArray(positionLocation);
ASSERT_GL_NO_ERROR();
int quarterWidth = getWindowWidth() / 4;
int quarterHeight = getWindowHeight() / 4;
// Validate the top left point's color.
EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() - 1, GLColor::blue);
// Validate the triangle is drawn on the bottom right.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue);
// Validate the triangle is NOT on the top left part.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::red);
}
// Simple state change tests, primarily focused on basic object lifetime and dependency management
// with back-ends that don't support that automatically (i.e. Vulkan).
class SimpleStateChangeTest : public ANGLETest
@@ -1430,6 +1492,282 @@ TEST_P(SimpleStateChangeTest, DrawArraysThenDrawElements)
EXPECT_PIXEL_COLOR_EQ((quarterWidth * 3), halfHeight, GLColor::blue);
}
// Draw a triangle with drawElements and a non-zero offset and draw the same
// triangle with the same offset followed by binding the same element buffer.
TEST_P(SimpleStateChangeTest, DrawElementsThenDrawElements)
{
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
glUseProgram(program);
// Background Red color
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// We expect to draw the triangle with the last three points on the bottom right, and
// rebind the same element buffer and draw with the same indices.
auto vertices = std::vector<Vector3>{
{-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}};
auto indices = std::vector<GLushort>{0, 1, 2, 1, 2, 3};
GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
GLBuffer indexBuffer;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLushort), &indices[0],
GL_STATIC_DRAW);
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort)));
// Rebind the same element buffer.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
// Draw the triangle again with the same offset.
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(3 * sizeof(GLushort)));
glDisableVertexAttribArray(positionLocation);
ASSERT_GL_NO_ERROR();
int quarterWidth = getWindowWidth() / 4;
int quarterHeight = getWindowHeight() / 4;
// Validate the triangle is drawn on the bottom right.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue);
// Validate the triangle is NOT on the top left part.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::red);
}
// Draw a triangle with drawElements then change the index buffer and draw again.
TEST_P(SimpleStateChangeTest, DrawElementsThenDrawElementsNewIndexBuffer)
{
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(program);
// Background Red color
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// We expect to draw the triangle with the last three points on the bottom right, and
// rebind the same element buffer and draw with the same indices.
auto vertices = std::vector<Vector3>{
{-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}};
auto indices8 = std::vector<GLubyte>{0, 1, 2, 1, 2, 3};
GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
GLint colorUniformLocation =
glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUniform4f(colorUniformLocation, 1.0f, 1.0f, 1.0f, 1.0f);
GLBuffer indexBuffer8;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer8);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices8.size() * sizeof(GLubyte), &indices8[0],
GL_STATIC_DRAW);
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte)));
auto indices2nd8 = std::vector<GLubyte>{2, 3, 0, 0, 1, 2};
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices2nd8.size() * sizeof(GLubyte), &indices2nd8[0],
GL_STATIC_DRAW);
glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
// Draw the triangle again with the same offset.
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte)));
glDisableVertexAttribArray(positionLocation);
ASSERT_GL_NO_ERROR();
int quarterWidth = getWindowWidth() / 4;
int quarterHeight = getWindowHeight() / 4;
// Validate the triangle is drawn on the bottom left.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::blue);
// Validate the triangle is NOT on the top right part.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::white);
}
// Draw a triangle with drawElements then change the indices and draw again.
TEST_P(SimpleStateChangeTest, DrawElementsThenDrawElementsNewIndices)
{
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(program);
// Background Red color
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// We expect to draw the triangle with the last three points on the bottom right, and
// rebind the same element buffer and draw with the same indices.
auto vertices = std::vector<Vector3>{
{-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}};
auto indices8 = std::vector<GLubyte>{0, 1, 2, 2, 3, 0};
GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
GLint colorUniformLocation =
glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUniform4f(colorUniformLocation, 1.0f, 1.0f, 1.0f, 1.0f);
GLBuffer indexBuffer8;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer8);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices8.size() * sizeof(GLubyte), &indices8[0],
GL_DYNAMIC_DRAW);
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte)));
auto newIndices8 = std::vector<GLubyte>{2, 3, 0};
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, newIndices8.size() * sizeof(GLubyte),
&newIndices8[0]);
glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
// Draw the triangle again with the same offset.
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte)));
glDisableVertexAttribArray(positionLocation);
ASSERT_GL_NO_ERROR();
int quarterWidth = getWindowWidth() / 4;
int quarterHeight = getWindowHeight() / 4;
// Validate the triangle is drawn on the bottom left.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::blue);
// Validate the triangle is NOT on the top right part.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::white);
}
// Draw a triangle with drawElements and a non-zero offset and draw the same
// triangle with the same offset followed by binding a USHORT element buffer.
TEST_P(SimpleStateChangeTest, DrawElementsUBYTEX2ThenDrawElementsUSHORT)
{
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
glUseProgram(program);
// Background Red color
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// We expect to draw the triangle with the last three points on the bottom right, and
// rebind the same element buffer and draw with the same indices.
auto vertices = std::vector<Vector3>{
{-1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}, {-1.0f, -1.0f, 0.0f}};
auto indices8 = std::vector<GLubyte>{0, 1, 2, 1, 2, 3};
GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
ASSERT_NE(-1, positionLocation);
GLint colorUniformLocation =
glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
ASSERT_NE(colorUniformLocation, -1);
glUniform4f(colorUniformLocation, 1.0f, 1.0f, 1.0f, 1.0f);
GLBuffer indexBuffer8;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer8);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices8.size() * sizeof(GLubyte), &indices8[0],
GL_STATIC_DRAW);
GLBuffer vertexBuffer;
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
GL_STATIC_DRAW);
glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(positionLocation);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte)));
auto indices2nd8 = std::vector<GLubyte>{2, 3, 0, 0, 1, 2};
GLBuffer indexBuffer2nd8;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer2nd8);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices2nd8.size() * sizeof(GLubyte), &indices2nd8[0],
GL_STATIC_DRAW);
glUniform4f(colorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, (void *)(0 * sizeof(GLubyte)));
// Bind the 16bit element buffer.
auto indices16 = std::vector<GLushort>{0, 1, 3, 1, 2, 3};
GLBuffer indexBuffer16;
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer16);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices16.size() * sizeof(GLushort), &indices16[0],
GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer16);
glUniform4f(colorUniformLocation, 0.0f, 0.0f, 1.0f, 1.0f);
// Draw the triangle again with the same offset.
glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, (void *)(0 * sizeof(GLushort)));
glDisableVertexAttribArray(positionLocation);
ASSERT_GL_NO_ERROR();
int quarterWidth = getWindowWidth() / 4;
int quarterHeight = getWindowHeight() / 4;
// Validate green triangle is drawn on the bottom.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight, GLColor::green);
// Validate white triangle is drawn on the right.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 3, quarterHeight * 2, GLColor::white);
// Validate blue triangle is on the top left part.
EXPECT_PIXEL_COLOR_EQ(quarterWidth * 2, quarterHeight * 3, GLColor::blue);
EXPECT_PIXEL_COLOR_EQ(quarterWidth, quarterHeight * 2, GLColor::blue);
}
// Handles deleting a Buffer when it's being used.
TEST_P(SimpleStateChangeTest, DeleteBufferInUse)
{