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