Include the fragment output locations in the program cache key. If the user rebinds the output locations and relinks a program, the wrong program may be loaded from the cache. TEST=gl_tests: TranslatorVariants/EXTBlendFuncExtended* BUG=angleproject:4535 Change-Id: If9a9c2ad935ea4d01c3fe4313810d221e9c9ce38 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2131252 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com>
diff --git a/src/libANGLE/MemoryProgramCache.cpp b/src/libANGLE/MemoryProgramCache.cpp index 299b2e3..7fea58c 100644 --- a/src/libANGLE/MemoryProgramCache.cpp +++ b/src/libANGLE/MemoryProgramCache.cpp
@@ -116,6 +116,7 @@ // Hash pre-link program properties. hashStream << program->getAttributeBindings() << program->getUniformLocationBindings() + << program->getFragmentOutputLocations() << program->getFragmentOutputIndexes() << program->getState().getTransformFeedbackVaryingNames() << program->getState().getTransformFeedbackBufferMode() << program->getState().getOutputLocations()
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp index 8801048..1a20de6 100644 --- a/src/libANGLE/Program.cpp +++ b/src/libANGLE/Program.cpp
@@ -2371,6 +2371,18 @@ return mState.mUniformLocationBindings; } +const gl::ProgramAliasedBindings &Program::getFragmentOutputLocations() const +{ + ASSERT(mLinkResolved); + return mFragmentOutputLocations; +} + +const gl::ProgramAliasedBindings &Program::getFragmentOutputIndexes() const +{ + ASSERT(mLinkResolved); + return mFragmentOutputIndexes; +} + ComponentTypeMask Program::getDrawBufferTypeMask() const { ASSERT(mLinkResolved);
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h index 5788ffb..690c3bd 100644 --- a/src/libANGLE/Program.h +++ b/src/libANGLE/Program.h
@@ -807,6 +807,8 @@ const ProgramBindings &getAttributeBindings() const; const ProgramAliasedBindings &getUniformLocationBindings() const; + const ProgramAliasedBindings &getFragmentOutputLocations() const; + const ProgramAliasedBindings &getFragmentOutputIndexes() const; int getNumViews() const {
diff --git a/src/tests/egl_tests/EGLBlobCacheTest.cpp b/src/tests/egl_tests/EGLBlobCacheTest.cpp index 8cf8499..3b23083 100644 --- a/src/tests/egl_tests/EGLBlobCacheTest.cpp +++ b/src/tests/egl_tests/EGLBlobCacheTest.cpp
@@ -108,10 +108,7 @@ void testTearDown() override { gApplicationCache.clear(); } - bool programBinaryAvailable() - { - return (getClientMajorVersion() >= 3 || IsGLExtensionEnabled("GL_OES_get_program_binary")); - } + bool programBinaryAvailable() { return IsGLExtensionEnabled("GL_OES_get_program_binary"); } bool mHasBlobCache; }; @@ -232,4 +229,57 @@ EXPECT_EGL_ERROR(EGL_BAD_PARAMETER); } -ANGLE_INSTANTIATE_TEST_ES2(EGLBlobCacheTest); +// Regression test for including the fragment output locatins in the program key. +// http://anglebug.com/4535 +TEST_P(EGLBlobCacheTest, FragmentOutputLocationKey) +{ + ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_blend_func_extended") || + getClientMajorVersion() < 3); + + EGLDisplay display = getEGLWindow()->getDisplay(); + + EXPECT_TRUE(mHasBlobCache); + eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob); + ASSERT_EGL_SUCCESS(); + + // Compile a shader so it puts something in the cache + if (programBinaryAvailable()) + { + constexpr char kFragmentShaderSrc[] = R"(#version 300 es +#extension GL_EXT_blend_func_extended : require +precision mediump float; +uniform vec4 src; +uniform vec4 src1; +out vec4 FragData; +out vec4 SecondaryFragData; +void main() { + FragData = src; + SecondaryFragData = src1; +})"; + + constexpr char kVertexShaderSrc[] = R"(#version 300 es +in vec4 position; +void main() { + gl_Position = position; +})"; + + GLuint program = CompileProgram(kVertexShaderSrc, kFragmentShaderSrc, [](GLuint p) { + glBindFragDataLocationEXT(p, 0, "FragData[0]"); + glBindFragDataLocationIndexedEXT(p, 0, 1, "SecondaryFragData[0]"); + }); + ASSERT_NE(0u, program); + EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult); + gLastCacheOpResult = CacheOpResult::ValueNotSet; + + // Re-link the program with different fragment output bindings + program = CompileProgram(kVertexShaderSrc, kFragmentShaderSrc, [](GLuint p) { + glBindFragDataLocationEXT(p, 0, "FragData"); + glBindFragDataLocationIndexedEXT(p, 0, 1, "SecondaryFragData"); + }); + ASSERT_NE(0u, program); + EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult); + gLastCacheOpResult = CacheOpResult::ValueNotSet; + } +} + +ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(EGLBlobCacheTest);