xref: /aosp_15_r20/external/angle/src/tests/egl_tests/EGLBlobCacheTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2018 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // EGLBlobCacheTest:
7 //   Unit tests for the EGL_ANDROID_blob_cache extension.
8 
9 // Must be included first to prevent errors with "None".
10 #include "test_utils/ANGLETest.h"
11 
12 #include <map>
13 #include <vector>
14 
15 #include "common/PackedEnums.h"
16 #include "common/angleutils.h"
17 #include "test_utils/ANGLETest.h"
18 #include "test_utils/MultiThreadSteps.h"
19 #include "test_utils/gl_raii.h"
20 #include "util/EGLWindow.h"
21 #include "util/test_utils.h"
22 
23 using namespace angle;
24 
25 constexpr char kEGLExtName[] = "EGL_ANDROID_blob_cache";
26 
27 enum class CacheOpResult
28 {
29     SetSuccess,
30     GetNotFound,
31     GetMemoryTooSmall,
32     GetSuccess,
33     ValueNotSet,
34     EnumCount
35 };
36 
37 angle::PackedEnumMap<CacheOpResult, std::string> kCacheOpToString = {
38     {CacheOpResult::SetSuccess, "SetSuccess"},
39     {CacheOpResult::GetNotFound, "GetNotFound"},
40     {CacheOpResult::GetMemoryTooSmall, "GetMemoryTooSmall"},
41     {CacheOpResult::GetSuccess, "GetSuccess"},
42     {CacheOpResult::ValueNotSet, "ValueNotSet"},
43 };
44 
operator <<(std::ostream & os,CacheOpResult result)45 std::ostream &operator<<(std::ostream &os, CacheOpResult result)
46 {
47     return os << kCacheOpToString[result];
48 }
49 
50 namespace
51 {
52 std::map<std::vector<uint8_t>, std::vector<uint8_t>> gApplicationCache;
53 CacheOpResult gLastCacheOpResult = CacheOpResult::ValueNotSet;
54 
SetBlob(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)55 void SetBlob(const void *key, EGLsizeiANDROID keySize, const void *value, EGLsizeiANDROID valueSize)
56 {
57     std::vector<uint8_t> keyVec(keySize);
58     memcpy(keyVec.data(), key, keySize);
59 
60     std::vector<uint8_t> valueVec(valueSize);
61     memcpy(valueVec.data(), value, valueSize);
62 
63     gApplicationCache[keyVec] = valueVec;
64 
65     gLastCacheOpResult = CacheOpResult::SetSuccess;
66 }
67 
SetCorruptedBlob(const void * key,EGLsizeiANDROID keySize,const void * value,EGLsizeiANDROID valueSize)68 void SetCorruptedBlob(const void *key,
69                       EGLsizeiANDROID keySize,
70                       const void *value,
71                       EGLsizeiANDROID valueSize)
72 {
73     std::vector<uint8_t> keyVec(keySize);
74     memcpy(keyVec.data(), key, keySize);
75 
76     std::vector<uint8_t> valueVec(valueSize);
77     memcpy(valueVec.data(), value, valueSize);
78 
79     // Corrupt the data
80     ++valueVec[valueVec.size() / 2];
81     ++valueVec[valueVec.size() / 3];
82     ++valueVec[valueVec.size() / 4];
83     ++valueVec[2 * valueVec.size() / 3];
84     ++valueVec[3 * valueVec.size() / 4];
85 
86     gApplicationCache[keyVec] = valueVec;
87 
88     gLastCacheOpResult = CacheOpResult::SetSuccess;
89 }
90 
GetBlob(const void * key,EGLsizeiANDROID keySize,void * value,EGLsizeiANDROID valueSize)91 EGLsizeiANDROID GetBlob(const void *key,
92                         EGLsizeiANDROID keySize,
93                         void *value,
94                         EGLsizeiANDROID valueSize)
95 {
96     std::vector<uint8_t> keyVec(keySize);
97     memcpy(keyVec.data(), key, keySize);
98 
99     auto entry = gApplicationCache.find(keyVec);
100     if (entry == gApplicationCache.end())
101     {
102         // A compile+link operation can generate multiple queries to the cache; one per shader and
103         // one for link.  For the purposes of the test, make sure that any of these hitting the
104         // cache is considered a success, particularly because it's valid for the pipeline cache
105         // entry not to exist in the cache.
106         if (gLastCacheOpResult != CacheOpResult::GetSuccess)
107         {
108             gLastCacheOpResult = CacheOpResult::GetNotFound;
109         }
110         return 0;
111     }
112 
113     if (entry->second.size() <= static_cast<size_t>(valueSize))
114     {
115         memcpy(value, entry->second.data(), entry->second.size());
116         gLastCacheOpResult = CacheOpResult::GetSuccess;
117     }
118     else
119     {
120         gLastCacheOpResult = CacheOpResult::GetMemoryTooSmall;
121     }
122 
123     return entry->second.size();
124 }
125 
WaitProgramBinaryReady(GLuint program)126 void WaitProgramBinaryReady(GLuint program)
127 {
128     // Using GL_ANGLE_program_binary_readiness_query, wait for post-link tasks to finish.
129     // Otherwise, the program binary may not yet be cached.  Only needed when a |set| operation is
130     // expected.
131     if (!IsGLExtensionEnabled("GL_ANGLE_program_binary_readiness_query"))
132     {
133         return;
134     }
135 
136     GLint ready = false;
137     while (!ready)
138     {
139         glGetProgramiv(program, GL_PROGRAM_BINARY_READY_ANGLE, &ready);
140         angle::Sleep(0);
141     }
142 }
143 }  // anonymous namespace
144 
145 class EGLBlobCacheTest : public ANGLETest<>
146 {
147   protected:
EGLBlobCacheTest()148     EGLBlobCacheTest() : mHasBlobCache(false)
149     {
150         // Force disply caching off. Blob cache functions require it.
151         forceNewDisplay();
152     }
153 
testSetUp()154     void testSetUp() override
155     {
156         EGLDisplay display = getEGLWindow()->getDisplay();
157         mHasBlobCache      = IsEGLDisplayExtensionEnabled(display, kEGLExtName);
158     }
159 
testTearDown()160     void testTearDown() override { gApplicationCache.clear(); }
161 
programBinaryAvailable()162     bool programBinaryAvailable() { return IsGLExtensionEnabled("GL_OES_get_program_binary"); }
163 
164     bool mHasBlobCache;
165 };
166 
167 // Makes sure the extension exists and works
TEST_P(EGLBlobCacheTest,Functional)168 TEST_P(EGLBlobCacheTest, Functional)
169 {
170     ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::CacheCompiledShader));
171     ANGLE_SKIP_TEST_IF(getEGLWindow()->isFeatureEnabled(Feature::DisableProgramCaching));
172 
173     EGLDisplay display = getEGLWindow()->getDisplay();
174 
175     EXPECT_TRUE(mHasBlobCache);
176     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
177     ASSERT_EGL_SUCCESS();
178 
179     constexpr char kVertexShaderSrc[] = R"(attribute vec4 aTest;
180 attribute vec2 aPosition;
181 varying vec4 vTest;
182 void main()
183 {
184     vTest        = aTest;
185     gl_Position  = vec4(aPosition, 0.0, 1.0);
186     gl_PointSize = 1.0;
187 })";
188 
189     constexpr char kFragmentShaderSrc[] = R"(precision mediump float;
190 varying vec4 vTest;
191 void main()
192 {
193     gl_FragColor = vTest;
194 })";
195 
196     constexpr char kVertexShaderSrc2[] = R"(attribute vec4 aTest;
197 attribute vec2 aPosition;
198 varying vec4 vTest;
199 void main()
200 {
201     vTest        = aTest;
202     gl_Position  = vec4(aPosition, 1.0, 1.0);
203     gl_PointSize = 1.0;
204 })";
205 
206     constexpr char kFragmentShaderSrc2[] = R"(precision mediump float;
207 varying vec4 vTest;
208 void main()
209 {
210     gl_FragColor = vTest - vec4(0.0, 1.0, 0.0, 0.0);
211 })";
212 
213     // Compile a shader so it puts something in the cache.  Note that with Vulkan, some optional
214     // link subtasks may run beyond link, and so the caching is delayed.  An explicit wait on these
215     // tasks is done for this reason.
216     if (programBinaryAvailable())
217     {
218         ANGLE_GL_PROGRAM(program, kVertexShaderSrc, kFragmentShaderSrc);
219         WaitProgramBinaryReady(program);
220         EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
221         gLastCacheOpResult = CacheOpResult::ValueNotSet;
222 
223         // Compile the same shader again, so it would try to retrieve it from the cache
224         program.makeRaster(kVertexShaderSrc, kFragmentShaderSrc);
225         ASSERT_TRUE(program.valid());
226         EXPECT_EQ(CacheOpResult::GetSuccess, gLastCacheOpResult);
227         gLastCacheOpResult = CacheOpResult::ValueNotSet;
228 
229         // Compile another shader, which should create a new entry
230         program.makeRaster(kVertexShaderSrc2, kFragmentShaderSrc2);
231         ASSERT_TRUE(program.valid());
232         WaitProgramBinaryReady(program);
233         EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
234         gLastCacheOpResult = CacheOpResult::ValueNotSet;
235 
236         // Compile the first shader again, which should still reside in the cache
237         program.makeRaster(kVertexShaderSrc, kFragmentShaderSrc);
238         ASSERT_TRUE(program.valid());
239         EXPECT_EQ(CacheOpResult::GetSuccess, gLastCacheOpResult);
240         gLastCacheOpResult = CacheOpResult::ValueNotSet;
241 
242         // Make sure deleting the program doesn't result in a binary save.  Regression test for a
243         // bug where the binary was re-cached after being loaded.
244         glUseProgram(0);
245         program.reset();
246 
247         EXPECT_EQ(CacheOpResult::ValueNotSet, gLastCacheOpResult);
248     }
249 }
250 
251 // Makes sure the caching is always done without an explicit wait for post-link events (if any)
TEST_P(EGLBlobCacheTest,FunctionalWithoutWait)252 TEST_P(EGLBlobCacheTest, FunctionalWithoutWait)
253 {
254     ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::CacheCompiledShader));
255     ANGLE_SKIP_TEST_IF(getEGLWindow()->isFeatureEnabled(Feature::DisableProgramCaching));
256 
257     EGLDisplay display = getEGLWindow()->getDisplay();
258 
259     EXPECT_TRUE(mHasBlobCache);
260     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
261     ASSERT_EGL_SUCCESS();
262 
263     constexpr char kVertexShaderSrc[] = R"(attribute vec4 aTest;
264 attribute vec2 aPosition;
265 varying vec4 vTest;
266 varying vec4 vTest2;
267 void main()
268 {
269     vTest        = aTest;
270     vTest2       = aTest;
271     gl_Position  = vec4(aPosition, 1.0, 1.0);
272     gl_PointSize = 1.0;
273 })";
274 
275     constexpr char kFragmentShaderSrc[] = R"(precision mediump float;
276 varying vec4 vTest;
277 varying vec4 vTest2;
278 void main()
279 {
280     gl_FragColor = vTest + vTest2 - vec4(0.0, 1.0, 0.0, 0.0);
281 })";
282 
283     if (programBinaryAvailable())
284     {
285         // Make the conditions ideal for Vulkan's warm up task to match the draw call.
286         constexpr uint32_t kSize = 1;
287         GLTexture color;
288         glBindTexture(GL_TEXTURE_2D, color);
289         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
290                      nullptr);
291 
292         GLFramebuffer fbo;
293         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
294         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
295         ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
296 
297         ANGLE_GL_PROGRAM(program, kVertexShaderSrc, kFragmentShaderSrc);
298 
299         // First, draw with the program.  In the Vulkan backend, this can lead to a wait on the warm
300         // up task since the description matches the one needed for the draw.
301         glUseProgram(program);
302         glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
303 
304         // Delete the program to make sure caching the binary can no longer be delayed.
305         glUseProgram(0);
306         program.reset();
307 
308         EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
309         gLastCacheOpResult = CacheOpResult::ValueNotSet;
310     }
311 }
312 
313 // Tests error conditions of the APIs.
TEST_P(EGLBlobCacheTest,NegativeAPI)314 TEST_P(EGLBlobCacheTest, NegativeAPI)
315 {
316     ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::CacheCompiledShader));
317     ANGLE_SKIP_TEST_IF(getEGLWindow()->isFeatureEnabled(Feature::DisableProgramCaching));
318 
319     EXPECT_TRUE(mHasBlobCache);
320 
321     // Test bad display
322     eglSetBlobCacheFuncsANDROID(EGL_NO_DISPLAY, nullptr, nullptr);
323     EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
324 
325     eglSetBlobCacheFuncsANDROID(EGL_NO_DISPLAY, SetBlob, GetBlob);
326     EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
327 
328     EGLDisplay display = getEGLWindow()->getDisplay();
329 
330     // Test bad arguments
331     eglSetBlobCacheFuncsANDROID(display, nullptr, nullptr);
332     EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
333 
334     eglSetBlobCacheFuncsANDROID(display, SetBlob, nullptr);
335     EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
336 
337     eglSetBlobCacheFuncsANDROID(display, nullptr, GetBlob);
338     EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
339 
340     // Set the arguments once and test setting them again (which should fail)
341     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
342     ASSERT_EGL_SUCCESS();
343 
344     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
345     EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
346 
347     // Try again with bad parameters
348     eglSetBlobCacheFuncsANDROID(EGL_NO_DISPLAY, nullptr, nullptr);
349     EXPECT_EGL_ERROR(EGL_BAD_DISPLAY);
350 
351     eglSetBlobCacheFuncsANDROID(display, nullptr, nullptr);
352     EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
353 
354     eglSetBlobCacheFuncsANDROID(display, SetBlob, nullptr);
355     EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
356 
357     eglSetBlobCacheFuncsANDROID(display, nullptr, GetBlob);
358     EXPECT_EGL_ERROR(EGL_BAD_PARAMETER);
359 }
360 
361 // Regression test for including the fragment output locatins in the program key.
362 // http://anglebug.com/42263144
TEST_P(EGLBlobCacheTest,FragmentOutputLocationKey)363 TEST_P(EGLBlobCacheTest, FragmentOutputLocationKey)
364 {
365     ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::CacheCompiledShader));
366     ANGLE_SKIP_TEST_IF(getEGLWindow()->isFeatureEnabled(Feature::DisableProgramCaching));
367 
368     ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_blend_func_extended") ||
369                        getClientMajorVersion() < 3);
370 
371     EGLDisplay display = getEGLWindow()->getDisplay();
372 
373     EXPECT_TRUE(mHasBlobCache);
374     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
375     ASSERT_EGL_SUCCESS();
376 
377     // Compile a shader so it puts something in the cache
378     if (programBinaryAvailable())
379     {
380         glEnable(GL_SCISSOR_TEST);
381         glScissor(0, 0, 1, 1);
382 
383         constexpr char kFragmentShaderSrc[] = R"(#version 300 es
384 #extension GL_EXT_blend_func_extended : require
385 precision mediump float;
386 uniform vec4 src;
387 uniform vec4 src1;
388 out vec4 FragData;
389 out vec4 SecondaryFragData;
390 void main() {
391     FragData = src;
392     SecondaryFragData = src1;
393 })";
394 
395         constexpr char kVertexShaderSrc[] = R"(#version 300 es
396 in vec4 position;
397 void main() {
398     gl_Position = position;
399 })";
400 
401         GLuint program = CompileProgram(kVertexShaderSrc, kFragmentShaderSrc, [](GLuint p) {
402             glBindFragDataLocationEXT(p, 0, "FragData[0]");
403             glBindFragDataLocationIndexedEXT(p, 0, 1, "SecondaryFragData[0]");
404         });
405         ASSERT_NE(0u, program);
406         glUseProgram(program);
407         glDrawArrays(GL_TRIANGLES, 0, 3);
408         WaitProgramBinaryReady(program);
409         EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
410         gLastCacheOpResult = CacheOpResult::ValueNotSet;
411 
412         // Re-link the program with different fragment output bindings
413         program = CompileProgram(kVertexShaderSrc, kFragmentShaderSrc, [](GLuint p) {
414             glBindFragDataLocationEXT(p, 0, "FragData");
415             glBindFragDataLocationIndexedEXT(p, 0, 1, "SecondaryFragData");
416         });
417         ASSERT_NE(0u, program);
418         glUseProgram(program);
419         glDrawArrays(GL_TRIANGLES, 0, 3);
420         WaitProgramBinaryReady(program);
421         EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
422         gLastCacheOpResult = CacheOpResult::ValueNotSet;
423     }
424 }
425 
426 // Checks that the shader cache, which is used when this extension is available, is working
427 // properly.
TEST_P(EGLBlobCacheTest,ShaderCacheFunctional)428 TEST_P(EGLBlobCacheTest, ShaderCacheFunctional)
429 {
430     ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::CacheCompiledShader));
431     ANGLE_SKIP_TEST_IF(getEGLWindow()->isFeatureEnabled(Feature::DisableProgramCaching));
432 
433     ANGLE_SKIP_TEST_IF(!IsVulkan());
434 
435     EGLDisplay display = getEGLWindow()->getDisplay();
436 
437     EXPECT_TRUE(mHasBlobCache);
438     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
439     ASSERT_EGL_SUCCESS();
440 
441     constexpr char kVertexShaderSrc[] = R"(attribute vec4 aTest;
442 attribute vec2 aPosition;
443 varying vec4 vTest;
444 void main()
445 {
446     vTest        = aTest;
447     gl_Position  = vec4(aPosition, 0.0, 1.0);
448     gl_PointSize = 1.0;
449 })";
450 
451     constexpr char kFragmentShaderSrc[] = R"(precision mediump float;
452 varying vec4 vTest;
453 void main()
454 {
455     gl_FragColor = vTest;
456 })";
457 
458     // Compile a shader so it puts something in the cache
459     GLuint shaderID = CompileShader(GL_VERTEX_SHADER, kVertexShaderSrc);
460     ASSERT_TRUE(shaderID != 0);
461     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
462     gLastCacheOpResult = CacheOpResult::ValueNotSet;
463     glDeleteShader(shaderID);
464 
465     // Compile the same shader again, so it would try to retrieve it from the cache
466     shaderID = CompileShader(GL_VERTEX_SHADER, kVertexShaderSrc);
467     ASSERT_TRUE(shaderID != 0);
468     EXPECT_EQ(CacheOpResult::GetSuccess, gLastCacheOpResult);
469     gLastCacheOpResult = CacheOpResult::ValueNotSet;
470     glDeleteShader(shaderID);
471 
472     // Compile another shader, which should create a new entry
473     shaderID = CompileShader(GL_FRAGMENT_SHADER, kFragmentShaderSrc);
474     ASSERT_TRUE(shaderID != 0);
475     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
476     gLastCacheOpResult = CacheOpResult::ValueNotSet;
477     glDeleteShader(shaderID);
478 
479     // Compile the first shader again, which should still reside in the cache
480     shaderID = CompileShader(GL_VERTEX_SHADER, kVertexShaderSrc);
481     ASSERT_TRUE(shaderID != 0);
482     EXPECT_EQ(CacheOpResult::GetSuccess, gLastCacheOpResult);
483     gLastCacheOpResult = CacheOpResult::ValueNotSet;
484     glDeleteShader(shaderID);
485 }
486 
487 // Tests compiling a program in multiple threads, then fetching the compiled program/shaders from
488 // the cache. We then perform a draw call and test the result to ensure nothing was corrupted.
TEST_P(EGLBlobCacheTest,ThreadSafety)489 TEST_P(EGLBlobCacheTest, ThreadSafety)
490 {
491     ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::CacheCompiledShader));
492     ANGLE_SKIP_TEST_IF(getEGLWindow()->isFeatureEnabled(Feature::DisableProgramCaching));
493 
494     ANGLE_SKIP_TEST_IF(!IsVulkan());
495 
496     EGLDisplay display = getEGLWindow()->getDisplay();
497 
498     EXPECT_TRUE(mHasBlobCache);
499     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
500     ASSERT_EGL_SUCCESS();
501 
502     auto threadFunc = [&](int threadID, EGLDisplay dpy, EGLSurface surface, EGLContext context) {
503         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
504 
505         ANGLE_GL_PROGRAM(unusedProgramTemp1, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
506 
507         // Insert a new entry into the cache unique to this thread.
508         std::stringstream ss;
509         ss << essl1_shaders::vs::Simple() << "//" << threadID;
510         std::string newEntryVSSource = ss.str().c_str();
511         ANGLE_GL_PROGRAM(unusedProgramTemp2, newEntryVSSource.c_str(), essl1_shaders::fs::Red());
512 
513         // Clean up
514         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
515     };
516 
517     constexpr int kNumThreads = 32;
518 
519     std::vector<LockStepThreadFunc> threadFuncs(kNumThreads);
520     for (int i = 0; i < kNumThreads; ++i)
521     {
522         threadFuncs[i] = [=](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
523             return threadFunc(i, dpy, surface, context);
524         };
525     }
526 
527     gLastCacheOpResult = CacheOpResult::ValueNotSet;
528 
529     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
530 
531     EXPECT_GL_NO_ERROR();
532 
533     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
534     EXPECT_EQ(CacheOpResult::GetSuccess, gLastCacheOpResult);
535 
536     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
537 
538     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
539 }
540 
541 // Makes sure ANGLE recovers from corrupted cache.
TEST_P(EGLBlobCacheTest,CacheCorruption)542 TEST_P(EGLBlobCacheTest, CacheCorruption)
543 {
544     ANGLE_SKIP_TEST_IF(!getEGLWindow()->isFeatureEnabled(Feature::CacheCompiledShader));
545     ANGLE_SKIP_TEST_IF(getEGLWindow()->isFeatureEnabled(Feature::DisableProgramCaching));
546 
547     EGLDisplay display = getEGLWindow()->getDisplay();
548 
549     EXPECT_TRUE(mHasBlobCache);
550     eglSetBlobCacheFuncsANDROID(display, SetCorruptedBlob, GetBlob);
551     ASSERT_EGL_SUCCESS();
552 
553     ANGLE_SKIP_TEST_IF(!programBinaryAvailable());
554 
555     // Compile the program once and draw with it
556     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
557     glUseProgram(program);
558 
559     const GLint colorUniformLocation =
560         glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
561     ASSERT_NE(colorUniformLocation, -1);
562 
563     glUniform4f(colorUniformLocation, 1, 0, 0, 1);
564     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
565     EXPECT_GL_NO_ERROR();
566     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
567 
568     WaitProgramBinaryReady(program);
569     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
570     gLastCacheOpResult = CacheOpResult::ValueNotSet;
571 
572     // Compile/link the same program again, so it would try to retrieve it from the cache.  GetBlob
573     // should return success, but because the cache is corrupt, ANGLE should redo the compile/link
574     // and set the blob again.
575     program.makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
576     ASSERT_TRUE(program.valid());
577     glUseProgram(program);
578 
579     glUniform4f(colorUniformLocation, 0, 1, 0, 1);
580     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
581     EXPECT_GL_NO_ERROR();
582     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
583 
584     WaitProgramBinaryReady(program);
585     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
586 }
587 
588 class EGLBlobCacheInternalRejectionTest : public EGLBlobCacheTest
589 {};
590 
591 // Makes sure ANGLE recovers from internal (backend) rejection of the program blob, while everything
592 // seems fine to ANGLE.
TEST_P(EGLBlobCacheInternalRejectionTest,Functional)593 TEST_P(EGLBlobCacheInternalRejectionTest, Functional)
594 {
595     EGLDisplay display = getEGLWindow()->getDisplay();
596 
597     EXPECT_TRUE(mHasBlobCache);
598     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
599     ASSERT_EGL_SUCCESS();
600 
601     ANGLE_SKIP_TEST_IF(!programBinaryAvailable());
602 
603     // Compile the program once and draw with it
604     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
605     glUseProgram(program);
606 
607     const GLint colorUniformLocation =
608         glGetUniformLocation(program, angle::essl1_shaders::ColorUniform());
609     ASSERT_NE(colorUniformLocation, -1);
610 
611     glUniform4f(colorUniformLocation, 1, 0, 0, 1);
612     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
613     EXPECT_GL_NO_ERROR();
614     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
615 
616     WaitProgramBinaryReady(program);
617     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
618     gLastCacheOpResult = CacheOpResult::ValueNotSet;
619 
620     // Compile/link the same program again, so it would try to retrieve it from the cache.  GetBlob
621     // should return success, and ANGLE would think the program is fine.  After ANGLE internal
622     // updates, the backend should reject the program binary, at which point ANGLE should redo the
623     // compile/link and set the blob again.
624     program.makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
625     ASSERT_TRUE(program.valid());
626     glUseProgram(program);
627 
628     glUniform4f(colorUniformLocation, 0, 1, 0, 1);
629     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
630     EXPECT_GL_NO_ERROR();
631     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
632 
633     WaitProgramBinaryReady(program);
634     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
635 }
636 
637 // Makes sure ANGLE recovers from internal (backend) rejection of the shader blob, while everything
638 // seems fine to ANGLE.
TEST_P(EGLBlobCacheInternalRejectionTest,ShaderCacheFunctional)639 TEST_P(EGLBlobCacheInternalRejectionTest, ShaderCacheFunctional)
640 {
641     ANGLE_SKIP_TEST_IF(!IsVulkan());
642 
643     EGLDisplay display = getEGLWindow()->getDisplay();
644 
645     EXPECT_TRUE(mHasBlobCache);
646     eglSetBlobCacheFuncsANDROID(display, SetBlob, GetBlob);
647     ASSERT_EGL_SUCCESS();
648 
649     // Compile a shader so it puts something in the cache
650     GLuint shaderID = CompileShader(GL_VERTEX_SHADER, essl1_shaders::vs::Simple());
651     ASSERT_TRUE(shaderID != 0);
652     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
653     gLastCacheOpResult = CacheOpResult::ValueNotSet;
654     glDeleteShader(shaderID);
655 
656     // Compile another shader, which should create a new entry
657     shaderID = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::UniformColor());
658     ASSERT_TRUE(shaderID != 0);
659     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
660     gLastCacheOpResult = CacheOpResult::ValueNotSet;
661     glDeleteShader(shaderID);
662 
663     // Compile the first shader again, which should still reside in the cache, but is corrupted.
664     // The cached entry should be discarded and compilation performed again (which sets another
665     // entry in the cache).
666     shaderID = CompileShader(GL_VERTEX_SHADER, essl1_shaders::vs::Simple());
667     ASSERT_TRUE(shaderID != 0);
668     EXPECT_EQ(CacheOpResult::SetSuccess, gLastCacheOpResult);
669     gLastCacheOpResult = CacheOpResult::ValueNotSet;
670     glDeleteShader(shaderID);
671 }
672 
673 ANGLE_INSTANTIATE_TEST(EGLBlobCacheTest,
674                        ES2_D3D9(),
675                        ES2_D3D11(),
676                        ES3_D3D11(),
677                        ES2_OPENGL(),
678                        ES3_OPENGL(),
679                        ES3_OPENGLES(),
680                        ES2_OPENGLES(),
681                        ES2_METAL(),
682                        ES3_METAL(),
683                        // Note: For the Vulkan backend, disable reads and writes for the global
684                        // pipeline cache, so it does not interfere with the test's expectations of
685                        // when the cache should and shouldn't be hit.
686                        ES2_VULKAN()
687                            .enable(Feature::DisablePipelineCacheLoadForTesting)
688                            .disable(Feature::SyncMonolithicPipelinesToBlobCache),
689                        ES3_VULKAN_SWIFTSHADER()
690                            .enable(Feature::DisablePipelineCacheLoadForTesting)
691                            .disable(Feature::SyncMonolithicPipelinesToBlobCache),
692                        ES3_VULKAN()
693                            .enable(Feature::AsyncCommandQueue)
694                            .enable(Feature::DisablePipelineCacheLoadForTesting)
695                            .disable(Feature::SyncMonolithicPipelinesToBlobCache),
696                        ES2_VULKAN_SWIFTSHADER()
697                            .enable(Feature::AsyncCommandQueue)
698                            .enable(Feature::DisablePipelineCacheLoadForTesting)
699                            .disable(Feature::SyncMonolithicPipelinesToBlobCache),
700                        ES2_VULKAN_SWIFTSHADER()
701                            .enable(Feature::EnableParallelCompileAndLink)
702                            .enable(Feature::DisablePipelineCacheLoadForTesting)
703                            .disable(Feature::SyncMonolithicPipelinesToBlobCache),
704                        ES3_VULKAN()
705                            .enable(Feature::EnableParallelCompileAndLink)
706                            .enable(Feature::DisablePipelineCacheLoadForTesting)
707                            .disable(Feature::SyncMonolithicPipelinesToBlobCache),
708                        ES2_VULKAN_SWIFTSHADER()
709                            .enable(Feature::EnableParallelCompileAndLink)
710                            .enable(Feature::AsyncCommandQueue)
711                            .enable(Feature::DisablePipelineCacheLoadForTesting)
712                            .disable(Feature::SyncMonolithicPipelinesToBlobCache));
713 
714 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLBlobCacheInternalRejectionTest);
715 ANGLE_INSTANTIATE_TEST(EGLBlobCacheInternalRejectionTest,
716                        ES2_OPENGL().enable(Feature::CorruptProgramBinaryForTesting),
717                        ES2_OPENGLES().enable(Feature::CorruptProgramBinaryForTesting));
718