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 // BindUniformLocationTest.cpp : Tests of the GL_CHROMIUM_bind_uniform_location extension.
8 
9 #include "test_utils/ANGLETest.h"
10 
11 #include <cmath>
12 
13 using namespace angle;
14 
15 namespace
16 {
17 
18 class BindUniformLocationTest : public ANGLETest
19 {
20   protected:
BindUniformLocationTest()21     BindUniformLocationTest()
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         mBindUniformLocation = reinterpret_cast<PFNGLBINDUNIFORMLOCATIONCHROMIUMPROC>(
35             eglGetProcAddress("glBindUniformLocationCHROMIUM"));
36     }
37 
TearDown()38     void TearDown() override
39     {
40         if (mProgram != 0)
41         {
42             glDeleteProgram(mProgram);
43         }
44         ANGLETest::TearDown();
45     }
46 
47     typedef void(GL_APIENTRYP PFNGLBINDUNIFORMLOCATIONCHROMIUMPROC)(GLuint mProgram,
48                                                                     GLint location,
49                                                                     const GLchar *name);
50     PFNGLBINDUNIFORMLOCATIONCHROMIUMPROC mBindUniformLocation = nullptr;
51 
52     GLuint mProgram = 0;
53 };
54 
55 // Test basic functionality of GL_CHROMIUM_bind_uniform_location
TEST_P(BindUniformLocationTest,Basic)56 TEST_P(BindUniformLocationTest, Basic)
57 {
58     if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
59     {
60         std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
61                   << std::endl;
62         return;
63     }
64 
65     ASSERT_NE(mBindUniformLocation, nullptr);
66 
67     // clang-format off
68     const std::string vsSource = SHADER_SOURCE
69     (
70         attribute vec4 a_position;
71         void main()
72         {
73             gl_Position = a_position;
74         }
75     );
76 
77     const std::string fsSource = SHADER_SOURCE
78     (
79         precision mediump float;
80         uniform vec4 u_colorC;
81         uniform vec4 u_colorB[2];
82         uniform vec4 u_colorA;
83         void main()
84         {
85             gl_FragColor = u_colorA + u_colorB[0] + u_colorB[1] + u_colorC;
86         }
87     );
88     // clang-format on
89 
90     GLint colorALocation = 3;
91     GLint colorBLocation = 10;
92     GLint colorCLocation = 5;
93 
94     GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
95     GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
96 
97     mProgram = glCreateProgram();
98 
99     mBindUniformLocation(mProgram, colorALocation, "u_colorA");
100     mBindUniformLocation(mProgram, colorBLocation, "u_colorB[0]");
101     mBindUniformLocation(mProgram, colorCLocation, "u_colorC");
102 
103     glAttachShader(mProgram, vs);
104     glDeleteShader(vs);
105 
106     glAttachShader(mProgram, fs);
107     glDeleteShader(fs);
108 
109     // Link the mProgram
110     glLinkProgram(mProgram);
111     // Check the link status
112     GLint linked = 0;
113     glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
114     ASSERT_EQ(1, linked);
115 
116     glUseProgram(mProgram);
117 
118     static const float colorB[] = {
119         0.0f, 0.50f, 0.0f, 0.0f, 0.0f, 0.0f, 0.75f, 0.0f,
120     };
121 
122     glUniform4f(colorALocation, 0.25f, 0.0f, 0.0f, 0.0f);
123     glUniform4fv(colorBLocation, 2, colorB);
124     glUniform4f(colorCLocation, 0.0f, 0.0f, 0.0f, 1.0f);
125 
126     drawQuad(mProgram, "a_position", 0.5f);
127 
128     EXPECT_GL_NO_ERROR();
129     EXPECT_PIXEL_NEAR(0, 0, 64, 128, 192, 255, 1.0);
130 }
131 
132 // Test that conflicts are detected when two uniforms are bound to the same location
TEST_P(BindUniformLocationTest,ConflictsDetection)133 TEST_P(BindUniformLocationTest, ConflictsDetection)
134 {
135     if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
136     {
137         std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
138                   << std::endl;
139         return;
140     }
141 
142     ASSERT_NE(nullptr, mBindUniformLocation);
143 
144     // clang-format off
145     const std::string vsSource = SHADER_SOURCE
146     (
147         attribute vec4 a_position;
148         void main()
149         {
150             gl_Position = a_position;
151         }
152     );
153 
154     const std::string fsSource = SHADER_SOURCE
155     (
156         precision mediump float;
157         uniform vec4 u_colorA;
158         uniform vec4 u_colorB;
159         void main()
160         {
161             gl_FragColor = u_colorA + u_colorB;
162         }
163     );
164     // clang-format on
165 
166     GLint colorALocation = 3;
167     GLint colorBLocation = 4;
168 
169     GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
170     GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
171 
172     mProgram = glCreateProgram();
173     glAttachShader(mProgram, vs);
174     glDeleteShader(vs);
175     glAttachShader(mProgram, fs);
176     glDeleteShader(fs);
177 
178     mBindUniformLocation(mProgram, colorALocation, "u_colorA");
179     // Bind u_colorB to location a, causing conflicts, link should fail.
180     mBindUniformLocation(mProgram, colorALocation, "u_colorB");
181     glLinkProgram(mProgram);
182     GLint linked = 0;
183     glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
184     ASSERT_EQ(0, linked);
185 
186     // Bind u_colorB to location b, no conflicts, link should succeed.
187     mBindUniformLocation(mProgram, colorBLocation, "u_colorB");
188     glLinkProgram(mProgram);
189     linked = 0;
190     glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
191     EXPECT_EQ(1, linked);
192 }
193 
194 // Test a use case of the chromium compositor
TEST_P(BindUniformLocationTest,Compositor)195 TEST_P(BindUniformLocationTest, Compositor)
196 {
197     if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
198     {
199         std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
200                   << std::endl;
201         return;
202     }
203 
204     ASSERT_NE(nullptr, mBindUniformLocation);
205 
206     // clang-format off
207     const std::string vsSource = SHADER_SOURCE
208     (
209         attribute vec4 a_position;
210         attribute vec2 a_texCoord;
211         uniform mat4 matrix;
212         uniform vec2 color_a[4];
213         uniform vec4 color_b;
214         varying vec4 v_color;
215         void main()
216         {
217             v_color.xy = color_a[0] + color_a[1];
218             v_color.zw = color_a[2] + color_a[3];
219             v_color += color_b;
220             gl_Position = matrix * a_position;
221         }
222     );
223 
224     const std::string fsSource = SHADER_SOURCE
225     (
226         precision mediump float;
227         varying vec4 v_color;
228         uniform float alpha;
229         uniform vec4 multiplier;
230         uniform vec3 color_c[8];
231         void main()
232         {
233             vec4 color_c_sum = vec4(0.0);
234             color_c_sum.xyz += color_c[0];
235             color_c_sum.xyz += color_c[1];
236             color_c_sum.xyz += color_c[2];
237             color_c_sum.xyz += color_c[3];
238             color_c_sum.xyz += color_c[4];
239             color_c_sum.xyz += color_c[5];
240             color_c_sum.xyz += color_c[6];
241             color_c_sum.xyz += color_c[7];
242             color_c_sum.w = alpha;
243             color_c_sum *= multiplier;
244             gl_FragColor = v_color + color_c_sum;
245         }
246     );
247     // clang-format on
248 
249     int counter            = 6;
250     int matrixLocation     = counter++;
251     int colorALocation     = counter++;
252     int colorBLocation     = counter++;
253     int alphaLocation      = counter++;
254     int multiplierLocation = counter++;
255     int colorCLocation     = counter++;
256 
257     GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
258     GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
259 
260     mProgram = glCreateProgram();
261 
262     mBindUniformLocation(mProgram, matrixLocation, "matrix");
263     mBindUniformLocation(mProgram, colorALocation, "color_a");
264     mBindUniformLocation(mProgram, colorBLocation, "color_b");
265     mBindUniformLocation(mProgram, alphaLocation, "alpha");
266     mBindUniformLocation(mProgram, multiplierLocation, "multiplier");
267     mBindUniformLocation(mProgram, colorCLocation, "color_c");
268 
269     glAttachShader(mProgram, vs);
270     glDeleteShader(vs);
271     glAttachShader(mProgram, fs);
272     glDeleteShader(fs);
273 
274     // Link the mProgram
275     glLinkProgram(mProgram);
276     // Check the link status
277     GLint linked = 0;
278     glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
279     ASSERT_EQ(1, linked);
280 
281     glUseProgram(mProgram);
282 
283     static const float colorA[] = {
284         0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f,
285     };
286 
287     static const float colorC[] = {
288         0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f,
289         0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f, 0.1f,
290     };
291 
292     static const float identity[] = {
293         1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
294     };
295 
296     glUniformMatrix4fv(matrixLocation, 1, false, identity);
297     glUniform2fv(colorALocation, 4, colorA);
298     glUniform4f(colorBLocation, 0.2f, 0.2f, 0.2f, 0.2f);
299     glUniform1f(alphaLocation, 0.8f);
300     glUniform4f(multiplierLocation, 0.5f, 0.5f, 0.5f, 0.5f);
301     glUniform3fv(colorCLocation, 8, colorC);
302 
303     glDrawArrays(GL_TRIANGLES, 0, 6);
304 
305     drawQuad(mProgram, "a_position", 0.5f);
306 
307     EXPECT_PIXEL_EQ(0, 0, 204, 204, 204, 204);
308 }
309 
310 // Test that unused uniforms don't conflict when bound to the same location
TEST_P(BindUniformLocationTest,UnusedUniformUpdate)311 TEST_P(BindUniformLocationTest, UnusedUniformUpdate)
312 {
313     if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
314     {
315         std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
316                   << std::endl;
317         return;
318     }
319 
320     ASSERT_NE(nullptr, mBindUniformLocation);
321 
322     // clang-format off
323     const std::string vsSource = SHADER_SOURCE
324     (
325         attribute vec4 a_position;
326         void main()
327         {
328             gl_Position = a_position;
329         }
330     );
331 
332     const std::string fsSource = SHADER_SOURCE
333     (
334         precision mediump float;
335         uniform vec4 u_colorA;
336         uniform float u_colorU;
337         uniform vec4 u_colorC;
338         void main()
339         {
340             gl_FragColor = u_colorA + u_colorC;
341         }
342     );
343     // clang-format on
344 
345     const GLint colorULocation      = 1;
346     const GLint nonexistingLocation = 5;
347     const GLint unboundLocation     = 6;
348 
349     GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
350     GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
351 
352     mProgram = glCreateProgram();
353     mBindUniformLocation(mProgram, colorULocation, "u_colorU");
354     // The non-existing uniform should behave like existing, but optimized away
355     // uniform.
356     mBindUniformLocation(mProgram, nonexistingLocation, "nonexisting");
357     // Let A and C be assigned automatic locations.
358     glAttachShader(mProgram, vs);
359     glDeleteShader(vs);
360     glAttachShader(mProgram, fs);
361     glDeleteShader(fs);
362     glLinkProgram(mProgram);
363     GLint linked = 0;
364     glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
365     ASSERT_EQ(1, linked);
366     glUseProgram(mProgram);
367 
368     // No errors on bound locations, since caller does not know
369     // if the driver optimizes them away or not.
370     glUniform1f(colorULocation, 0.25f);
371     EXPECT_GL_NO_ERROR();
372 
373     // No errors on bound locations of names that do not exist
374     // in the shader. Otherwise it would be inconsistent wrt the
375     // optimization case.
376     glUniform1f(nonexistingLocation, 0.25f);
377     EXPECT_GL_NO_ERROR();
378 
379     // The above are equal to updating -1.
380     glUniform1f(-1, 0.25f);
381     EXPECT_GL_NO_ERROR();
382 
383     // No errors when updating with other type either.
384     // The type can not be known with the non-existing case.
385     glUniform2f(colorULocation, 0.25f, 0.25f);
386     EXPECT_GL_NO_ERROR();
387     glUniform2f(nonexistingLocation, 0.25f, 0.25f);
388     EXPECT_GL_NO_ERROR();
389     glUniform2f(-1, 0.25f, 0.25f);
390     EXPECT_GL_NO_ERROR();
391 
392     // Ensure that driver or ANGLE has optimized the variable
393     // away and the test tests what it is supposed to.
394     EXPECT_EQ(-1, glGetUniformLocation(mProgram, "u_colorU"));
395 
396     // The bound location gets marked as used and the driver
397     // does not allocate other variables to that location.
398     EXPECT_NE(colorULocation, glGetUniformLocation(mProgram, "u_colorA"));
399     EXPECT_NE(colorULocation, glGetUniformLocation(mProgram, "u_colorC"));
400     EXPECT_NE(nonexistingLocation, glGetUniformLocation(mProgram, "u_colorA"));
401     EXPECT_NE(nonexistingLocation, glGetUniformLocation(mProgram, "u_colorC"));
402 
403     // Unintuitive: while specifying value works, getting the value does not.
404     GLfloat getResult = 0.0f;
405     glGetUniformfv(mProgram, colorULocation, &getResult);
406     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
407     glGetUniformfv(mProgram, nonexistingLocation, &getResult);
408     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
409     glGetUniformfv(mProgram, -1, &getResult);
410     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
411 
412     // Updating an unbound, non-existing location still causes
413     // an error.
414     glUniform1f(unboundLocation, 0.25f);
415     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
416 }
417 
418 // Test for a bug where using a sampler caused GL error if the mProgram had
419 // uniforms that were optimized away by the driver. This was only a problem with
420 // glBindUniformLocationCHROMIUM implementation. This could be reproed by
421 // binding the sampler to a location higher than the amount of active uniforms.
TEST_P(BindUniformLocationTest,UseSamplerWhenUnusedUniforms)422 TEST_P(BindUniformLocationTest, UseSamplerWhenUnusedUniforms)
423 {
424     if (!extensionEnabled("GL_CHROMIUM_bind_uniform_location"))
425     {
426         std::cout << "Test skipped because GL_CHROMIUM_bind_uniform_location is not available."
427                   << std::endl;
428         return;
429     }
430 
431     ASSERT_NE(nullptr, mBindUniformLocation);
432 
433     // clang-format off
434     const std::string vsSource = SHADER_SOURCE
435     (
436         void main()
437         {
438             gl_Position = vec4(0);
439         }
440     );
441 
442     const std::string fsSource = SHADER_SOURCE
443     (
444         uniform sampler2D tex;
445         void main()
446         {
447             gl_FragColor = texture2D(tex, vec2(1));
448         }
449     );
450     // clang-format on
451 
452     const GLuint texLocation = 54;
453 
454     GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
455     GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
456 
457     mProgram = glCreateProgram();
458     mBindUniformLocation(mProgram, texLocation, "tex");
459 
460     glAttachShader(mProgram, vs);
461     glDeleteShader(vs);
462     glAttachShader(mProgram, fs);
463     glDeleteShader(fs);
464 
465     glLinkProgram(mProgram);
466 
467     GLint linked = 0;
468     glGetProgramiv(mProgram, GL_LINK_STATUS, &linked);
469     EXPECT_NE(0, linked);
470     glUseProgram(mProgram);
471     glUniform1i(texLocation, 0);
472     EXPECT_GL_NO_ERROR();
473 }
474 
475 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
476 // tests should be run against.
477 ANGLE_INSTANTIATE_TEST(BindUniformLocationTest,
478                        ES2_D3D9(),
479                        ES2_D3D11(),
480                        ES2_D3D11_FL9_3(),
481                        ES2_OPENGL(),
482                        ES2_OPENGLES());
483 
484 }  // namespace
485