PPO: Fix updating sampler uniforms between draws
Updating sampler uniforms when using PPOs is currently broken, since the
Context/State use the currently bound ProgramExecutable which belongs to
the PPO, but the updates only happen to the Program's executable that
the uniform belongs to.
This change updates Program::updateSamplerUniform() to update any PPO
ProgramExecutables with the updated texture information when a Program's
sampler uniforms are updated, so the Context/State use the correct data.
Bug: b/182409935
Test: ProgramPipelineTest31.SampleTextureAThenTextureB
Test: SamplersTest31.SampleTextureAThenTextureB
Change-Id: I3c4e156c6e0c781e706f321f0bd12baf484ff42a
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2797951
Commit-Queue: Tim Van Patten <timvp@google.com>
Reviewed-by: Cody Northrop <cnorthrop@google.com>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/src/libANGLE/Observer.h b/src/libANGLE/Observer.h
index 18aa4e7..35b462a 100644
--- a/src/libANGLE/Observer.h
+++ b/src/libANGLE/Observer.h
@@ -55,6 +55,8 @@
// Indicates an external change to the default framebuffer.
SurfaceChanged,
+ // Indicates a separable program's textures or images changed in the ProgramExecutable.
+ ProgramTextureOrImageBindingChanged,
// Indicates a separable program was successfully re-linked.
ProgramRelinked,
// Indicates a separable program's sampler uniforms were updated.
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 4a00306..c030891 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -4445,6 +4445,14 @@
}
}
+ // Update the observing PPO's executable, if any.
+ // Do this before any of the Context work, since that uses the current ProgramExecutable,
+ // which will be the PPO's if this Program is bound to it, rather than this Program's.
+ if (isSeparable())
+ {
+ onStateChange(angle::SubjectMessage::ProgramTextureOrImageBindingChanged);
+ }
+
// Notify context.
if (context)
{
diff --git a/src/libANGLE/ProgramPipeline.cpp b/src/libANGLE/ProgramPipeline.cpp
index b005b08..25e89c2 100644
--- a/src/libANGLE/ProgramPipeline.cpp
+++ b/src/libANGLE/ProgramPipeline.cpp
@@ -660,8 +660,9 @@
{
switch (message)
{
- case angle::SubjectMessage::SubjectChanged:
+ case angle::SubjectMessage::ProgramTextureOrImageBindingChanged:
mState.mIsLinked = false;
+ mState.mExecutable->mActiveSamplerRefCounts.fill(0);
mState.updateExecutableTextures();
break;
case angle::SubjectMessage::ProgramRelinked:
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 4bc0d3c..84b0dd2 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -3581,7 +3581,7 @@
: nullptr;
updateTextureBinding(context, textureUnit, activeTexture);
- mExecutable->onStateChange(angle::SubjectMessage::SubjectChanged);
+ mExecutable->onStateChange(angle::SubjectMessage::ProgramTextureOrImageBindingChanged);
}
}
@@ -3618,7 +3618,7 @@
mDirtyObjects.set(DIRTY_OBJECT_IMAGES_INIT);
}
- mExecutable->onStateChange(angle::SubjectMessage::SubjectChanged);
+ mExecutable->onStateChange(angle::SubjectMessage::ProgramTextureOrImageBindingChanged);
}
}
diff --git a/src/tests/gl_tests/ProgramPipelineTest.cpp b/src/tests/gl_tests/ProgramPipelineTest.cpp
index e90370d..584ba29 100644
--- a/src/tests/gl_tests/ProgramPipelineTest.cpp
+++ b/src/tests/gl_tests/ProgramPipelineTest.cpp
@@ -835,6 +835,85 @@
EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
}
+// Test that updating a sampler uniform in a separable program behaves correctly with PPOs.
+TEST_P(ProgramPipelineTest31, SampleTextureAThenTextureB)
+{
+ ANGLE_SKIP_TEST_IF(!IsVulkan());
+
+ constexpr int kWidth = 2;
+ constexpr int kHeight = 2;
+
+ const GLchar *vertString = R"(#version 310 es
+precision highp float;
+in vec2 a_position;
+out vec2 texCoord;
+void main()
+{
+ gl_Position = vec4(a_position, 0, 1);
+ texCoord = a_position * 0.5 + vec2(0.5);
+})";
+
+ const GLchar *fragString = R"(#version 310 es
+precision highp float;
+in vec2 texCoord;
+uniform sampler2D tex;
+out vec4 my_FragColor;
+void main()
+{
+ my_FragColor = texture(tex, texCoord);
+})";
+
+ std::array<GLColor, kWidth *kHeight> redColor = {
+ {GLColor::red, GLColor::red, GLColor::red, GLColor::red}};
+ std::array<GLColor, kWidth *kHeight> greenColor = {
+ {GLColor::green, GLColor::green, GLColor::green, GLColor::green}};
+
+ // Create a red texture and bind to texture unit 0
+ GLTexture redTex;
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, redTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ redColor.data());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+ // Create a green texture and bind to texture unit 1
+ GLTexture greenTex;
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, greenTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ greenColor.data());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glActiveTexture(GL_TEXTURE0);
+ ASSERT_GL_NO_ERROR();
+
+ bindProgramPipeline(vertString, fragString);
+
+ GLint location1 = glGetUniformLocation(mFragProg, "tex");
+ ASSERT_NE(location1, -1);
+ glActiveShaderProgram(mPipeline, mFragProg);
+ ASSERT_GL_NO_ERROR();
+
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ // Draw red
+ glUniform1i(location1, 0);
+ ASSERT_GL_NO_ERROR();
+ drawQuadWithPPO("a_position", 0.5f, 1.0f);
+ ASSERT_GL_NO_ERROR();
+
+ // Draw green
+ glUniform1i(location1, 1);
+ ASSERT_GL_NO_ERROR();
+ drawQuadWithPPO("a_position", 0.5f, 1.0f);
+ ASSERT_GL_NO_ERROR();
+
+ EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::yellow);
+}
+
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProgramPipelineTest);
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ProgramPipelineTest);
diff --git a/src/tests/gl_tests/SamplersTest.cpp b/src/tests/gl_tests/SamplersTest.cpp
index 96ff853..6554f79 100644
--- a/src/tests/gl_tests/SamplersTest.cpp
+++ b/src/tests/gl_tests/SamplersTest.cpp
@@ -37,6 +37,9 @@
}
};
+class SamplersTest31 : public SamplersTest
+{};
+
// Verify that samplerParameterf supports TEXTURE_MAX_ANISOTROPY_EXT valid values.
TEST_P(SamplersTest, ValidTextureSamplerMaxAnisotropyExt)
{
@@ -85,7 +88,95 @@
validateInvalidAnisotropy(sampler, maxValue);
}
+// Test that updating a sampler uniform in a program behaves correctly.
+TEST_P(SamplersTest31, SampleTextureAThenTextureB)
+{
+ ANGLE_SKIP_TEST_IF(!IsVulkan());
+
+ constexpr int kWidth = 2;
+ constexpr int kHeight = 2;
+
+ const GLchar *vertString = R"(#version 310 es
+precision highp float;
+in vec2 a_position;
+out vec2 texCoord;
+void main()
+{
+ gl_Position = vec4(a_position, 0, 1);
+ texCoord = a_position * 0.5 + vec2(0.5);
+})";
+
+ const GLchar *fragString = R"(#version 310 es
+precision highp float;
+in vec2 texCoord;
+uniform sampler2D tex;
+out vec4 my_FragColor;
+void main()
+{
+ my_FragColor = texture(tex, texCoord);
+})";
+
+ std::array<GLColor, kWidth *kHeight> redColor = {
+ {GLColor::red, GLColor::red, GLColor::red, GLColor::red}};
+ std::array<GLColor, kWidth *kHeight> greenColor = {
+ {GLColor::green, GLColor::green, GLColor::green, GLColor::green}};
+
+ // Create a red texture and bind to texture unit 0
+ GLTexture redTex;
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, redTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ redColor.data());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ ASSERT_GL_NO_ERROR();
+ // Create a green texture and bind to texture unit 1
+ GLTexture greenTex;
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, greenTex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+ greenColor.data());
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glActiveTexture(GL_TEXTURE0);
+ ASSERT_GL_NO_ERROR();
+
+ GLProgram program;
+ program.makeRaster(vertString, fragString);
+ ASSERT_NE(0u, program.get());
+ glUseProgram(program);
+
+ GLint location = glGetUniformLocation(program, "tex");
+ ASSERT_NE(location, -1);
+ ASSERT_GL_NO_ERROR();
+
+ // Draw red
+ glUniform1i(location, 0);
+ ASSERT_GL_NO_ERROR();
+ drawQuad(program, "a_position", 0.5f);
+ ASSERT_GL_NO_ERROR();
+
+ glEnable(GL_BLEND);
+ glBlendEquation(GL_FUNC_ADD);
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ // Draw green
+ glUniform1i(location, 1);
+ ASSERT_GL_NO_ERROR();
+ drawQuad(program, "a_position", 0.5f);
+ ASSERT_GL_NO_ERROR();
+
+ // Draw red
+ glUniform1i(location, 0);
+ ASSERT_GL_NO_ERROR();
+ drawQuad(program, "a_position", 0.5f);
+ ASSERT_GL_NO_ERROR();
+
+ EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::yellow);
+}
+
// Samplers are only supported on ES3.
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SamplersTest);
ANGLE_INSTANTIATE_TEST_ES3(SamplersTest);
+ANGLE_INSTANTIATE_TEST_ES31(SamplersTest31);
} // namespace angle