diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 5593e8df9..28baff878 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -25,6 +25,7 @@ Google Inc. Eric Boren Henry Bridge Nat Duca + Courtney Goeltzenleuchter Peter Kasting Vangelis Kokkevis Zhenyao Mo diff --git a/src/libANGLE/renderer/vulkan/ContextVk.cpp b/src/libANGLE/renderer/vulkan/ContextVk.cpp index ee67e7105..c963d666f 100644 --- a/src/libANGLE/renderer/vulkan/ContextVk.cpp +++ b/src/libANGLE/renderer/vulkan/ContextVk.cpp @@ -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; diff --git a/src/libANGLE/renderer/vulkan/ContextVk.h b/src/libANGLE/renderer/vulkan/ContextVk.h index eb301f5a2..01895074d 100644 --- a/src/libANGLE/renderer/vulkan/ContextVk.h +++ b/src/libANGLE/renderer/vulkan/ContextVk.h @@ -232,7 +232,11 @@ class ContextVk : public ContextImpl, public vk::Context, public vk::CommandBuff unsigned int line) override; const gl::ActiveTextureArray &getActiveTextures() const; - void setIndexBufferDirty() { mDirtyBits.set(DIRTY_BIT_INDEX_BUFFER); } + void setIndexBufferDirty() + { + mDirtyBits.set(DIRTY_BIT_INDEX_BUFFER); + mLastIndexBufferOffset = reinterpret_cast(angle::DirtyPointer); + } angle::Result flushImpl(const gl::Semaphore *semaphore); angle::Result finishImpl(); diff --git a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp index 3e0757fab..9163e15e0 100644 --- a/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp +++ b/src/libANGLE/renderer/vulkan/VertexArrayVk.cpp @@ -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; diff --git a/src/tests/gl_tests/StateChangeTest.cpp b/src/tests/gl_tests/StateChangeTest.cpp index 28aaa1343..d2298bfd9 100644 --- a/src/tests/gl_tests/StateChangeTest.cpp +++ b/src/tests/gl_tests/StateChangeTest.cpp @@ -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{ + {-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{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{ + {-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{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{ + {-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{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{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{ + {-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{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{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{ + {-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{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{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{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) {