4 ** Copyright (c) 2012 The Khronos Group Inc.
6 ** Permission is hereby granted, free of charge, to any person obtaining a
7 ** copy of this software and/or associated documentation files (the
8 ** "Materials"), to deal in the Materials without restriction, including
9 ** without limitation the rights to use, copy, modify, merge, publish,
10 ** distribute, sublicense, and/or sell copies of the Materials, and to
11 ** permit persons to whom the Materials are furnished to do so, subject to
12 ** the following conditions:
14 ** The above copyright notice and this permission notice shall be included
15 ** in all copies or substantial portions of the Materials.
17 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
31 <meta charset="utf-8">
32 <title>WebGL uniform array Conformance Tests</title>
33 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
34 <script src="../../resources/js-test-pre.js"></script>
35 <script src="../resources/webgl-test-utils.js"></script>
38 <div id="description"></div>
39 <div id="console"></div>
40 <canvas id="example" width="2" height="2"> </canvas>
41 <script id="vshader" type="x-shader/x-vertex">
42 attribute vec4 a_position;
45 gl_Position = a_position;
49 <script id="fshader" type="x-shader/x-fragment">
50 precision mediump float;
51 uniform $(type) color[3];
54 gl_FragColor = vec4(color[0]$(elem), color[1]$(elem), color[2]$(elem), 1);
57 <script id="fshader-max" type="x-shader/x-fragment">
58 precision mediump float;
59 uniform vec4 colora[$(maxUniformVectors)];
62 gl_FragColor = vec4(colora[$(usedUniformVector)]);
65 <script id="fshader-max-ab-ab" type="x-shader/x-fragment">
66 precision mediump float;
67 uniform vec4 $(decl1);
68 uniform vec4 $(decl2);
71 gl_FragColor = vec4($(usage1) + $(usage2));
78 var wtu = WebGLTestUtils;
79 var gl = wtu.create3DContext("example");
81 var vSrc = wtu.getScript("vshader");
82 var fTemplate = wtu.getScript("fshader");
90 invalidSet: function(loc) {
91 gl.uniform2fv(loc, [1, 2]);
93 srcValueAsString: function(index, srcValues) {
94 return srcValues[index].toString();
96 returnValueAsString: function(value) {
97 return value === null ? 'null' : value.toString();
99 checkType: function(value) {
100 return typeof value === 'number';
102 checkValue: function(typeInfo, index, value) {
103 return typeInfo.srcValues[index] == value;
105 srcValues: [16, 15, 14],
107 srcValuesLessMultiple: [16],
108 srcValuesMoreMultiple: [16, 15, 14, 13],
109 srcValuesNonMultiple: null,
112 jsTypeOf: 'Float32Array',
113 setter: 'uniform2fv',
116 invalidSet: function(loc) {
117 gl.uniform1fv(loc, [2]);
119 illegalSet: function(loc) {
120 gl.uniform1fv(loc, 2);
122 srcValueAsString: function(index, srcValues) {
123 return "[" + srcValues[index * 2 + 0].toString() + ", " +
124 srcValues[index * 2 + 1].toString() + "]";
126 returnValueAsString: function(value) {
127 return value === null ? 'null' : ("[" + value[0] + ", " + value[1] + "]");
129 checkType: function(value) {
131 typeof value.length === 'number' &&
134 checkValue: function(typeInfo, index, value) {
135 return value !== null &&
136 typeInfo.srcValues[index * 2 + 0] == value[0] &&
137 typeInfo.srcValues[index * 2 + 1] == value[1];
139 srcValues: [16, 15, 14, 13, 12, 11],
141 srcValuesLessMultiple: [16, 15, 14, 13],
142 srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
143 srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10],
146 jsTypeOf: 'Float32Array',
147 setter: 'uniform3fv',
150 invalidSet: function(loc) {
151 gl.uniform1fv(loc, [2]);
153 illegalSet: function(loc) {
154 gl.uniform1fv(loc, 2);
156 srcValueAsString: function(index, srcValues) {
157 return "[" + srcValues[index * 3 + 0].toString() + ", " +
158 srcValues[index * 3 + 1].toString() + ", " +
159 srcValues[index * 3 + 2].toString() + "]";
161 returnValueAsString: function(value) {
162 return value === null ? 'null' :
163 ("[" + value[0] + ", " + value[1] + ", " + value[2] + "]");
165 checkType: function(value) {
167 typeof value.length === 'number' &&
170 checkValue: function(typeInfo, index, value) {
171 return value !== null &&
172 typeInfo.srcValues[index * 3 + 0] == value[0] &&
173 typeInfo.srcValues[index * 3 + 1] == value[1] &&
174 typeInfo.srcValues[index * 3 + 2] == value[2];
176 srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8],
177 srcValuesLess: [16, 15],
178 srcValuesLessMultiple: [16, 15, 14, 13, 12, 11],
179 srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2],
180 srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7],
183 jsTypeOf: 'Float32Array',
184 setter: 'uniform4fv',
187 invalidSet: function(loc) {
188 gl.uniform1fv(loc, [2]);
190 illegalSet: function(loc) {
191 gl.uniform1fv(loc, 2);
193 srcValueAsString: function(index, srcValues) {
194 return "[" + srcValues[index * 4 + 0].toString() + ", " +
195 srcValues[index * 4 + 1].toString() + ", " +
196 srcValues[index * 4 + 2].toString() + ", " +
197 srcValues[index * 4 + 3].toString() + "]";
199 returnValueAsString: function(value) {
200 return value === null ? 'null' :
201 ("[" + value[0] + ", " + value[1] +
202 ", " + value[2] + ", " + value[3] + "]");
204 checkType: function(value) {
206 typeof value.length === 'number' &&
209 checkValue: function(typeInfo, index, value) {
210 return value !== null &&
211 typeInfo.srcValues[index * 4 + 0] == value[0] &&
212 typeInfo.srcValues[index * 4 + 1] == value[1] &&
213 typeInfo.srcValues[index * 4 + 2] == value[2] &&
214 typeInfo.srcValues[index * 4 + 3] == value[3];
216 srcValues: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5],
217 srcValuesLess: [16, 15, 14],
218 srcValuesLessMultiple: [16, 15, 14, 13, 12, 11, 10, 9],
219 srcValuesMoreMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1],
220 srcValuesNonMultiple: [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4],
224 for (var tt = 0; tt < typeInfos.length; ++tt) {
225 var typeInfo = typeInfos[tt];
227 debug("check " + typeInfo.type);
228 var fSrc = wtu.replaceParams(fTemplate, typeInfo);
229 //debug("fSrc: " + fSrc);
230 var program = wtu.loadProgram(gl, vSrc, fSrc);
232 var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
233 assertMsg(numUniforms == 1, "1 uniform found");
234 var info = gl.getActiveUniform(program, 0);
235 assertMsg(info.name == "color[0]",
236 "uniform name is 'color[0]' not 'color' as per OpenGL ES 2.0.24 section 2.10");
237 var loc = gl.getUniformLocation(program, "color[0]");
238 var srcValues = typeInfo.srcValues;
239 var srcValuesLess = typeInfo.srcValuesLess;
240 var srcValuesLessMultiple = typeInfo.srcValuesLessMultiple;
241 var srcValuesMoreMultiple = typeInfo.srcValuesMoreMultiple;
242 var srcValuesNonMultiple = typeInfo.srcValuesNonMultiple;
244 // Try setting the value before using the program
245 gl[typeInfo.setter](loc, srcValues);
246 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
247 "should fail if there is no current program");
249 gl.useProgram(program);
250 gl[typeInfo.setter](loc, srcValuesLess);
251 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
252 "should fail with insufficient array size with gl." + typeInfo.setter);
253 if (srcValuesNonMultiple) {
254 gl[typeInfo.setter](loc, srcValuesNonMultiple);
255 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE,
256 "should fail with non-multiple array size with gl." + typeInfo.setter);
258 gl[typeInfo.setter](loc, srcValues);
259 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
260 "can set an array of uniforms with gl." + typeInfo.setter);
261 gl[typeInfo.setter](loc, srcValuesLessMultiple);
262 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
263 "can partially set an array of uniforms with gl." + typeInfo.setter + " with a smaller array");
264 gl[typeInfo.setter](loc, srcValuesMoreMultiple);
265 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
266 "can set an array of uniforms with gl." + typeInfo.setter + " with a larger array");
268 var values = gl.getUniform(program, loc);
269 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
270 "can call gl.getUniform");
271 assertMsg(typeInfo.checkType(values),
272 "gl.getUniform returns the correct type.");
273 for (var ii = 0; ii < typeInfo.numSrcValues; ++ii) {
274 var elemLoc = gl.getUniformLocation(program, "color[" + ii + "]");
275 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
276 "can get location of element " + ii +
277 " of array from gl.getUniformLocation");
278 var value = gl.getUniform(program, elemLoc);
279 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
280 "can get value of element " + ii + " of array from gl.getUniform");
281 assertMsg(typeInfo.checkValue(typeInfo, ii, value),
282 "value put in (" + typeInfo.srcValueAsString(ii, srcValues) +
283 ") matches value pulled out (" +
284 typeInfo.returnValueAsString(value) + ")");
286 typeInfo.invalidSet(loc);
287 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION,
288 "using the wrong size of gl.Uniform fails");
289 var exceptionCaught = false;
290 if (typeInfo.illegalSet) {
292 typeInfo.illegalSet(loc);
294 exceptionCaught = true;
296 assertMsg(exceptionCaught, "passing non-array to glUniform*fv should throw TypeError");
300 wtu.glErrorShouldBe(gl, gl.NO_ERROR,
301 "can call gl.useProgram(null)");
304 // This test is to test drivers the have bugs related to optimizing
305 // an array of uniforms when only 1 of those uniforms is used.
307 debug("Test drivers don't over optimize unused array elements");
308 var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
310 { desc: "using last element",
311 maxUniformVectors: maxUniformVectors,
312 usedUniformVector: maxUniformVectors - 1,
313 shader: "fshader-max",
318 { desc: "using first element",
319 maxUniformVectors: maxUniformVectors,
320 usedUniformVector: 0,
321 shader: "fshader-max",
328 // Generate test shaders. We're trying to force the driver to
329 // overflow from 1 array into the next if it optimizes. So for example if it was C
335 // Would end up setting little[0] instead of big. Some drivers optimize
336 // where if you only use say 'big[3]' it will actually only allocate just 1 element
339 // But, some drivers have a bug where the fact that they optimized big to 1 element
340 // does not get passed down to glUniform so when setting the uniform 'big[3]' they
343 // If the driver crashes, yea. We found a bug. We can blacklist the driver.
344 // Otherwise we try various combinations so that setting 'little[0]' first
345 // and then setting all elements of 'big' we hope it will overwrite 'little[0]'
346 // which will show the bug and again we can blacklist the driver.
348 // We don't know how the driver will order, in memory, the various uniforms
349 // or for that matter we don't even know if they will be contiguous in memory
350 // but to hopefully expose any bugs we try various combinations.
352 // It could be the compiler orders uniforms alphabetically.
353 // It could be it orders them in order of declaration.
354 // It could be it orders them in order of usage.
356 // We also test using only first element of big or just the last element of big.
358 for (var nameOrder = 0; nameOrder < 2; ++nameOrder) {
359 var name1 = nameOrder ? "colora" : "colorb";
360 var name2 = nameOrder ? "colorb" : "colora";
361 for (var last = 0; last < 2; ++last) {
362 var usedUniformVector = last ? maxUniformVectors - 2 : 0;
363 for (var declOrder = 0; declOrder < 2; ++declOrder) {
364 var bigName = declOrder ? name1 : name2;
365 var littleName = declOrder ? name2 : name1;
366 var decl1 = bigName + "[" + (maxUniformVectors - 1) + "]";
367 var decl2 = littleName + "[1]";
373 for (var usageOrder = 0; usageOrder < 2; ++usageOrder) {
374 var usage1 = bigName + "[" + usedUniformVector + "]";
375 var usage2 = littleName + "[0]";
381 var fSrc = wtu.replaceParams(wtu.getScript("fshader-max-ab-ab"), {
387 var desc = "testing: " + name1 + ":" + name2 + " using " + (last ? "last" : "first") +
388 " creating uniforms " + decl1 + " " + decl2 + " and accessing " + usage1 + " " + usage2;
391 maxUniformVectors: maxUniformVectors - 1,
392 usedUniformVector: usedUniformVector,
396 extraName: littleName,
403 var allRequiredUniformLocationsQueryable = true;
404 function testUniformOptimizationIssues(testIndex) {
405 var test = tests[testIndex];
408 var fSrc = test.source;
410 fSrc = wtu.replaceParams(wtu.getScript(test.shader), test);
413 var consoleElem = document.getElementById("console");
415 consoleElem, "vertex shader", vSrc);
417 consoleElem, "fragment shader", fSrc);
419 var program = wtu.loadProgram(gl, vSrc, fSrc);
420 gl.useProgram(program);
422 var colorbLocation = gl.getUniformLocation(program, test.extraName + "[0]");
423 if (colorbLocation) {
424 gl.uniform4fv(colorbLocation, [0, 1, 0, 0]);
427 // Set just the used uniform
428 var name = test.arrayName + "[" + test.usedUniformVector + "]";
429 var uniformLocation = gl.getUniformLocation(program, name);
430 gl.uniform4fv(uniformLocation, test.color);
431 wtu.setupIndexedQuad(gl, 1, positionLoc);
432 wtu.clearAndDrawIndexedQuad(gl, 1);
433 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
435 // Set all the unused uniforms
437 allRequiredUniformLocationsQueryable = true;
438 for (var ii = 0; ii < test.maxUniformVectors; ++ii) {
439 var name = test.arrayName + "[" + ii + "]";
440 var uniformLocation = gl.getUniformLocation(program, name);
441 locations.push(uniformLocation);
442 if (ii == test.usedUniformVector) {
445 // Locations > usedUnformVector may not exist.
446 // Locations <= usedUniformVector MUST exist.
447 if (ii <= test.usedUniformVector && (uniformLocation === undefined || uniformLocation === null)) {
448 allRequiredUniformLocationsQueryable = false;
450 gl.uniform4fv(uniformLocation, [1, 0, 0, 1]);
452 shouldBeTrue("allRequiredUniformLocationsQueryable");
453 var positionLoc = gl.getAttribLocation(program, "a_position");
454 wtu.setupIndexedQuad(gl, 1, positionLoc);
455 wtu.clearAndDrawIndexedQuad(gl, 1);
456 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
458 // Check we can read & write each uniform.
459 // Note: uniforms past test.usedUniformVector might not exist.
460 for (var ii = 0; ii < test.maxUniformVectors; ++ii) {
461 gl.uniform4fv(locations[ii], [ii + 4, ii + 2, ii + 3, ii + 1]);
465 var isSame = function(v1, v2) {
466 return Math.abs(v1 - v2) < kEpsilon;
469 for (var ii = 0; ii < test.maxUniformVectors; ++ii) {
470 var location = locations[ii];
472 var value = gl.getUniform(program, locations[ii]);
473 if (!isSame(value[0], ii + 4) ||
474 !isSame(value[1], ii + 2) ||
475 !isSame(value[2], ii + 3) ||
476 !isSame(value[3], ii + 1)) {
477 testFailed("location: " + ii + " was not correct value");
485 function runNextTest() {
486 testUniformOptimizationIssues(testIndex++);
487 if (testIndex < tests.length) {
488 setTimeout(runNextTest, 0);
490 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
497 var successfullyParsed = true;