1 //
2 // Copyright 2015 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
7 #include "test_utils/ANGLETest.h"
8
9 #include <stdint.h>
10 #include <memory>
11
12 #include "common/string_utils.h"
13 #include "test_utils/angle_test_configs.h"
14 #include "test_utils/gl_raii.h"
15 #include "util/EGLWindow.h"
16 #include "util/OSWindow.h"
17
18 using namespace angle;
19
20 class ProgramBinaryTest : public ANGLETest<>
21 {
22 protected:
ProgramBinaryTest()23 ProgramBinaryTest()
24 {
25 setWindowWidth(128);
26 setWindowHeight(128);
27 setConfigRedBits(8);
28 setConfigGreenBits(8);
29 setConfigBlueBits(8);
30 setConfigAlphaBits(8);
31
32 // Test flakiness was noticed when reusing displays.
33 forceNewDisplay();
34 }
35
testSetUp()36 void testSetUp() override
37 {
38 mProgram = CompileProgram(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
39 if (mProgram == 0)
40 {
41 FAIL() << "shader compilation failed.";
42 }
43
44 glGenBuffers(1, &mBuffer);
45 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
46 glBufferData(GL_ARRAY_BUFFER, 128, nullptr, GL_STATIC_DRAW);
47 glBindBuffer(GL_ARRAY_BUFFER, 0);
48
49 ASSERT_GL_NO_ERROR();
50 }
51
testTearDown()52 void testTearDown() override
53 {
54 glDeleteProgram(mProgram);
55 glDeleteBuffers(1, &mBuffer);
56 }
57
getAvailableProgramBinaryFormatCount() const58 GLint getAvailableProgramBinaryFormatCount() const
59 {
60 GLint formatCount;
61 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &formatCount);
62 return formatCount;
63 }
64
supported() const65 bool supported() const
66 {
67 if (!IsGLExtensionEnabled("GL_OES_get_program_binary"))
68 {
69 std::cout << "Test skipped because GL_OES_get_program_binary is not available."
70 << std::endl;
71 return false;
72 }
73
74 if (getAvailableProgramBinaryFormatCount() == 0)
75 {
76 std::cout << "Test skipped because no program binary formats are available."
77 << std::endl;
78 return false;
79 }
80
81 return true;
82 }
83
saveAndLoadProgram(GLuint programToSave,GLuint loadedProgram)84 void saveAndLoadProgram(GLuint programToSave, GLuint loadedProgram)
85 {
86 GLint programLength = 0;
87 GLint writtenLength = 0;
88 GLenum binaryFormat = 0;
89
90 glGetProgramiv(programToSave, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
91 EXPECT_GL_NO_ERROR();
92
93 std::vector<uint8_t> binary(programLength);
94 glGetProgramBinaryOES(programToSave, programLength, &writtenLength, &binaryFormat,
95 binary.data());
96 EXPECT_GL_NO_ERROR();
97
98 // The lengths reported by glGetProgramiv and glGetProgramBinaryOES should match
99 EXPECT_EQ(programLength, writtenLength);
100
101 if (writtenLength)
102 {
103 glProgramBinaryOES(loadedProgram, binaryFormat, binary.data(), writtenLength);
104
105 EXPECT_GL_NO_ERROR();
106
107 GLint linkStatus;
108 glGetProgramiv(loadedProgram, GL_LINK_STATUS, &linkStatus);
109 if (linkStatus == 0)
110 {
111 GLint infoLogLength;
112 glGetProgramiv(loadedProgram, GL_INFO_LOG_LENGTH, &infoLogLength);
113
114 if (infoLogLength > 0)
115 {
116 std::vector<GLchar> infoLog(infoLogLength);
117 glGetProgramInfoLog(loadedProgram, static_cast<GLsizei>(infoLog.size()),
118 nullptr, &infoLog[0]);
119 FAIL() << "program link failed: " << &infoLog[0];
120 }
121 else
122 {
123 FAIL() << "program link failed.";
124 }
125 }
126 else
127 {
128 glUseProgram(loadedProgram);
129 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
130
131 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, nullptr);
132 glEnableVertexAttribArray(0);
133 glDrawArrays(GL_POINTS, 0, 1);
134
135 EXPECT_GL_NO_ERROR();
136 }
137 }
138 }
139
140 GLuint mProgram;
141 GLuint mBuffer;
142 };
143
144 // This tests the assumption that float attribs of different size
145 // should not internally cause a vertex shader recompile (for conversion).
TEST_P(ProgramBinaryTest,FloatDynamicShaderSize)146 TEST_P(ProgramBinaryTest, FloatDynamicShaderSize)
147 {
148 if (!supported())
149 {
150 return;
151 }
152
153 glUseProgram(mProgram);
154 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
155
156 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, nullptr);
157 glEnableVertexAttribArray(0);
158 glDrawArrays(GL_POINTS, 0, 1);
159
160 GLint programLength;
161 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
162
163 EXPECT_GL_NO_ERROR();
164
165 for (GLsizei size = 1; size <= 3; size++)
166 {
167 glVertexAttribPointer(0, size, GL_FLOAT, GL_FALSE, 8, nullptr);
168 glEnableVertexAttribArray(0);
169 glDrawArrays(GL_POINTS, 0, 1);
170
171 GLint newProgramLength;
172 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &newProgramLength);
173 EXPECT_GL_NO_ERROR();
174 EXPECT_EQ(programLength, newProgramLength);
175 }
176 }
177
178 // Tests that switching between signed and unsigned un-normalized data doesn't trigger a bug
179 // in the D3D11 back-end.
TEST_P(ProgramBinaryTest,DynamicShadersSignatureBug)180 TEST_P(ProgramBinaryTest, DynamicShadersSignatureBug)
181 {
182 glUseProgram(mProgram);
183 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
184
185 GLint attribLocation = glGetAttribLocation(mProgram, essl1_shaders::PositionAttrib());
186 ASSERT_NE(-1, attribLocation);
187 glEnableVertexAttribArray(attribLocation);
188
189 glVertexAttribPointer(attribLocation, 2, GL_BYTE, GL_FALSE, 0, nullptr);
190 glDrawArrays(GL_POINTS, 0, 1);
191
192 glVertexAttribPointer(attribLocation, 2, GL_UNSIGNED_BYTE, GL_FALSE, 0, nullptr);
193 glDrawArrays(GL_POINTS, 0, 1);
194 }
195
196 // This tests the ability to successfully save and load a program binary.
TEST_P(ProgramBinaryTest,SaveAndLoadBinary)197 TEST_P(ProgramBinaryTest, SaveAndLoadBinary)
198 {
199 if (!supported())
200 {
201 return;
202 }
203
204 GLuint programToLoad = glCreateProgram();
205
206 saveAndLoadProgram(mProgram, programToLoad);
207
208 glDeleteProgram(programToLoad);
209 EXPECT_GL_NO_ERROR();
210 }
211
212 // This tests the ability to successfully save and load a program binary and then
213 // save and load from the same program that was loaded.
TEST_P(ProgramBinaryTest,SaveAndLoadBinaryTwice)214 TEST_P(ProgramBinaryTest, SaveAndLoadBinaryTwice)
215 {
216 if (!supported())
217 {
218 return;
219 }
220
221 GLuint programToLoad = glCreateProgram();
222 GLuint programToLoad2 = glCreateProgram();
223
224 saveAndLoadProgram(mProgram, programToLoad);
225 saveAndLoadProgram(programToLoad, programToLoad2);
226
227 glDeleteProgram(programToLoad);
228 glDeleteProgram(programToLoad2);
229 EXPECT_GL_NO_ERROR();
230 }
231
232 // Ensures that we init the compiler before calling ProgramBinary.
TEST_P(ProgramBinaryTest,CallProgramBinaryBeforeLink)233 TEST_P(ProgramBinaryTest, CallProgramBinaryBeforeLink)
234 {
235 if (!supported())
236 {
237 return;
238 }
239
240 // Initialize a simple program.
241 glUseProgram(mProgram);
242
243 GLsizei length = 0;
244 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH, &length);
245 ASSERT_GL_NO_ERROR();
246 ASSERT_GT(length, 0);
247
248 GLsizei readLength = 0;
249 GLenum binaryFormat = GL_NONE;
250 std::vector<uint8_t> binaryBlob(length);
251 glGetProgramBinaryOES(mProgram, length, &readLength, &binaryFormat, binaryBlob.data());
252 ASSERT_GL_NO_ERROR();
253
254 // Shutdown and restart GL entirely.
255 recreateTestFixture();
256
257 ANGLE_GL_BINARY_OES_PROGRAM(binaryProgram, binaryBlob, binaryFormat);
258 ASSERT_GL_NO_ERROR();
259
260 drawQuad(binaryProgram, essl1_shaders::PositionAttrib(), 0.5f);
261 ASSERT_GL_NO_ERROR();
262 }
263
264 // Test that unlinked programs have a binary size of 0
TEST_P(ProgramBinaryTest,ZeroSizedUnlinkedBinary)265 TEST_P(ProgramBinaryTest, ZeroSizedUnlinkedBinary)
266 {
267 ANGLE_SKIP_TEST_IF(!supported());
268
269 ANGLE_GL_EMPTY_PROGRAM(program);
270 GLsizei length = 0;
271 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &length);
272 ASSERT_EQ(0, length);
273 }
274
275 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
276 // tests should be run against.
277 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(
278 ProgramBinaryTest,
279 ES3_VULKAN().disable(Feature::EnablePipelineCacheDataCompression));
280
281 class ProgramBinaryES3Test : public ProgramBinaryTest
282 {
283 protected:
ProgramBinaryES3Test()284 ProgramBinaryES3Test()
285 {
286 // Test flakiness was noticed when reusing displays.
287 forceNewDisplay();
288 }
289
290 void testBinaryAndUBOBlockIndexes(bool drawWithProgramFirst);
291 };
292
testBinaryAndUBOBlockIndexes(bool drawWithProgramFirst)293 void ProgramBinaryES3Test::testBinaryAndUBOBlockIndexes(bool drawWithProgramFirst)
294 {
295 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
296
297 constexpr char kVS[] =
298 "#version 300 es\n"
299 "uniform block {\n"
300 " float f;\n"
301 "};\n"
302 "in vec4 position;\n"
303 "out vec4 color;\n"
304 "void main() {\n"
305 " gl_Position = position;\n"
306 " color = vec4(f, f, f, 1);\n"
307 "}";
308
309 constexpr char kFS[] =
310 "#version 300 es\n"
311 "precision mediump float;\n"
312 "in vec4 color;\n"
313 "out vec4 colorOut;\n"
314 "void main() {\n"
315 " colorOut = color;\n"
316 "}";
317
318 // Init and draw with the program.
319 ANGLE_GL_PROGRAM(program, kVS, kFS);
320
321 float fData[4] = {1.0f, 1.0f, 1.0f, 1.0f};
322 GLuint bindIndex = 2;
323
324 GLBuffer ubo;
325 glBindBuffer(GL_UNIFORM_BUFFER, ubo.get());
326 glBufferData(GL_UNIFORM_BUFFER, sizeof(fData), &fData, GL_STATIC_DRAW);
327 glBindBufferRange(GL_UNIFORM_BUFFER, bindIndex, ubo.get(), 0, sizeof(fData));
328
329 GLint blockIndex = glGetUniformBlockIndex(program.get(), "block");
330 ASSERT_NE(-1, blockIndex);
331
332 glUniformBlockBinding(program.get(), blockIndex, bindIndex);
333
334 glClearColor(1.0, 0.0, 0.0, 1.0);
335 glClear(GL_COLOR_BUFFER_BIT);
336 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
337
338 if (drawWithProgramFirst)
339 {
340 drawQuad(program.get(), "position", 0.5f);
341 ASSERT_GL_NO_ERROR();
342 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
343 }
344
345 // Read back the binary.
346 GLint programLength = 0;
347 glGetProgramiv(program.get(), GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
348 ASSERT_GL_NO_ERROR();
349
350 GLsizei readLength = 0;
351 GLenum binaryFormat = GL_NONE;
352 std::vector<uint8_t> binary(programLength);
353 glGetProgramBinary(program.get(), programLength, &readLength, &binaryFormat, binary.data());
354 ASSERT_GL_NO_ERROR();
355
356 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
357
358 // Load a new program with the binary and draw.
359 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
360
361 glClearColor(1.0, 0.0, 0.0, 1.0);
362 glClear(GL_COLOR_BUFFER_BIT);
363 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
364
365 drawQuad(binaryProgram.get(), "position", 0.5f);
366 ASSERT_GL_NO_ERROR();
367 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
368 }
369
370 // Tests that saving and loading a program perserves uniform block binding info.
TEST_P(ProgramBinaryES3Test,UniformBlockBindingWithDraw)371 TEST_P(ProgramBinaryES3Test, UniformBlockBindingWithDraw)
372 {
373 testBinaryAndUBOBlockIndexes(true);
374 }
375
376 // Same as above, but does not do an initial draw with the program. Covers an ANGLE crash.
377 // http://anglebug.com/1637
TEST_P(ProgramBinaryES3Test,UniformBlockBindingNoDraw)378 TEST_P(ProgramBinaryES3Test, UniformBlockBindingNoDraw)
379 {
380 testBinaryAndUBOBlockIndexes(false);
381 }
382
383 // Test the shaders with arrays-of-struct uniforms are properly saved and restored
TEST_P(ProgramBinaryES3Test,TestArrayOfStructUniform)384 TEST_P(ProgramBinaryES3Test, TestArrayOfStructUniform)
385 {
386 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
387
388 constexpr char kVS[] =
389 "#version 300 es\n"
390 "in highp vec4 position;\n"
391 "out mediump float v_vtxOut;\n"
392 "\n"
393 "struct structType\n"
394 "{\n"
395 " mediump vec4 m0;\n"
396 " mediump vec4 m1;\n"
397 "};\n"
398 "uniform structType u_var[3];\n"
399 "\n"
400 "mediump float compare_float(mediump float a, mediump float b)\n"
401 "{\n"
402 " return abs(a - b) < 0.05 ? 1.0 : 0.0;\n"
403 "}\n"
404 "mediump float compare_vec4(mediump vec4 a, mediump vec4 b)\n"
405 "{\n"
406 " return compare_float(a.x, b.x)*compare_float(a.y, b.y)*\n"
407 " compare_float(a.z, b.z)*compare_float(a.w, b.w);\n"
408 "}\n"
409 "\n"
410 "void main (void)\n"
411 "{\n"
412 " gl_Position = position;\n"
413 " v_vtxOut = 1.0;\n"
414 " v_vtxOut *= compare_vec4(u_var[0].m0, vec4(0.15, 0.52, 0.26, 0.35));\n"
415 " v_vtxOut *= compare_vec4(u_var[0].m1, vec4(0.88, 0.09, 0.30, 0.61));\n"
416 " v_vtxOut *= compare_vec4(u_var[1].m0, vec4(0.85, 0.59, 0.33, 0.71));\n"
417 " v_vtxOut *= compare_vec4(u_var[1].m1, vec4(0.62, 0.89, 0.09, 0.99));\n"
418 " v_vtxOut *= compare_vec4(u_var[2].m0, vec4(0.53, 0.89, 0.01, 0.08));\n"
419 " v_vtxOut *= compare_vec4(u_var[2].m1, vec4(0.26, 0.72, 0.60, 0.12));\n"
420 "}";
421
422 constexpr char kFS[] =
423 "#version 300 es\n"
424 "in mediump float v_vtxOut;\n"
425 "\n"
426 "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
427 "\n"
428 "void main (void)\n"
429 "{\n"
430 " mediump float result = v_vtxOut;\n"
431 " dEQP_FragColor = vec4(result, result, result, 1.0);\n"
432 "}";
433
434 // Init and draw with the program.
435 ANGLE_GL_PROGRAM(program, kVS, kFS);
436
437 glUseProgram(program.get());
438
439 int location = glGetUniformLocation(program.get(), "u_var[0].m0");
440 ASSERT_NE(location, -1);
441 glUniform4f(location, 0.15, 0.52, 0.26, 0.35);
442 location = glGetUniformLocation(program.get(), "u_var[0].m1");
443 ASSERT_NE(location, -1);
444 glUniform4f(location, 0.88, 0.09, 0.30, 0.61);
445 location = glGetUniformLocation(program.get(), "u_var[1].m0");
446 ASSERT_NE(location, -1);
447 glUniform4f(location, 0.85, 0.59, 0.33, 0.71);
448 location = glGetUniformLocation(program.get(), "u_var[1].m1");
449 ASSERT_NE(location, -1);
450 glUniform4f(location, 0.62, 0.89, 0.09, 0.99);
451 location = glGetUniformLocation(program.get(), "u_var[2].m0");
452 ASSERT_NE(location, -1);
453 glUniform4f(location, 0.53, 0.89, 0.01, 0.08);
454 location = glGetUniformLocation(program.get(), "u_var[2].m1");
455 ASSERT_NE(location, -1);
456 glUniform4f(location, 0.26, 0.72, 0.60, 0.12);
457 ASSERT_GL_NO_ERROR();
458
459 // Clear and draw with the original program:
460 glClearColor(1.0, 0.0, 0.0, 1.0);
461 glClear(GL_COLOR_BUFFER_BIT);
462 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
463 drawQuad(program.get(), "position", 0.5f);
464 ASSERT_GL_NO_ERROR();
465 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
466
467 // Read back the binary.
468 GLint programLength = 0;
469 glGetProgramiv(program.get(), GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
470 ASSERT_GL_NO_ERROR();
471
472 GLsizei readLength = 0;
473 GLenum binaryFormat = GL_NONE;
474 std::vector<uint8_t> binary(programLength);
475 glGetProgramBinary(program.get(), programLength, &readLength, &binaryFormat, binary.data());
476 ASSERT_GL_NO_ERROR();
477
478 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
479
480 // Load a new program with the binary and draw.
481 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
482
483 glUseProgram(binaryProgram.get());
484
485 location = glGetUniformLocation(binaryProgram.get(), "u_var[0].m0");
486 ASSERT_NE(location, -1);
487 glUniform4f(location, 0.15, 0.52, 0.26, 0.35);
488 location = glGetUniformLocation(binaryProgram.get(), "u_var[0].m1");
489 ASSERT_NE(location, -1);
490 glUniform4f(location, 0.88, 0.09, 0.30, 0.61);
491 location = glGetUniformLocation(binaryProgram.get(), "u_var[1].m0");
492 ASSERT_NE(location, -1);
493 glUniform4f(location, 0.85, 0.59, 0.33, 0.71);
494 location = glGetUniformLocation(binaryProgram.get(), "u_var[1].m1");
495 ASSERT_NE(location, -1);
496 glUniform4f(location, 0.62, 0.89, 0.09, 0.99);
497 location = glGetUniformLocation(binaryProgram.get(), "u_var[2].m0");
498 ASSERT_NE(location, -1);
499 glUniform4f(location, 0.53, 0.89, 0.01, 0.08);
500 location = glGetUniformLocation(binaryProgram.get(), "u_var[2].m1");
501 ASSERT_NE(location, -1);
502 glUniform4f(location, 0.26, 0.72, 0.60, 0.12);
503 ASSERT_GL_NO_ERROR();
504
505 // Clear and draw with the restored program:
506 glClearColor(1.0, 0.0, 0.0, 1.0);
507 glClear(GL_COLOR_BUFFER_BIT);
508 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
509 drawQuad(binaryProgram.get(), "position", 0.5f);
510 ASSERT_GL_NO_ERROR();
511 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
512 }
513
514 // Verify that saving/loading binary with detached shaders followed by indexed
515 // drawing works.
TEST_P(ProgramBinaryES3Test,SaveAndLoadDetachedShaders)516 TEST_P(ProgramBinaryES3Test, SaveAndLoadDetachedShaders)
517 {
518 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
519
520 // We use shaders with the "flat" qualifier, to ensure that "flat" behaves
521 // across save/load. This is primarily to catch possible bugs in the Metal
522 // backend, where it needs to detect "flat" at shader link time and
523 // preserve that detection across binary save/load.
524 constexpr char kVS[] = R"(#version 300 es
525 precision highp float;
526
527 in vec4 a_position;
528 flat out vec4 v_color;
529
530 const vec4 colors[4] = vec4[](
531 vec4(0.0f, 0.0f, 1.0f, 1.0f),
532 vec4(0.0f, 0.0f, 1.0f, 1.0f),
533 vec4(0.0f, 1.0f, 0.0f, 1.0f),
534 vec4(0.0f, 1.0f, 0.0f, 1.0f)
535 );
536
537 void main()
538 {
539 v_color = colors[gl_VertexID];
540 gl_Position = a_position;
541 }
542 )";
543
544 constexpr char kFS[] = R"(#version 300 es
545 precision highp float;
546
547 flat in vec4 v_color;
548 out vec4 o_color;
549
550 void main()
551 {
552 o_color = v_color;
553 }
554 )";
555
556 const auto &vertices = GetIndexedQuadVertices();
557
558 GLBuffer vertexBuffer;
559 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
560 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
561 GL_STATIC_DRAW);
562
563 GLBuffer indexBuffer;
564 const auto &indices = GetQuadIndices();
565 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
566 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices[0]) * indices.size(), indices.data(),
567 GL_STATIC_DRAW);
568 ASSERT_GL_NO_ERROR();
569
570 GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, kVS);
571 GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, kFS);
572 ASSERT_NE(0u, vertexShader);
573 ASSERT_NE(0u, fragmentShader);
574
575 GLuint program = glCreateProgram();
576 glAttachShader(program, vertexShader);
577 glAttachShader(program, fragmentShader);
578
579 glLinkProgram(program);
580
581 GLint linkStatus;
582 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
583 EXPECT_EQ(GL_TRUE, linkStatus);
584
585 glDetachShader(program, vertexShader);
586 glDetachShader(program, fragmentShader);
587 glDeleteShader(vertexShader);
588 glDeleteShader(fragmentShader);
589 ASSERT_GL_NO_ERROR();
590
591 GLuint loadedProgram = glCreateProgram();
592 saveAndLoadProgram(program, loadedProgram);
593 glDeleteProgram(program);
594
595 ASSERT_EQ(glIsProgram(loadedProgram), GL_TRUE);
596 glUseProgram(loadedProgram);
597 ASSERT_GL_NO_ERROR();
598
599 GLint posLocation = glGetAttribLocation(loadedProgram, "a_position");
600 ASSERT_NE(-1, posLocation);
601
602 GLVertexArray vertexArray;
603 glBindVertexArray(vertexArray);
604 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
605 glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
606 glEnableVertexAttribArray(posLocation);
607 ASSERT_GL_NO_ERROR();
608
609 glClearColor(1.0, 0.0, 0.0, 1.0);
610 glClear(GL_COLOR_BUFFER_BIT);
611 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
612
613 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
614 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
615 ASSERT_GL_NO_ERROR();
616
617 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
618
619 glDeleteProgram(loadedProgram);
620 ASSERT_GL_NO_ERROR();
621 }
622
623 // Tests the difference between uniform static and active use
TEST_P(ProgramBinaryES3Test,ActiveUniformShader)624 TEST_P(ProgramBinaryES3Test, ActiveUniformShader)
625 {
626 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
627
628 constexpr char kVS[] =
629 "#version 300 es\n"
630 "in vec4 position;\n"
631 "void main() {\n"
632 " gl_Position = position;\n"
633 "}";
634
635 constexpr char kFS[] =
636 "#version 300 es\n"
637 "precision mediump float;\n"
638 "uniform float values[2];\n"
639 "out vec4 color;\n"
640 "bool isZero(float value) {\n"
641 " return value == 0.0f;\n"
642 "}\n"
643 "void main() {\n"
644 " color = isZero(values[1]) ? vec4(1.0f,0.0f,0.0f,1.0f) : vec4(0.0f,1.0f,0.0f,1.0f);\n"
645 "}";
646
647 // Init and draw with the program.
648 ANGLE_GL_PROGRAM(program, kVS, kFS);
649
650 GLint valuesLoc = glGetUniformLocation(program.get(), "values");
651 ASSERT_NE(-1, valuesLoc);
652
653 glUseProgram(program.get());
654 GLfloat values[2] = {0.5f, 1.0f};
655 glUniform1fv(valuesLoc, 2, values);
656 ASSERT_GL_NO_ERROR();
657
658 glClearColor(1.0, 0.0, 0.0, 1.0);
659 glClear(GL_COLOR_BUFFER_BIT);
660 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
661
662 drawQuad(program.get(), "position", 0.5f);
663 ASSERT_GL_NO_ERROR();
664 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
665
666 // Read back the binary.
667 GLint programLength = 0;
668 glGetProgramiv(program.get(), GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
669 ASSERT_GL_NO_ERROR();
670
671 GLsizei readLength = 0;
672 GLenum binaryFormat = GL_NONE;
673 std::vector<uint8_t> binary(programLength);
674 glGetProgramBinary(program.get(), programLength, &readLength, &binaryFormat, binary.data());
675 ASSERT_GL_NO_ERROR();
676
677 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
678
679 // Load a new program with the binary and draw.
680 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
681
682 valuesLoc = glGetUniformLocation(program.get(), "values");
683 ASSERT_NE(-1, valuesLoc);
684
685 glUseProgram(binaryProgram.get());
686 GLfloat values2[2] = {0.1f, 1.0f};
687 glUniform1fv(valuesLoc, 2, values2);
688 ASSERT_GL_NO_ERROR();
689
690 glClearColor(1.0, 0.0, 0.0, 1.0);
691 glClear(GL_COLOR_BUFFER_BIT);
692 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
693
694 drawQuad(binaryProgram.get(), "position", 0.5f);
695 ASSERT_GL_NO_ERROR();
696 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
697 }
698
699 // Test that uses many uniforms in the shaders
TEST_P(ProgramBinaryES3Test,BinaryWithLargeUniformCount)700 TEST_P(ProgramBinaryES3Test, BinaryWithLargeUniformCount)
701 {
702 // Suspecting AMD driver bug - failure seen on bots running on ATI GPU on Windows.
703 // http://anglebug.com/3721
704 ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL() && IsWindows());
705
706 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
707
708 constexpr char kVS[] =
709 "#version 300 es\n"
710 "uniform float redVS; \n"
711 "uniform block0 {\n"
712 " float val0;\n"
713 "};\n"
714 "uniform float greenVS; \n"
715 "uniform float blueVS; \n"
716 "in vec4 position;\n"
717 "out vec4 color;\n"
718 "void main() {\n"
719 " gl_Position = position;\n"
720 " color = vec4(redVS + val0, greenVS, blueVS, 1.0f);\n"
721 "}";
722
723 constexpr char kFS[] =
724 "#version 300 es\n"
725 "precision mediump float;\n"
726 "uniform float redFS; \n"
727 "uniform float greenFS; \n"
728 "uniform block1 {\n"
729 " float val1;\n"
730 " float val2;\n"
731 "};\n"
732 "uniform float blueFS; \n"
733 "in vec4 color;\n"
734 "out vec4 colorOut;\n"
735 "void main() {\n"
736 " colorOut = vec4(color.r + redFS,\n"
737 " color.g + greenFS + val1,\n"
738 " color.b + blueFS + val2, \n"
739 " color.a);\n"
740 "}";
741
742 // Init and draw with the program.
743 ANGLE_GL_PROGRAM(program, kVS, kFS);
744
745 float block0Data[4] = {-0.7f, 1.0f, 1.0f, 1.0f};
746 float block1Data[4] = {0.4f, -0.8f, 1.0f, 1.0f};
747 GLuint bindIndex0 = 5;
748 GLuint bindIndex1 = 2;
749
750 GLBuffer ubo0;
751 glBindBuffer(GL_UNIFORM_BUFFER, ubo0.get());
752 glBufferData(GL_UNIFORM_BUFFER, sizeof(block0Data), &block0Data, GL_STATIC_DRAW);
753 glBindBufferRange(GL_UNIFORM_BUFFER, bindIndex0, ubo0.get(), 0, sizeof(block0Data));
754 ASSERT_GL_NO_ERROR();
755
756 GLBuffer ubo1;
757 glBindBuffer(GL_UNIFORM_BUFFER, ubo1.get());
758 glBufferData(GL_UNIFORM_BUFFER, sizeof(block1Data), &block1Data, GL_STATIC_DRAW);
759 glBindBufferRange(GL_UNIFORM_BUFFER, bindIndex1, ubo1.get(), 0, sizeof(block1Data));
760 ASSERT_GL_NO_ERROR();
761
762 GLint block0Index = glGetUniformBlockIndex(program.get(), "block0");
763 ASSERT_NE(-1, block0Index);
764
765 GLint block1Index = glGetUniformBlockIndex(program.get(), "block1");
766 ASSERT_NE(-1, block1Index);
767
768 glUniformBlockBinding(program.get(), block0Index, bindIndex0);
769 glUniformBlockBinding(program.get(), block1Index, bindIndex1);
770 ASSERT_GL_NO_ERROR();
771
772 GLint redVSLoc = glGetUniformLocation(program.get(), "redVS");
773 ASSERT_NE(-1, redVSLoc);
774 GLint greenVSLoc = glGetUniformLocation(program.get(), "greenVS");
775 ASSERT_NE(-1, greenVSLoc);
776 GLint blueVSLoc = glGetUniformLocation(program.get(), "blueVS");
777 ASSERT_NE(-1, blueVSLoc);
778 GLint redFSLoc = glGetUniformLocation(program.get(), "redFS");
779 ASSERT_NE(-1, redFSLoc);
780 GLint greenFSLoc = glGetUniformLocation(program.get(), "greenFS");
781 ASSERT_NE(-1, greenFSLoc);
782 GLint blueFSLoc = glGetUniformLocation(program.get(), "blueFS");
783 ASSERT_NE(-1, blueFSLoc);
784
785 glUseProgram(program.get());
786 glUniform1f(redVSLoc, 0.6f);
787 glUniform1f(greenVSLoc, 0.2f);
788 glUniform1f(blueVSLoc, 1.1f);
789 glUniform1f(redFSLoc, 0.1f);
790 glUniform1f(greenFSLoc, 0.4f);
791 glUniform1f(blueFSLoc, 0.7f);
792 ASSERT_GL_NO_ERROR();
793
794 glClearColor(1.0, 0.0, 0.0, 1.0);
795 glClear(GL_COLOR_BUFFER_BIT);
796 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
797
798 drawQuad(program.get(), "position", 0.5f);
799 ASSERT_GL_NO_ERROR();
800 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::cyan);
801
802 // Read back the binary.
803 GLint programLength = 0;
804 glGetProgramiv(program.get(), GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
805 ASSERT_GL_NO_ERROR();
806
807 GLsizei readLength = 0;
808 GLenum binaryFormat = GL_NONE;
809 std::vector<uint8_t> binary(programLength);
810 glGetProgramBinary(program.get(), programLength, &readLength, &binaryFormat, binary.data());
811 ASSERT_GL_NO_ERROR();
812
813 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
814
815 // Load a new program with the binary and draw.
816 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
817
818 redVSLoc = glGetUniformLocation(program.get(), "redVS");
819 ASSERT_NE(-1, redVSLoc);
820 greenVSLoc = glGetUniformLocation(program.get(), "greenVS");
821 ASSERT_NE(-1, greenVSLoc);
822 blueVSLoc = glGetUniformLocation(program.get(), "blueVS");
823 ASSERT_NE(-1, blueVSLoc);
824 redFSLoc = glGetUniformLocation(program.get(), "redFS");
825 ASSERT_NE(-1, redFSLoc);
826 greenFSLoc = glGetUniformLocation(program.get(), "greenFS");
827 ASSERT_NE(-1, greenFSLoc);
828 blueFSLoc = glGetUniformLocation(program.get(), "blueFS");
829 ASSERT_NE(-1, blueFSLoc);
830
831 glUseProgram(binaryProgram.get());
832 glUniform1f(redVSLoc, 0.2f);
833 glUniform1f(greenVSLoc, -0.6f);
834 glUniform1f(blueVSLoc, 1.0f);
835 glUniform1f(redFSLoc, 1.5f);
836 glUniform1f(greenFSLoc, 0.2f);
837 glUniform1f(blueFSLoc, 0.8f);
838 ASSERT_GL_NO_ERROR();
839
840 glClearColor(1.0, 0.0, 0.0, 1.0);
841 glClear(GL_COLOR_BUFFER_BIT);
842 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
843
844 drawQuad(binaryProgram.get(), "position", 0.5f);
845 ASSERT_GL_NO_ERROR();
846 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
847 }
848
849 // Test that sampler texelFetch references are saved and loaded correctly
TEST_P(ProgramBinaryTest,SRGBDecodeWithSamplerAndTexelFetchTest)850 TEST_P(ProgramBinaryTest, SRGBDecodeWithSamplerAndTexelFetchTest)
851 {
852 if (!supported())
853 {
854 return;
855 }
856
857 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_sRGB_decode") ||
858 getClientMajorVersion() < 3);
859
860 // These OpenGL drivers appear not to respect the texelFetch exception
861 // http://anglebug.com/4991
862 ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
863 ANGLE_SKIP_TEST_IF(IsOpenGL() && IsAMD() && IsWindows());
864 ANGLE_SKIP_TEST_IF(IsOpenGL() && (IsNVIDIA() || IsARM64()) && IsMac());
865 ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsNexus5X());
866
867 constexpr char kVS[] =
868 "#version 300 es\n"
869 "precision highp float;\n"
870 "in vec4 position;\n"
871 "\n"
872 "void main()\n"
873 "{\n"
874 " gl_Position = vec4(position.xy, 0.0, 1.0);\n"
875 "}\n";
876
877 constexpr char kFS[] =
878 "#version 300 es\n"
879 "precision highp float;\n"
880 "uniform sampler2D tex;\n"
881 "in vec2 texcoord;\n"
882 "out vec4 out_color;\n"
883 "\n"
884 "void main()\n"
885 "{\n"
886 " out_color = texelFetch(tex, ivec2(0, 0), 0);\n"
887 "}\n";
888
889 GLProgram program;
890 program.makeRaster(kVS, kFS);
891 ASSERT_NE(0u, program.get());
892
893 GLuint reloadedProgram = glCreateProgram();
894 saveAndLoadProgram(program.get(), reloadedProgram);
895
896 GLint textureLocation = glGetUniformLocation(reloadedProgram, "tex");
897 ASSERT_NE(-1, textureLocation);
898
899 GLColor linearColor(64, 127, 191, 255);
900 GLColor srgbColor(13, 54, 133, 255);
901
902 GLTexture tex;
903 glBindTexture(GL_TEXTURE_2D, tex.get());
904 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8_ALPHA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
905 &linearColor);
906 ASSERT_GL_NO_ERROR();
907
908 GLSampler sampler;
909 glBindSampler(0, sampler.get());
910 glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_DECODE_EXT);
911
912 glUseProgram(reloadedProgram);
913 glUniform1i(textureLocation, 0);
914
915 glDisable(GL_DEPTH_TEST);
916 drawQuad(reloadedProgram, "position", 0.5f);
917
918 EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
919
920 glSamplerParameteri(sampler.get(), GL_TEXTURE_SRGB_DECODE_EXT, GL_SKIP_DECODE_EXT);
921 drawQuad(reloadedProgram, "position", 0.5f);
922
923 EXPECT_PIXEL_COLOR_NEAR(0, 0, srgbColor, 1.0);
924
925 glDeleteProgram(reloadedProgram);
926 }
927
928 // Test that array of structs containing array of samplers work as expected.
TEST_P(ProgramBinaryES3Test,ArrayOfStructContainingArrayOfSamplers)929 TEST_P(ProgramBinaryES3Test, ArrayOfStructContainingArrayOfSamplers)
930 {
931 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
932
933 constexpr char kFS[] =
934 "precision mediump float;\n"
935 "struct Data { mediump sampler2D data[2]; };\n"
936 "uniform Data test[2];\n"
937 "void main() {\n"
938 " gl_FragColor = vec4(texture2D(test[1].data[1], vec2(0.0, 0.0)).r,\n"
939 " texture2D(test[1].data[0], vec2(0.0, 0.0)).r,\n"
940 " texture2D(test[0].data[1], vec2(0.0, 0.0)).r,\n"
941 " texture2D(test[0].data[0], vec2(0.0, 0.0)).r);\n"
942 "}\n";
943
944 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
945 glUseProgram(program.get());
946 GLTexture textures[4];
947 GLColor expected = MakeGLColor(32, 64, 96, 255);
948 GLubyte data[8] = {}; // 4 bytes of padding, so that texture can be initialized with 4 bytes
949 memcpy(data, expected.data(), sizeof(expected));
950 for (int i = 0; i < 4; i++)
951 {
952 int outerIdx = i % 2;
953 int innerIdx = i / 2;
954 glActiveTexture(GL_TEXTURE0 + i);
955 glBindTexture(GL_TEXTURE_2D, textures[i]);
956 // Each element provides two components.
957 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data + i);
958 std::stringstream uniformName;
959 uniformName << "test[" << innerIdx << "].data[" << outerIdx << "]";
960 // Then send it as a uniform
961 GLint uniformLocation = glGetUniformLocation(program.get(), uniformName.str().c_str());
962 // The uniform should be active.
963 EXPECT_NE(uniformLocation, -1);
964
965 glUniform1i(uniformLocation, 3 - i);
966 ASSERT_GL_NO_ERROR();
967 }
968
969 drawQuad(program.get(), essl1_shaders::PositionAttrib(), 0.5f);
970 ASSERT_GL_NO_ERROR();
971 EXPECT_PIXEL_COLOR_EQ(0, 0, expected);
972
973 // Read back the binary.
974 GLint programLength = 0;
975 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programLength);
976 ASSERT_GL_NO_ERROR();
977
978 GLsizei readLength = 0;
979 GLenum binaryFormat = GL_NONE;
980 std::vector<uint8_t> binary(programLength);
981 glGetProgramBinary(program, programLength, &readLength, &binaryFormat, binary.data());
982 ASSERT_GL_NO_ERROR();
983
984 // Load a new program with the binary.
985 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
986 glUseProgram(binaryProgram);
987
988 for (int i = 0; i < 4; i++)
989 {
990 int outerIdx = i % 2;
991 int innerIdx = i / 2;
992 std::stringstream uniformName;
993 uniformName << "test[" << innerIdx << "].data[" << outerIdx << "]";
994 // Then send it as a uniform
995 GLint uniformLocation = glGetUniformLocation(program, uniformName.str().c_str());
996 // The uniform should be active.
997 EXPECT_NE(uniformLocation, -1);
998
999 glUniform1i(uniformLocation, 3 - i);
1000 ASSERT_GL_NO_ERROR();
1001 }
1002
1003 // Verify the uniform data with the binary program.
1004 drawQuad(binaryProgram, essl1_shaders::PositionAttrib(), 0.5f);
1005 ASSERT_GL_NO_ERROR();
1006 EXPECT_PIXEL_COLOR_EQ(0, 0, expected);
1007 }
1008
1009 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProgramBinaryES3Test);
1010 ANGLE_INSTANTIATE_TEST_ES3(ProgramBinaryES3Test);
1011
1012 class ProgramBinaryES31Test : public ANGLETest<>
1013 {
1014 protected:
ProgramBinaryES31Test()1015 ProgramBinaryES31Test()
1016 {
1017 setWindowWidth(128);
1018 setWindowHeight(128);
1019 setConfigRedBits(8);
1020 setConfigGreenBits(8);
1021 setConfigBlueBits(8);
1022 setConfigAlphaBits(8);
1023
1024 // Test flakiness was noticed when reusing displays.
1025 forceNewDisplay();
1026 }
1027
getAvailableProgramBinaryFormatCount() const1028 GLint getAvailableProgramBinaryFormatCount() const
1029 {
1030 GLint formatCount;
1031 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &formatCount);
1032 return formatCount;
1033 }
1034 };
1035
1036 // Tests that saving and loading a program attached with computer shader.
TEST_P(ProgramBinaryES31Test,ProgramBinaryWithComputeShader)1037 TEST_P(ProgramBinaryES31Test, ProgramBinaryWithComputeShader)
1038 {
1039 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
1040
1041 // http://anglebug.com/4092
1042 ANGLE_SKIP_TEST_IF(IsVulkan());
1043
1044 constexpr char kCS[] =
1045 "#version 310 es\n"
1046 "layout(local_size_x=4, local_size_y=3, local_size_z=1) in;\n"
1047 "uniform block {\n"
1048 " vec2 f;\n"
1049 "};\n"
1050 "uniform vec2 g;\n"
1051 "uniform highp sampler2D tex;\n"
1052 "void main() {\n"
1053 " vec4 color = texture(tex, f + g);\n"
1054 "}";
1055
1056 ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
1057
1058 // Read back the binary.
1059 GLint programLength = 0;
1060 glGetProgramiv(program.get(), GL_PROGRAM_BINARY_LENGTH, &programLength);
1061 ASSERT_GL_NO_ERROR();
1062
1063 GLsizei readLength = 0;
1064 GLenum binaryFormat = GL_NONE;
1065 std::vector<uint8_t> binary(programLength);
1066 glGetProgramBinary(program.get(), programLength, &readLength, &binaryFormat, binary.data());
1067 ASSERT_GL_NO_ERROR();
1068
1069 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
1070
1071 // Load a new program with the binary.
1072 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
1073 ASSERT_GL_NO_ERROR();
1074
1075 // Dispatch compute with the loaded binary program
1076 glUseProgram(binaryProgram.get());
1077 glDispatchCompute(8, 4, 2);
1078 ASSERT_GL_NO_ERROR();
1079 }
1080
1081 // Tests saving and loading a separable program that has a computer shader using a uniform and a
1082 // uniform block.
TEST_P(ProgramBinaryES31Test,SeparableProgramLinkedUniforms)1083 TEST_P(ProgramBinaryES31Test, SeparableProgramLinkedUniforms)
1084 {
1085 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
1086
1087 constexpr char kComputeShader[] = R"(#version 310 es
1088 layout(local_size_x=4, local_size_y=3, local_size_z=1) in;
1089 uniform float testUint;
1090 uniform TestBlock
1091 {
1092 mat4 testMatrix;
1093 };
1094 layout(rgba32ui) uniform highp writeonly uimage2D imageOut;
1095 void main() {
1096 imageStore(imageOut, ivec2(gl_LocalInvocationIndex, 0), uvec4(testMatrix[0][0] + testUint, 0, 0, 0));
1097 })";
1098
1099 ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader);
1100 glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
1101
1102 // Read back the binary.
1103 GLint programLength = 0;
1104 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programLength);
1105 ASSERT_GL_NO_ERROR();
1106
1107 GLsizei readLength = 0;
1108 GLenum binaryFormat = GL_NONE;
1109 std::vector<uint8_t> binary(programLength);
1110 glGetProgramBinary(program, programLength, &readLength, &binaryFormat, binary.data());
1111 ASSERT_GL_NO_ERROR();
1112
1113 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
1114
1115 // Load a new program with the binary.
1116 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
1117 ASSERT_GL_NO_ERROR();
1118
1119 glUseProgram(binaryProgram);
1120 ASSERT_GL_NO_ERROR();
1121 }
1122
1123 // Tests that saving and loading a program attached with computer shader.
TEST_P(ProgramBinaryES31Test,ProgramBinaryWithAtomicCounterComputeShader)1124 TEST_P(ProgramBinaryES31Test, ProgramBinaryWithAtomicCounterComputeShader)
1125 {
1126 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
1127
1128 constexpr char kComputeShader[] = R"(#version 310 es
1129 layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
1130 layout(binding = 0, offset = 4) uniform atomic_uint ac[2];
1131 void main() {
1132 atomicCounterIncrement(ac[0]);
1133 atomicCounterDecrement(ac[1]);
1134 })";
1135
1136 ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader);
1137
1138 // Read back the binary.
1139 GLint programLength = 0;
1140 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programLength);
1141 ASSERT_GL_NO_ERROR();
1142
1143 GLsizei readLength = 0;
1144 GLenum binaryFormat = GL_NONE;
1145 std::vector<uint8_t> binary(programLength);
1146 glGetProgramBinary(program, programLength, &readLength, &binaryFormat, binary.data());
1147 ASSERT_GL_NO_ERROR();
1148
1149 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
1150
1151 // Load a new program with the binary.
1152 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
1153 ASSERT_GL_NO_ERROR();
1154
1155 // Dispatch compute with the loaded binary program
1156 glUseProgram(binaryProgram);
1157
1158 // The initial value of 'ac[0]' is 3u, 'ac[1]' is 1u.
1159 unsigned int bufferData[3] = {11u, 3u, 1u};
1160 GLBuffer atomicCounterBuffer;
1161 glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer);
1162 glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW);
1163
1164 glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicCounterBuffer);
1165
1166 glDispatchCompute(1, 1, 1);
1167 EXPECT_GL_NO_ERROR();
1168
1169 glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
1170
1171 glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicCounterBuffer);
1172 void *mappedBuffer =
1173 glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint) * 3, GL_MAP_READ_BIT);
1174 memcpy(bufferData, mappedBuffer, sizeof(bufferData));
1175 glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
1176
1177 EXPECT_EQ(11u, bufferData[0]);
1178 EXPECT_EQ(4u, bufferData[1]);
1179 EXPECT_EQ(0u, bufferData[2]);
1180 ASSERT_GL_NO_ERROR();
1181 }
1182
1183 // Tests that image texture works correctly when loading a program from binary.
TEST_P(ProgramBinaryES31Test,ImageTextureBinding)1184 TEST_P(ProgramBinaryES31Test, ImageTextureBinding)
1185 {
1186 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
1187
1188 const char kComputeShader[] =
1189 R"(#version 310 es
1190 layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
1191 layout(r32ui, binding = 1) writeonly uniform highp uimage2D writeImage;
1192 void main()
1193 {
1194 imageStore(writeImage, ivec2(gl_LocalInvocationID.xy), uvec4(200u));
1195 })";
1196
1197 ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShader);
1198
1199 // Read back the binary.
1200 GLint programLength = 0;
1201 glGetProgramiv(program.get(), GL_PROGRAM_BINARY_LENGTH, &programLength);
1202 ASSERT_GL_NO_ERROR();
1203
1204 GLsizei readLength = 0;
1205 GLenum binaryFormat = GL_NONE;
1206 std::vector<uint8_t> binary(programLength);
1207 glGetProgramBinary(program.get(), programLength, &readLength, &binaryFormat, binary.data());
1208 ASSERT_GL_NO_ERROR();
1209
1210 EXPECT_EQ(static_cast<GLsizei>(programLength), readLength);
1211
1212 // Load a new program with the binary.
1213 ANGLE_GL_BINARY_ES3_PROGRAM(binaryProgram, binary, binaryFormat);
1214 ASSERT_GL_NO_ERROR();
1215
1216 // Dispatch compute with the loaded binary program
1217 glUseProgram(binaryProgram.get());
1218 GLTexture texture;
1219 glBindTexture(GL_TEXTURE_2D, texture);
1220 glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
1221 constexpr GLuint kInputValue = 100u;
1222 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &kInputValue);
1223 EXPECT_GL_NO_ERROR();
1224
1225 glBindImageTexture(1, texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
1226
1227 glDispatchCompute(1, 1, 1);
1228 EXPECT_GL_NO_ERROR();
1229
1230 GLFramebuffer framebuffer;
1231 glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
1232 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1233
1234 GLuint outputValue;
1235 glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &outputValue);
1236 EXPECT_EQ(200u, outputValue);
1237 ASSERT_GL_NO_ERROR();
1238 }
1239
1240 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProgramBinaryES31Test);
1241 ANGLE_INSTANTIATE_TEST_ES31(ProgramBinaryES31Test);
1242
1243 class ProgramBinaryTransformFeedbackTest : public ANGLETest<>
1244 {
1245 protected:
ProgramBinaryTransformFeedbackTest()1246 ProgramBinaryTransformFeedbackTest()
1247 {
1248 setWindowWidth(128);
1249 setWindowHeight(128);
1250 setConfigRedBits(8);
1251 setConfigGreenBits(8);
1252 setConfigBlueBits(8);
1253 setConfigAlphaBits(8);
1254 }
1255
testSetUp()1256 void testSetUp() override
1257 {
1258 constexpr char kVS[] = R"(#version 300 es
1259 in vec4 inputAttribute;
1260 out vec4 outputVarying;
1261 void main()
1262 {
1263 outputVarying = inputAttribute;
1264 })";
1265
1266 constexpr char kFS[] = R"(#version 300 es
1267 precision highp float;
1268 out vec4 outputColor;
1269 void main()
1270 {
1271 outputColor = vec4(1,0,0,1);
1272 })";
1273
1274 std::vector<std::string> transformFeedbackVaryings;
1275 transformFeedbackVaryings.push_back("outputVarying");
1276
1277 mProgram = CompileProgramWithTransformFeedback(kVS, kFS, transformFeedbackVaryings,
1278 GL_SEPARATE_ATTRIBS);
1279 if (mProgram == 0)
1280 {
1281 FAIL() << "shader compilation failed.";
1282 }
1283
1284 ASSERT_GL_NO_ERROR();
1285 }
1286
testTearDown()1287 void testTearDown() override { glDeleteProgram(mProgram); }
1288
getAvailableProgramBinaryFormatCount() const1289 GLint getAvailableProgramBinaryFormatCount() const
1290 {
1291 GLint formatCount;
1292 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &formatCount);
1293 return formatCount;
1294 }
1295
1296 GLuint mProgram;
1297 };
1298
1299 // This tests the assumption that float attribs of different size
1300 // should not internally cause a vertex shader recompile (for conversion).
TEST_P(ProgramBinaryTransformFeedbackTest,GetTransformFeedbackVarying)1301 TEST_P(ProgramBinaryTransformFeedbackTest, GetTransformFeedbackVarying)
1302 {
1303 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_get_program_binary"));
1304
1305 ANGLE_SKIP_TEST_IF(getAvailableProgramBinaryFormatCount() == 0);
1306
1307 // http://anglebug.com/3690
1308 ANGLE_SKIP_TEST_IF(IsAndroid() && (IsPixel2() || IsPixel2XL()) && IsVulkan());
1309 // http://anglebug.com/4092
1310 ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
1311
1312 std::vector<uint8_t> binary(0);
1313 GLint programLength = 0;
1314 GLint writtenLength = 0;
1315 GLenum binaryFormat = 0;
1316
1317 // Save the program binary out
1318 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
1319 ASSERT_GL_NO_ERROR();
1320 binary.resize(programLength);
1321 glGetProgramBinaryOES(mProgram, programLength, &writtenLength, &binaryFormat, binary.data());
1322 ASSERT_GL_NO_ERROR();
1323
1324 glDeleteProgram(mProgram);
1325
1326 // Load program binary
1327 mProgram = glCreateProgram();
1328 glProgramBinaryOES(mProgram, binaryFormat, binary.data(), writtenLength);
1329
1330 // Ensure the loaded binary is linked
1331 GLint linkStatus;
1332 glGetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus);
1333 EXPECT_TRUE(linkStatus != 0);
1334
1335 // Query information about the transform feedback varying
1336 char varyingName[64];
1337 GLsizei varyingSize = 0;
1338 GLenum varyingType = GL_NONE;
1339
1340 glGetTransformFeedbackVarying(mProgram, 0, 64, &writtenLength, &varyingSize, &varyingType,
1341 varyingName);
1342 EXPECT_GL_NO_ERROR();
1343
1344 EXPECT_EQ(13, writtenLength);
1345 EXPECT_STREQ("outputVarying", varyingName);
1346 EXPECT_EQ(1, varyingSize);
1347 EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varyingType);
1348
1349 EXPECT_GL_NO_ERROR();
1350 }
1351
1352 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProgramBinaryTransformFeedbackTest);
1353 ANGLE_INSTANTIATE_TEST_ES3(ProgramBinaryTransformFeedbackTest);
1354
1355 // For the ProgramBinariesAcrossPlatforms tests, we need two sets of params:
1356 // - a set to save the program binary
1357 // - a set to load the program binary
1358 // We combine these into one struct extending PlatformParameters so we can reuse existing ANGLE test
1359 // macros
1360 struct PlatformsWithLinkResult : PlatformParameters
1361 {
PlatformsWithLinkResultPlatformsWithLinkResult1362 PlatformsWithLinkResult(PlatformParameters saveParams,
1363 PlatformParameters loadParamsIn,
1364 bool expectedLinkResultIn)
1365 {
1366 majorVersion = saveParams.majorVersion;
1367 minorVersion = saveParams.minorVersion;
1368 eglParameters = saveParams.eglParameters;
1369 loadParams = loadParamsIn;
1370 expectedLinkResult = expectedLinkResultIn;
1371 }
1372
1373 PlatformParameters loadParams;
1374 bool expectedLinkResult;
1375 };
1376
1377 // Provide a custom gtest parameter name function for PlatformsWithLinkResult
1378 // to avoid returning the same parameter name twice. Such a conflict would happen
1379 // between ES2_D3D11_to_ES2D3D11 and ES2_D3D11_to_ES3D3D11 as they were both
1380 // named ES2_D3D11
operator <<(std::ostream & stream,const PlatformsWithLinkResult & platform)1381 std::ostream &operator<<(std::ostream &stream, const PlatformsWithLinkResult &platform)
1382 {
1383 const PlatformParameters &platform1 = platform;
1384 const PlatformParameters &platform2 = platform.loadParams;
1385 stream << platform1 << "_to_" << platform2;
1386 return stream;
1387 }
1388
1389 class ProgramBinariesAcrossPlatforms : public testing::TestWithParam<PlatformsWithLinkResult>
1390 {
1391 public:
SetUp()1392 void SetUp() override
1393 {
1394 mOSWindow = OSWindow::New();
1395 bool result = mOSWindow->initialize("ProgramBinariesAcrossRenderersTests", 100, 100);
1396
1397 if (result == false)
1398 {
1399 FAIL() << "Failed to create OS window";
1400 }
1401
1402 mEntryPointsLib.reset(
1403 angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, angle::SearchType::ModuleDir));
1404 }
1405
createAndInitEGLWindow(angle::PlatformParameters & param)1406 EGLWindow *createAndInitEGLWindow(angle::PlatformParameters ¶m)
1407 {
1408 EGLWindow *eglWindow = EGLWindow::New(param.clientType, param.majorVersion,
1409 param.minorVersion, param.profileMask);
1410 ConfigParameters configParams;
1411 bool result = eglWindow->initializeGL(mOSWindow, mEntryPointsLib.get(), param.driver,
1412 param.eglParameters, configParams);
1413 if (!result)
1414 {
1415 EGLWindow::Delete(&eglWindow);
1416 }
1417
1418 LoadUtilGLES(eglGetProcAddress);
1419
1420 return eglWindow;
1421 }
1422
destroyEGLWindow(EGLWindow ** eglWindow)1423 void destroyEGLWindow(EGLWindow **eglWindow)
1424 {
1425 ASSERT_NE(nullptr, *eglWindow);
1426 (*eglWindow)->destroyGL();
1427 EGLWindow::Delete(eglWindow);
1428 }
1429
createES2ProgramFromSource()1430 GLuint createES2ProgramFromSource()
1431 {
1432 return CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1433 }
1434
createES3ProgramFromSource()1435 GLuint createES3ProgramFromSource()
1436 {
1437 return CompileProgram(essl3_shaders::vs::Simple(), essl3_shaders::fs::Red());
1438 }
1439
drawWithProgram(GLuint program)1440 void drawWithProgram(GLuint program)
1441 {
1442 glClearColor(0, 0, 0, 1);
1443 glClear(GL_COLOR_BUFFER_BIT);
1444
1445 GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
1446
1447 glUseProgram(program);
1448
1449 const GLfloat vertices[] = {
1450 -1.0f, 1.0f, 0.5f, -1.0f, -1.0f, 0.5f, 1.0f, -1.0f, 0.5f,
1451
1452 -1.0f, 1.0f, 0.5f, 1.0f, -1.0f, 0.5f, 1.0f, 1.0f, 0.5f,
1453 };
1454
1455 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
1456 glEnableVertexAttribArray(positionLocation);
1457
1458 glDrawArrays(GL_TRIANGLES, 0, 6);
1459
1460 glDisableVertexAttribArray(positionLocation);
1461 glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
1462
1463 EXPECT_PIXEL_EQ(mOSWindow->getWidth() / 2, mOSWindow->getHeight() / 2, 255, 0, 0, 255);
1464 }
1465
TearDown()1466 void TearDown() override
1467 {
1468 mOSWindow->destroy();
1469 OSWindow::Delete(&mOSWindow);
1470 }
1471
1472 OSWindow *mOSWindow = nullptr;
1473 std::unique_ptr<angle::Library> mEntryPointsLib;
1474 };
1475
1476 // Tries to create a program binary using one set of platform params, then load it using a different
1477 // sent of params
TEST_P(ProgramBinariesAcrossPlatforms,CreateAndReloadBinary)1478 TEST_P(ProgramBinariesAcrossPlatforms, CreateAndReloadBinary)
1479 {
1480 angle::PlatformParameters firstRenderer = GetParam();
1481 angle::PlatformParameters secondRenderer = GetParam().loadParams;
1482 bool expectedLinkResult = GetParam().expectedLinkResult;
1483
1484 // First renderer not supported, skipping test.
1485 ANGLE_SKIP_TEST_IF(!(IsPlatformAvailable(firstRenderer)));
1486
1487 // Second renderer not supported, skipping test.
1488 ANGLE_SKIP_TEST_IF(!(IsPlatformAvailable(secondRenderer)));
1489
1490 EGLWindow *eglWindow = nullptr;
1491 std::vector<uint8_t> binary(0);
1492 GLuint program = 0;
1493
1494 GLint programLength = 0;
1495 GLint writtenLength = 0;
1496 GLenum binaryFormat = 0;
1497
1498 // Create a EGL window with the first renderer
1499 eglWindow = createAndInitEGLWindow(firstRenderer);
1500 if (eglWindow == nullptr)
1501 {
1502 FAIL() << "Failed to create EGL window";
1503 }
1504
1505 // If the test is trying to use both the default GPU and WARP, but the default GPU *IS* WARP,
1506 // then our expectations for the test results will be invalid.
1507 if (firstRenderer.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE &&
1508 secondRenderer.eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_D3D_WARP_ANGLE)
1509 {
1510 std::string rendererString =
1511 std::string(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
1512 angle::ToLower(&rendererString);
1513
1514 auto basicRenderPos = rendererString.find(std::string("microsoft basic render"));
1515 auto softwareAdapterPos = rendererString.find(std::string("software adapter"));
1516
1517 // The first renderer is using WARP, even though we didn't explictly request it
1518 // We should skip this test
1519 ANGLE_SKIP_TEST_IF(basicRenderPos != std::string::npos ||
1520 softwareAdapterPos != std::string::npos);
1521 }
1522
1523 // Create a program
1524 if (firstRenderer.majorVersion == 3)
1525 {
1526 program = createES3ProgramFromSource();
1527 }
1528 else
1529 {
1530 program = createES2ProgramFromSource();
1531 }
1532
1533 if (program == 0)
1534 {
1535 destroyEGLWindow(&eglWindow);
1536 FAIL() << "Failed to create program from source";
1537 }
1538
1539 // Draw using the program to ensure it works as expected
1540 drawWithProgram(program);
1541 EXPECT_GL_NO_ERROR();
1542
1543 // Save the program binary out from this renderer
1544 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
1545 EXPECT_GL_NO_ERROR();
1546 binary.resize(programLength);
1547 glGetProgramBinaryOES(program, programLength, &writtenLength, &binaryFormat, binary.data());
1548 EXPECT_GL_NO_ERROR();
1549
1550 // Destroy the first renderer
1551 glDeleteProgram(program);
1552 destroyEGLWindow(&eglWindow);
1553
1554 // Create an EGL window with the second renderer
1555 eglWindow = createAndInitEGLWindow(secondRenderer);
1556 if (eglWindow == nullptr)
1557 {
1558 FAIL() << "Failed to create EGL window";
1559 }
1560
1561 program = glCreateProgram();
1562 glProgramBinaryOES(program, binaryFormat, binary.data(), writtenLength);
1563
1564 GLint linkStatus;
1565 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
1566 EXPECT_EQ(expectedLinkResult, (linkStatus != 0));
1567
1568 if (linkStatus != 0)
1569 {
1570 // If the link was successful, then we should try to draw using the program to ensure it
1571 // works as expected
1572 drawWithProgram(program);
1573 EXPECT_GL_NO_ERROR();
1574 }
1575
1576 // Destroy the second renderer
1577 glDeleteProgram(program);
1578 destroyEGLWindow(&eglWindow);
1579 }
1580
1581 // clang-format off
1582 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProgramBinariesAcrossPlatforms);
1583 ANGLE_INSTANTIATE_TEST(ProgramBinariesAcrossPlatforms,
1584 // | Save the | Load the | Expected
1585 // | program in | program in | link
1586 // | this config | this config | result
1587 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D11(), true ), // Loading + reloading binary should work
1588 PlatformsWithLinkResult(ES3_D3D11(), ES3_D3D11(), true ), // Loading + reloading binary should work
1589 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D9(), false), // Switching from D3D11 to D3D9 shouldn't work
1590 PlatformsWithLinkResult(ES2_D3D9(), ES2_D3D11(), false), // Switching from D3D9 to D3D11 shouldn't work
1591 PlatformsWithLinkResult(ES2_D3D11(), ES3_D3D11(), false), // Switching to newer client version shouldn't work
1592 PlatformsWithLinkResult(ES2_VULKAN(), ES2_VULKAN(), true ), // Loading + reloading binary should work
1593 PlatformsWithLinkResult(ES3_VULKAN(), ES3_VULKAN(), true ), // Loading + reloading binary should work
1594 PlatformsWithLinkResult(ES31_VULKAN(), ES31_VULKAN(), true ), // Loading + reloading binary should work
1595 PlatformsWithLinkResult(ES3_VULKAN(), ES31_VULKAN(), false), // Switching to newer client version shouldn't work with Vulkan
1596 );
1597 // clang-format on
1598