Commit 806d812e by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Reorder descriptor sets

This change moves driver uniforms to a set with a lower id than the one that includes (emulation) transform feedback buffers. Imagine the following: - Program 1 with xfb: UniformsSet: 1 VS uniforms buffer, 1 FS uniforms buffer, 1 xfb buffer - Program 2 without xfb: UniformsSet: 1 VS uniforms bufer, 1 FS uniforms buffer. Previously, UniformsSet was index 0, and DriverUniformsSet was index 3. When switching from Program 1 to Program 2, the layout of UniformsSet changes, which means every subsequent set needs to be rebound. This is a Vulkan pipeline layout compatibility rule. This is done with invalidateCurrentTextures() and invalidateCurrentShaderResources() already when handling gl::State::DIRTY_BIT_PROGRAM_EXECUTABLE. The bug is that the driver uniforms are not invalidated. This is normally not an issue, because usually when switching from Program 1 to Program 2, transform feedback is paused, and this state change does invalidate driver uniforms. However, the following scenario doesn't do this: - Begin Xfb - Pause Xfb - Use Program 1 - Draw - Use Program 2 - Draw - End Xfb There is no driver state change between the two draw calls, which means the second draw will attempt to draw using the driver uniforms bound for the first draw call. There is a Vulkan validation error here due to the above pipeline layout validation rule. The issue manifests itself only when the second draw call actually uses driver uniforms, as otherwise that set is inactive and not validated; i.e. when line raster emulation is used. In summary, the validation error manifests itself when: - Transform feedback and line raster both use emulation - Transform feedback is paused - A draw with an xfb program is followed by a non-xfb program - The second draw is a line draw A test is added for this. The solution is to reorder the sets so that DriverUniformsSet is placed before UniformsSet. This way, changes to the layout of UniformsSet don't invalidate DriverUniformsSet. In fact, based on the above, any change in the layout of the program should have required an invalidation of the driver uniforms. This bug is probably masked by the fact that ContextVk::handleDirtyDescriptorSetsImpl() always rebinds the graphics driver every time any descriptor set needs rebinding. That should be removed in a follow up change. Bug: angleproject:4261 Change-Id: I21ad4152b454a1fe70554be02e18a9c94fb3e7a8 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1986927Reviewed-by: 's avatarTobin Ehlis <tobine@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 51e653f0
...@@ -1724,9 +1724,10 @@ angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk, ...@@ -1724,9 +1724,10 @@ angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk,
// Find the maximum non-null descriptor set. This is used in conjunction with a driver // Find the maximum non-null descriptor set. This is used in conjunction with a driver
// workaround to bind empty descriptor sets only for gaps in between 0 and max and avoid // workaround to bind empty descriptor sets only for gaps in between 0 and max and avoid
// binding unnecessary empty descriptor sets for the sets beyond max. // binding unnecessary empty descriptor sets for the sets beyond max.
size_t descriptorSetRange = 0; const size_t descriptorSetStart = kUniformsAndXfbDescriptorSetIndex;
for (size_t descriptorSetIndex = 0; descriptorSetIndex < mDescriptorSets.size(); size_t descriptorSetRange = 0;
++descriptorSetIndex) for (size_t descriptorSetIndex = descriptorSetStart;
descriptorSetIndex < mDescriptorSets.size(); ++descriptorSetIndex)
{ {
if (mDescriptorSets[descriptorSetIndex] != VK_NULL_HANDLE) if (mDescriptorSets[descriptorSetIndex] != VK_NULL_HANDLE)
{ {
...@@ -1737,7 +1738,7 @@ angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk, ...@@ -1737,7 +1738,7 @@ angle::Result ProgramVk::updateDescriptorSets(ContextVk *contextVk,
const VkPipelineBindPoint pipelineBindPoint = const VkPipelineBindPoint pipelineBindPoint =
mState.isCompute() ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS; mState.isCompute() ? VK_PIPELINE_BIND_POINT_COMPUTE : VK_PIPELINE_BIND_POINT_GRAPHICS;
for (uint32_t descriptorSetIndex = 0; descriptorSetIndex < descriptorSetRange; for (uint32_t descriptorSetIndex = descriptorSetStart; descriptorSetIndex < descriptorSetRange;
++descriptorSetIndex) ++descriptorSetIndex)
{ {
VkDescriptorSet descSet = mDescriptorSets[descriptorSetIndex]; VkDescriptorSet descSet = mDescriptorSets[descriptorSetIndex];
......
...@@ -900,23 +900,27 @@ class PipelineLayoutCache final : angle::NonCopyable ...@@ -900,23 +900,27 @@ class PipelineLayoutCache final : angle::NonCopyable
// //
// The set/binding assignment is done as following: // The set/binding assignment is done as following:
// //
// - Set 0 contains uniform blocks created to encompass default uniforms. 1 binding is used per // - Set 0 contains the ANGLE driver uniforms at binding 0. Note that driver uniforms are updated
// only under rare circumstances, such as viewport or depth range change. However, there is only
// one binding in this set. This set is placed before Set 1 containing transform feedback
// buffers, so that switching between xfb and non-xfb programs doesn't require rebinding this set.
// Otherwise, as the layout of Set 1 changes (due to addition and removal of xfb buffers), and all
// subsequent sets need to be rebound (due to Vulkan pipeline layout validation rules), we would
// have needed to invalidateGraphicsDriverUniforms().
// - Set 1 contains uniform blocks created to encompass default uniforms. 1 binding is used per
// pipeline stage. Additionally, transform feedback buffers are bound from binding 2 and up. // pipeline stage. Additionally, transform feedback buffers are bound from binding 2 and up.
// - Set 1 contains all textures. // - Set 2 contains all textures.
// - Set 2 contains all other shader resources, such as uniform and storage blocks, atomic counter // - Set 3 contains all other shader resources, such as uniform and storage blocks, atomic counter
// buffers and images. // buffers and images.
// - Set 3 contains the ANGLE driver uniforms at binding 0. Note that driver uniforms are updated
// only under rare circumstances, such as viewport or depth range change. However, there is only
// one binding in this set.
// ANGLE driver uniforms set index (binding is always 0):
constexpr uint32_t kDriverUniformsDescriptorSetIndex = 0;
// Uniforms set index: // Uniforms set index:
constexpr uint32_t kUniformsAndXfbDescriptorSetIndex = 0; constexpr uint32_t kUniformsAndXfbDescriptorSetIndex = 1;
// Textures set index: // Textures set index:
constexpr uint32_t kTextureDescriptorSetIndex = 1; constexpr uint32_t kTextureDescriptorSetIndex = 2;
// Other shader resources set index: // Other shader resources set index:
constexpr uint32_t kShaderResourceDescriptorSetIndex = 2; constexpr uint32_t kShaderResourceDescriptorSetIndex = 3;
// ANGLE driver uniforms set index (binding is always 3):
constexpr uint32_t kDriverUniformsDescriptorSetIndex = 3;
// Only 1 driver uniform binding is used. // Only 1 driver uniform binding is used.
constexpr uint32_t kReservedDriverUniformBindingCount = 1; constexpr uint32_t kReservedDriverUniformBindingCount = 1;
......
...@@ -382,6 +382,39 @@ void main (void) ...@@ -382,6 +382,39 @@ void main (void)
EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, GLColor::black, kPixelTolerance); EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, GLColor::black, kPixelTolerance);
} }
// Tests that drawing with transform feedback paused, then lines without transform feedback works
// without Vulkan validation errors.
TEST_P(StateChangeTestES3, DrawPausedXfbThenNonXfbLines)
{
// glTransformFeedbackVaryings for program2 returns GL_INVALID_OPERATION on both Linux and
// windows. http://anglebug.com/4265
ANGLE_SKIP_TEST_IF(IsIntel() && IsOpenGL());
std::vector<std::string> tfVaryings = {"gl_Position"};
ANGLE_GL_PROGRAM_TRANSFORM_FEEDBACK(program1, essl1_shaders::vs::Simple(),
essl1_shaders::fs::Blue(), tfVaryings, GL_SEPARATE_ATTRIBS);
GLBuffer xfbBuffer;
glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, xfbBuffer);
glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 6 * sizeof(float[4]), nullptr, GL_STATIC_DRAW);
GLTransformFeedback xfb;
glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, xfb);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, xfbBuffer);
glUseProgram(program1);
glBeginTransformFeedback(GL_TRIANGLES);
glPauseTransformFeedback();
glDrawArrays(GL_TRIANGLES, 0, 6);
ANGLE_GL_PROGRAM(program2, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
glUseProgram(program2);
glDrawArrays(GL_LINES, 0, 6);
glEndTransformFeedback();
ASSERT_GL_NO_ERROR();
}
// Tests that vertex attribute value is preserved across context switches. // Tests that vertex attribute value is preserved across context switches.
TEST_P(StateChangeTest, MultiContextVertexAttribute) TEST_P(StateChangeTest, MultiContextVertexAttribute)
{ {
......
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