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 <memory>
10 #include <stdint.h>
11 
12 #include "EGLWindow.h"
13 #include "OSWindow.h"
14 #include "test_utils/angle_test_configs.h"
15 
16 using namespace angle;
17 
18 class ProgramBinaryTest : public ANGLETest
19 {
20   protected:
ProgramBinaryTest()21     ProgramBinaryTest()
22     {
23         setWindowWidth(128);
24         setWindowHeight(128);
25         setConfigRedBits(8);
26         setConfigGreenBits(8);
27         setConfigBlueBits(8);
28         setConfigAlphaBits(8);
29     }
30 
SetUp()31     void SetUp() override
32     {
33         ANGLETest::SetUp();
34 
35         const std::string vertexShaderSource = SHADER_SOURCE
36         (
37             attribute vec4 inputAttribute;
38             void main()
39             {
40                 gl_Position = inputAttribute;
41             }
42         );
43 
44         const std::string fragmentShaderSource = SHADER_SOURCE
45         (
46             void main()
47             {
48                 gl_FragColor = vec4(1,0,0,1);
49             }
50         );
51 
52         mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
53         if (mProgram == 0)
54         {
55             FAIL() << "shader compilation failed.";
56         }
57 
58         glGenBuffers(1, &mBuffer);
59         glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
60         glBufferData(GL_ARRAY_BUFFER, 128, NULL, GL_STATIC_DRAW);
61         glBindBuffer(GL_ARRAY_BUFFER, 0);
62 
63         ASSERT_GL_NO_ERROR();
64     }
65 
TearDown()66     void TearDown() override
67     {
68         glDeleteProgram(mProgram);
69         glDeleteBuffers(1, &mBuffer);
70 
71         ANGLETest::TearDown();
72     }
73 
getAvailableProgramBinaryFormatCount() const74     GLint getAvailableProgramBinaryFormatCount() const
75     {
76         GLint formatCount;
77         glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &formatCount);
78         return formatCount;
79     }
80 
81     GLuint mProgram;
82     GLuint mBuffer;
83 };
84 
85 // This tests the assumption that float attribs of different size
86 // should not internally cause a vertex shader recompile (for conversion).
TEST_P(ProgramBinaryTest,FloatDynamicShaderSize)87 TEST_P(ProgramBinaryTest, FloatDynamicShaderSize)
88 {
89     if (!extensionEnabled("GL_OES_get_program_binary"))
90     {
91         std::cout << "Test skipped because GL_OES_get_program_binary is not available."
92                   << std::endl;
93         return;
94     }
95 
96     if (getAvailableProgramBinaryFormatCount() == 0)
97     {
98         std::cout << "Test skipped because no program binary formats are available." << std::endl;
99         return;
100     }
101 
102     glUseProgram(mProgram);
103     glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
104 
105     glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, NULL);
106     glEnableVertexAttribArray(0);
107     glDrawArrays(GL_POINTS, 0, 1);
108 
109     GLint programLength;
110     glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
111 
112     EXPECT_GL_NO_ERROR();
113 
114     for (GLsizei size = 1; size <= 3; size++)
115     {
116         glVertexAttribPointer(0, size, GL_FLOAT, GL_FALSE, 8, NULL);
117         glEnableVertexAttribArray(0);
118         glDrawArrays(GL_POINTS, 0, 1);
119 
120         GLint newProgramLength;
121         glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &newProgramLength);
122         EXPECT_GL_NO_ERROR();
123         EXPECT_EQ(programLength, newProgramLength);
124     }
125 }
126 
127 // Tests that switching between signed and unsigned un-normalized data doesn't trigger a bug
128 // in the D3D11 back-end.
TEST_P(ProgramBinaryTest,DynamicShadersSignatureBug)129 TEST_P(ProgramBinaryTest, DynamicShadersSignatureBug)
130 {
131     glUseProgram(mProgram);
132     glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
133 
134     GLint attribLocation = glGetAttribLocation(mProgram, "inputAttribute");
135     ASSERT_NE(-1, attribLocation);
136     glEnableVertexAttribArray(attribLocation);
137 
138     glVertexAttribPointer(attribLocation, 2, GL_BYTE, GL_FALSE, 0, nullptr);
139     glDrawArrays(GL_POINTS, 0, 1);
140 
141     glVertexAttribPointer(attribLocation, 2, GL_UNSIGNED_BYTE, GL_FALSE, 0, nullptr);
142     glDrawArrays(GL_POINTS, 0, 1);
143 }
144 
145 // This tests the ability to successfully save and load a program binary.
TEST_P(ProgramBinaryTest,SaveAndLoadBinary)146 TEST_P(ProgramBinaryTest, SaveAndLoadBinary)
147 {
148     if (!extensionEnabled("GL_OES_get_program_binary"))
149     {
150         std::cout << "Test skipped because GL_OES_get_program_binary is not available."
151                   << std::endl;
152         return;
153     }
154 
155     if (getAvailableProgramBinaryFormatCount() == 0)
156     {
157         std::cout << "Test skipped because no program binary formats are available." << std::endl;
158         return;
159     }
160 
161     GLint programLength = 0;
162     GLint writtenLength = 0;
163     GLenum binaryFormat = 0;
164 
165     glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
166     EXPECT_GL_NO_ERROR();
167 
168     std::vector<uint8_t> binary(programLength);
169     glGetProgramBinaryOES(mProgram, programLength, &writtenLength, &binaryFormat, binary.data());
170     EXPECT_GL_NO_ERROR();
171 
172     // The lengths reported by glGetProgramiv and glGetProgramBinaryOES should match
173     EXPECT_EQ(programLength, writtenLength);
174 
175     if (writtenLength)
176     {
177         GLuint program2 = glCreateProgram();
178         glProgramBinaryOES(program2, binaryFormat, binary.data(), writtenLength);
179 
180         EXPECT_GL_NO_ERROR();
181 
182         GLint linkStatus;
183         glGetProgramiv(program2, GL_LINK_STATUS, &linkStatus);
184         if (linkStatus == 0)
185         {
186             GLint infoLogLength;
187             glGetProgramiv(program2, GL_INFO_LOG_LENGTH, &infoLogLength);
188 
189             if (infoLogLength > 0)
190             {
191                 std::vector<GLchar> infoLog(infoLogLength);
192                 glGetProgramInfoLog(program2, static_cast<GLsizei>(infoLog.size()), NULL,
193                                     &infoLog[0]);
194                 FAIL() << "program link failed: " << &infoLog[0];
195             }
196             else
197             {
198                 FAIL() << "program link failed.";
199             }
200         }
201         else
202         {
203             glUseProgram(program2);
204             glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
205 
206             glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, NULL);
207             glEnableVertexAttribArray(0);
208             glDrawArrays(GL_POINTS, 0, 1);
209 
210             EXPECT_GL_NO_ERROR();
211         }
212 
213         glDeleteProgram(program2);
214     }
215 }
216 
217 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
218 ANGLE_INSTANTIATE_TEST(ProgramBinaryTest,
219                        ES2_D3D9(),
220                        ES2_D3D11(),
221                        ES3_D3D11(),
222                        ES2_OPENGL(),
223                        ES3_OPENGL());
224 
225 class ProgramBinaryTransformFeedbackTest : public ANGLETest
226 {
227   protected:
ProgramBinaryTransformFeedbackTest()228     ProgramBinaryTransformFeedbackTest()
229     {
230         setWindowWidth(128);
231         setWindowHeight(128);
232         setConfigRedBits(8);
233         setConfigGreenBits(8);
234         setConfigBlueBits(8);
235         setConfigAlphaBits(8);
236     }
237 
SetUp()238     void SetUp() override
239     {
240         ANGLETest::SetUp();
241 
242         const std::string vertexShaderSource = SHADER_SOURCE
243         (   #version 300 es\n
244             in vec4 inputAttribute;
245             out vec4 outputVarying;
246             void main()
247             {
248                 outputVarying = inputAttribute;
249             }
250         );
251 
252         const std::string fragmentShaderSource = SHADER_SOURCE
253         (   #version 300 es\n
254             precision highp float;
255             out vec4 outputColor;
256             void main()
257             {
258                 outputColor = vec4(1,0,0,1);
259             }
260         );
261 
262         std::vector<std::string> transformFeedbackVaryings;
263         transformFeedbackVaryings.push_back("outputVarying");
264 
265         mProgram = CompileProgramWithTransformFeedback(
266             vertexShaderSource, fragmentShaderSource, transformFeedbackVaryings,
267             GL_SEPARATE_ATTRIBS);
268         if (mProgram == 0)
269         {
270             FAIL() << "shader compilation failed.";
271         }
272 
273         ASSERT_GL_NO_ERROR();
274     }
275 
TearDown()276     void TearDown() override
277     {
278         glDeleteProgram(mProgram);
279 
280         ANGLETest::TearDown();
281     }
282 
getAvailableProgramBinaryFormatCount() const283     GLint getAvailableProgramBinaryFormatCount() const
284     {
285         GLint formatCount;
286         glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &formatCount);
287         return formatCount;
288     }
289 
290     GLuint mProgram;
291 };
292 
293 // This tests the assumption that float attribs of different size
294 // should not internally cause a vertex shader recompile (for conversion).
TEST_P(ProgramBinaryTransformFeedbackTest,GetTransformFeedbackVarying)295 TEST_P(ProgramBinaryTransformFeedbackTest, GetTransformFeedbackVarying)
296 {
297     if (!extensionEnabled("GL_OES_get_program_binary"))
298     {
299         std::cout << "Test skipped because GL_OES_get_program_binary is not available."
300                   << std::endl;
301         return;
302     }
303 
304     if (getAvailableProgramBinaryFormatCount() == 0)
305     {
306         std::cout << "Test skipped because no program binary formats are available." << std::endl;
307         return;
308     }
309 
310     std::vector<uint8_t> binary(0);
311     GLint programLength = 0;
312     GLint writtenLength = 0;
313     GLenum binaryFormat = 0;
314 
315     // Save the program binary out
316     glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
317     ASSERT_GL_NO_ERROR();
318     binary.resize(programLength);
319     glGetProgramBinaryOES(mProgram, programLength, &writtenLength, &binaryFormat, binary.data());
320     ASSERT_GL_NO_ERROR();
321 
322     glDeleteProgram(mProgram);
323 
324     // Load program binary
325     mProgram = glCreateProgram();
326     glProgramBinaryOES(mProgram, binaryFormat, binary.data(), writtenLength);
327 
328     // Ensure the loaded binary is linked
329     GLint linkStatus;
330     glGetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus);
331     EXPECT_TRUE(linkStatus != 0);
332 
333     // Query information about the transform feedback varying
334     char varyingName[64];
335     GLsizei varyingSize = 0;
336     GLenum varyingType = GL_NONE;
337 
338     glGetTransformFeedbackVarying(mProgram, 0, 64, &writtenLength, &varyingSize, &varyingType, varyingName);
339     EXPECT_GL_NO_ERROR();
340 
341     EXPECT_EQ(13, writtenLength);
342     EXPECT_STREQ("outputVarying", varyingName);
343     EXPECT_EQ(1, varyingSize);
344     EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varyingType);
345 
346     EXPECT_GL_NO_ERROR();
347 }
348 
349 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
350 ANGLE_INSTANTIATE_TEST(ProgramBinaryTransformFeedbackTest,
351                        ES3_D3D11(),
352                        ES3_OPENGL());
353 
354 // For the ProgramBinariesAcrossPlatforms tests, we need two sets of params:
355 // - a set to save the program binary
356 // - a set to load the program binary
357 // We combine these into one struct extending PlatformParameters so we can reuse existing ANGLE test macros
358 struct PlatformsWithLinkResult : PlatformParameters
359 {
PlatformsWithLinkResultPlatformsWithLinkResult360     PlatformsWithLinkResult(PlatformParameters saveParams, PlatformParameters loadParamsIn, bool expectedLinkResultIn)
361     {
362         majorVersion = saveParams.majorVersion;
363         minorVersion = saveParams.minorVersion;
364         eglParameters = saveParams.eglParameters;
365         loadParams = loadParamsIn;
366         expectedLinkResult = expectedLinkResultIn;
367     }
368 
369     PlatformParameters loadParams;
370     bool expectedLinkResult;
371 };
372 
373 // Provide a custom gtest parameter name function for PlatformsWithLinkResult
374 // to avoid returning the same parameter name twice. Such a conflict would happen
375 // between ES2_D3D11_to_ES2D3D11 and ES2_D3D11_to_ES3D3D11 as they were both
376 // named ES2_D3D11
operator <<(std::ostream & stream,const PlatformsWithLinkResult & platform)377 std::ostream &operator<<(std::ostream& stream, const PlatformsWithLinkResult &platform)
378 {
379     const PlatformParameters &platform1 = platform;
380     const PlatformParameters &platform2 = platform.loadParams;
381     stream << platform1 << "_to_" << platform2;
382     return stream;
383 }
384 
385 class ProgramBinariesAcrossPlatforms : public testing::TestWithParam<PlatformsWithLinkResult>
386 {
387   public:
SetUp()388     void SetUp() override
389     {
390         mOSWindow = CreateOSWindow();
391         bool result = mOSWindow->initialize("ProgramBinariesAcrossRenderersTests", 100, 100);
392 
393         if (result == false)
394         {
395             FAIL() << "Failed to create OS window";
396         }
397     }
398 
createAndInitEGLWindow(angle::PlatformParameters & param)399     EGLWindow *createAndInitEGLWindow(angle::PlatformParameters &param)
400     {
401         EGLWindow *eglWindow =
402             new EGLWindow(param.majorVersion, param.minorVersion, param.eglParameters);
403         bool result = eglWindow->initializeGL(mOSWindow);
404         if (result == false)
405         {
406             SafeDelete(eglWindow);
407             eglWindow = nullptr;
408         }
409 
410         return eglWindow;
411     }
412 
destroyEGLWindow(EGLWindow ** eglWindow)413     void destroyEGLWindow(EGLWindow **eglWindow)
414     {
415         ASSERT(*eglWindow != nullptr);
416         (*eglWindow)->destroyGL();
417         SafeDelete(*eglWindow);
418         *eglWindow = nullptr;
419     }
420 
createES2ProgramFromSource()421     GLuint createES2ProgramFromSource()
422     {
423         const std::string testVertexShaderSource = SHADER_SOURCE
424         (
425             attribute highp vec4 position;
426 
427             void main(void)
428             {
429                 gl_Position = position;
430             }
431         );
432 
433         const std::string testFragmentShaderSource = SHADER_SOURCE
434         (
435             void main(void)
436             {
437                 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
438             }
439         );
440 
441         return CompileProgram(testVertexShaderSource, testFragmentShaderSource);
442     }
443 
createES3ProgramFromSource()444     GLuint createES3ProgramFromSource()
445     {
446         const std::string testVertexShaderSource = SHADER_SOURCE
447         (   #version 300 es\n
448             precision highp float;
449             in highp vec4 position;
450 
451             void main(void)
452             {
453                 gl_Position = position;
454             }
455         );
456 
457         const std::string testFragmentShaderSource = SHADER_SOURCE
458         (   #version 300 es \n
459             precision highp float;
460             out vec4 out_FragColor;
461 
462             void main(void)
463             {
464                 out_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
465             }
466         );
467 
468         return CompileProgram(testVertexShaderSource, testFragmentShaderSource);
469     }
470 
drawWithProgram(GLuint program)471     void drawWithProgram(GLuint program)
472     {
473         glClearColor(0, 0, 0, 1);
474         glClear(GL_COLOR_BUFFER_BIT);
475 
476         GLint positionLocation = glGetAttribLocation(program, "position");
477 
478         glUseProgram(program);
479 
480         const GLfloat vertices[] =
481         {
482             -1.0f,  1.0f, 0.5f,
483             -1.0f, -1.0f, 0.5f,
484              1.0f, -1.0f, 0.5f,
485 
486             -1.0f,  1.0f, 0.5f,
487              1.0f, -1.0f, 0.5f,
488              1.0f,  1.0f, 0.5f,
489         };
490 
491         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
492         glEnableVertexAttribArray(positionLocation);
493 
494         glDrawArrays(GL_TRIANGLES, 0, 6);
495 
496         glDisableVertexAttribArray(positionLocation);
497         glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
498 
499         EXPECT_PIXEL_EQ(mOSWindow->getWidth() / 2, mOSWindow->getHeight() / 2, 255, 0, 0, 255);
500     }
501 
TearDown()502     void TearDown() override
503     {
504         mOSWindow->destroy();
505         SafeDelete(mOSWindow);
506     }
507 
508     OSWindow *mOSWindow;
509 };
510 
511 // Tries to create a program binary using one set of platform params, then load it using a different sent of params
TEST_P(ProgramBinariesAcrossPlatforms,CreateAndReloadBinary)512 TEST_P(ProgramBinariesAcrossPlatforms, CreateAndReloadBinary)
513 {
514     angle::PlatformParameters firstRenderer  = GetParam();
515     angle::PlatformParameters secondRenderer = GetParam().loadParams;
516     bool expectedLinkResult                  = GetParam().expectedLinkResult;
517 
518     if (!(IsPlatformAvailable(firstRenderer)))
519     {
520         std::cout << "First renderer not supported, skipping test";
521         return;
522     }
523 
524     if (!(IsPlatformAvailable(secondRenderer)))
525     {
526         std::cout << "Second renderer not supported, skipping test";
527         return;
528     }
529 
530     EGLWindow *eglWindow = nullptr;
531     std::vector<uint8_t> binary(0);
532     GLuint program = 0;
533 
534     GLint programLength = 0;
535     GLint writtenLength = 0;
536     GLenum binaryFormat = 0;
537 
538     // Create a EGL window with the first renderer
539     eglWindow = createAndInitEGLWindow(firstRenderer);
540     if (eglWindow == nullptr)
541     {
542         FAIL() << "Failed to create EGL window";
543         return;
544     }
545 
546     // If the test is trying to use both the default GPU and WARP, but the default GPU *IS* WARP,
547     // then our expectations for the test results will be invalid.
548     if (firstRenderer.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE &&
549         secondRenderer.eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE)
550     {
551         std::string rendererString = std::string(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
552         std::transform(rendererString.begin(), rendererString.end(), rendererString.begin(), ::tolower);
553 
554         auto basicRenderPos = rendererString.find(std::string("microsoft basic render"));
555         auto softwareAdapterPos = rendererString.find(std::string("software adapter"));
556 
557         if (basicRenderPos != std::string::npos || softwareAdapterPos != std::string::npos)
558         {
559             // The first renderer is using WARP, even though we didn't explictly request it
560             // We should skip this test
561             std::cout << "Test skipped on when default GPU is WARP." << std::endl;
562             return;
563         }
564     }
565 
566     // Create a program
567     if (firstRenderer.majorVersion == 3)
568     {
569         program = createES3ProgramFromSource();
570     }
571     else
572     {
573         program = createES2ProgramFromSource();
574     }
575 
576     if (program == 0)
577     {
578         destroyEGLWindow(&eglWindow);
579         FAIL() << "Failed to create program from source";
580     }
581 
582     // Draw using the program to ensure it works as expected
583     drawWithProgram(program);
584     EXPECT_GL_NO_ERROR();
585 
586     // Save the program binary out from this renderer
587     glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
588     EXPECT_GL_NO_ERROR();
589     binary.resize(programLength);
590     glGetProgramBinaryOES(program, programLength, &writtenLength, &binaryFormat, binary.data());
591     EXPECT_GL_NO_ERROR();
592 
593     // Destroy the first renderer
594     glDeleteProgram(program);
595     destroyEGLWindow(&eglWindow);
596 
597     // Create an EGL window with the second renderer
598     eglWindow = createAndInitEGLWindow(secondRenderer);
599     if (eglWindow == nullptr)
600     {
601         FAIL() << "Failed to create EGL window";
602         return;
603     }
604 
605     program = glCreateProgram();
606     glProgramBinaryOES(program, binaryFormat, binary.data(), writtenLength);
607 
608     GLint linkStatus;
609     glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
610     EXPECT_EQ(expectedLinkResult, (linkStatus != 0));
611 
612     if (linkStatus != 0)
613     {
614         // If the link was successful, then we should try to draw using the program to ensure it works as expected
615         drawWithProgram(program);
616         EXPECT_GL_NO_ERROR();
617     }
618 
619     // Destroy the second renderer
620     glDeleteProgram(program);
621     destroyEGLWindow(&eglWindow);
622 }
623 
624 ANGLE_INSTANTIATE_TEST(ProgramBinariesAcrossPlatforms,
625                        //                     | Save the program   | Load the program      | Expected
626                        //                     | using these params | using these params    | link result
627                        PlatformsWithLinkResult(ES2_D3D11(),         ES2_D3D11(),            true         ), // Loading + reloading binary should work
628                        PlatformsWithLinkResult(ES3_D3D11(),         ES3_D3D11(),            true         ), // Loading + reloading binary should work
629                        PlatformsWithLinkResult(ES2_D3D11_FL11_0(),  ES2_D3D11_FL9_3(),      false        ), // Switching feature level shouldn't work
630                        PlatformsWithLinkResult(ES2_D3D11(),         ES2_D3D11_WARP(),       false        ), // Switching from hardware to software shouldn't work
631                        PlatformsWithLinkResult(ES2_D3D11_FL9_3(),   ES2_D3D11_FL9_3_WARP(), false        ), // Switching from hardware to software shouldn't work for FL9 either
632                        PlatformsWithLinkResult(ES2_D3D11(),         ES2_D3D9(),             false        ), // Switching from D3D11 to D3D9 shouldn't work
633                        PlatformsWithLinkResult(ES2_D3D9(),          ES2_D3D11(),            false        ), // Switching from D3D9 to D3D11 shouldn't work
634                        PlatformsWithLinkResult(ES2_D3D11(),         ES3_D3D11(),            true         )  // Switching to newer client version should work
635 
636                        // TODO: ANGLE issue 523
637                        // Compiling a program with client version 3, saving the binary, then loading it with client version 2 should not work
638                        // PlatformsWithLinkResult(ES3_D3D11(),         ES2_D3D11(),            false       )
639                        );
640