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