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