D3D11: Fix buffer state tracking in TransformFeedback11. TransformFeedback11::getSOBuffers would only update the elements of mBuffers if the GL buffer binding was non-null. This could lead to setting a previously-deleted buffer on the DeviceContext later. Update the state tracking in TransformFeedback11 to null out entries in mBuffers every time a new buffer is bound and add a null check when synchronizing mBuffers. Bug: angleproject:489791425 Change-Id: Ic80e36c1511d5e14d41a13c56f5055c55f36bc20 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/7689826 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.cpp b/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.cpp index 4620f67..3560fee 100644 --- a/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.cpp +++ b/src/libANGLE/renderer/d3d/d3d11/TransformFeedback11.cpp
@@ -77,6 +77,7 @@ { mIsDirty = true; mBufferOffsets[index] = static_cast<UINT>(binding.getOffset()); + mBuffers[index] = nullptr; mRenderer->getStateManager()->invalidateTransformFeedback(); return angle::Result::Continue; } @@ -114,6 +115,10 @@ &mBuffers[bindingIdx], &feedback)); binding.get()->applyImplFeedback(context, feedback); } + else + { + mBuffers[bindingIdx] = nullptr; + } } *buffersOut = &mBuffers;
diff --git a/src/tests/gl_tests/TransformFeedbackTest.cpp b/src/tests/gl_tests/TransformFeedbackTest.cpp index ed019b1..d52d3b3 100644 --- a/src/tests/gl_tests/TransformFeedbackTest.cpp +++ b/src/tests/gl_tests/TransformFeedbackTest.cpp
@@ -4736,6 +4736,43 @@ glEndTransformFeedback(); } +// Test that deleting a buffer bound to a transform feedback slot that is not used by the current +// program. +TEST_P(TransformFeedbackTest, StaleBufferBinding) +{ + std::vector<std::string> tfVaryings = {"gl_Position"}; + mProgram = CompileProgramWithTransformFeedback( + essl3_shaders::vs::Simple(), essl3_shaders::fs::Red(), tfVaryings, GL_INTERLEAVED_ATTRIBS); + ASSERT_NE(0u, mProgram); + glUseProgram(mProgram); + + GLBuffer buf0, buf1; + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf0); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 1024, nullptr, GL_DYNAMIC_COPY); + glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buf1); + glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, 1024, nullptr, GL_DYNAMIC_COPY); + + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buf0); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, buf1); + + // Draw once with the buffers, syncs initial state. + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, 1); + glEndTransformFeedback(); + + // Regular draw while TF inactive, syncs null transform feedback buffers. + glDrawArrays(GL_POINTS, 0, 1); + + buf1.reset(); + + // Draw with TF after the buffer has been deleted. It should not be referenced. + glBeginTransformFeedback(GL_POINTS); + glDrawArrays(GL_POINTS, 0, 1); + glEndTransformFeedback(); + + ASSERT_GL_NO_ERROR(); +} + GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackTest); ANGLE_INSTANTIATE_TEST_ES3_AND(TransformFeedbackTest, ES3_VULKAN().disable(Feature::SupportsTransformFeedbackExtension),