diff options
Diffstat (limited to '')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js b/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js new file mode 100644 index 0000000000..1202b7868c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/js/tests/no-over-optimizations-on-uniform-array.js @@ -0,0 +1,247 @@ +/* +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +*/ +NoOverOptimizeOnUniformArrayTester = (function(){ + +var vshader = [ + "attribute vec4 a_position;", + "void main()", + "{", + " gl_Position = a_position;", + "}" +].join('\n'); + +var fshader_max = [ + "precision mediump float;", + "uniform vec4 colora[$(maxUniformVectors)];", + "void main()", + "{", + " gl_FragColor = vec4(colora[$(usedUniformVector)]);", + "}" +].join('\n'); + +var fshader_max_ab_ab = [ + "precision mediump float;", + "uniform vec4 $(decl1);", + "uniform vec4 $(decl2);", + "void main()", + "{", + "gl_FragColor = vec4($(usage1) + $(usage2));", + "}" +].join('\n'); + +// MaxInt32 is 2^32-1. We need +1 of that to test overflow conditions +var MaxInt32PlusOne = 4294967296; + +function setupTests(gl) { + var tests = []; + var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); + + // This test is to test drivers the have bugs related to optimizing + // an array of uniforms when only 1 of those uniforms is used. + tests.push({ + desc: "using last element", + maxUniformVectors: maxUniformVectors, + usedUniformVector: maxUniformVectors - 1, + shader: "fshader-max", + color: [0, 1, 0, 1], + arrayName: "colora", + extraName: "colorb", + }); + tests.push({ + desc: "using first element", + maxUniformVectors: maxUniformVectors, + usedUniformVector: 0, + shader: "fshader-max", + color: [0, 1, 0, 1], + arrayName: "colora", + extraName: "colorb", + }); + + // Generate test shaders. We're trying to force the driver to + // overflow from 1 array into the next if it optimizes. So for example if it was C + // + // int big[4]; + // int little[1]; + // big[5] = 124; + // + // Would end up setting little[0] instead of big. Some drivers optimize + // where if you only use say 'big[3]' it will actually only allocate just 1 element + // for big. + // + // But, some drivers have a bug where the fact that they optimized big to 1 element + // does not get passed down to glUniform so when setting the uniform 'big[3]' they + // overwrite memory. + // + // If the driver crashes, yea. We found a bug. We can block the driver. + // Otherwise we try various combinations so that setting 'little[0]' first + // and then setting all elements of 'big' we hope it will overwrite 'little[0]' + // which will show the bug and again we can block the driver. + // + // We don't know how the driver will order, in memory, the various uniforms + // or for that matter we don't even know if they will be contiguous in memory + // but to hopefully expose any bugs we try various combinations. + // + // It could be the compiler orders uniforms alphabetically. + // It could be it orders them in order of declaration. + // It could be it orders them in order of usage. + // + // We also test using only first element of big or just the last element of big. + // + for (var nameOrder = 0; nameOrder < 2; ++nameOrder) { + var name1 = nameOrder ? "colora" : "colorb"; + var name2 = nameOrder ? "colorb" : "colora"; + for (var last = 0; last < 2; ++last) { + var usedUniformVector = last ? maxUniformVectors - 2 : 0; + for (var declOrder = 0; declOrder < 2; ++declOrder) { + var bigName = declOrder ? name1 : name2; + var littleName = declOrder ? name2 : name1; + var decl1 = bigName + "[" + (maxUniformVectors - 1) + "]"; + var decl2 = littleName + "[1]"; + if (declOrder) { + var t = decl1; + decl1 = decl2; + decl2 = t; + } + for (var usageOrder = 0; usageOrder < 2; ++usageOrder) { + var usage1 = bigName + "[" + usedUniformVector + "]"; + var usage2 = littleName + "[0]"; + if (usageOrder) { + var t = usage1; + usage1 = usage2; + usage2 = t; + } + var fSrc = wtu.replaceParams(fshader_max_ab_ab, { + decl1: decl1, + decl2: decl2, + usage1: usage1, + usage2: usage2, + }); + var desc = "testing: " + name1 + ":" + name2 + " using " + (last ? "last" : "first") + + " creating uniforms " + decl1 + " " + decl2 + " and accessing " + usage1 + " " + usage2; + tests.push({ + desc: desc, + maxUniformVectors: maxUniformVectors - 1, + usedUniformVector: usedUniformVector, + source: fSrc, + color: [0, 0, 0, 1], + arrayName: bigName, + extraName: littleName, + }); + } + } + } + } + return tests; +}; + +function testUniformOptimizationIssues(test) { + debug(""); + debug(test.desc); + var fshader = test.source; + if (!fshader) { + fshader = wtu.replaceParams(fshader_max, test); + } + + var consoleElem = document.getElementById("console"); + wtu.addShaderSource( + consoleElem, "vertex shader", vshader); + wtu.addShaderSource( + consoleElem, "fragment shader", fshader); + + var program = wtu.loadProgram(gl, vshader, fshader); + gl.useProgram(program); + + var colorbLocation = gl.getUniformLocation(program, test.extraName + "[0]"); + if (colorbLocation) { + gl.uniform4fv(colorbLocation, [0, 1, 0, 0]); + } + + // Ensure that requesting an array uniform past MaxInt32PlusOne returns no uniform + var nameMaxInt32PlusOne = test.arrayName + "[" + (test.usedUniformVector + MaxInt32PlusOne) + "]"; + assertMsg(gl.getUniformLocation(program, nameMaxInt32PlusOne) === null, + "Requesting " + nameMaxInt32PlusOne + " uniform should return a null uniform location"); + + // Set just the used uniform + var name = test.arrayName + "[" + test.usedUniformVector + "]"; + var uniformLocation = gl.getUniformLocation(program, name); + gl.uniform4fv(uniformLocation, test.color); + wtu.setupIndexedQuad(gl, 1); + wtu.clearAndDrawIndexedQuad(gl, 1); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + + // Set all the unused uniforms + var locations = []; + var allRequiredUniformLocationsQueryable = true; + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + var name = test.arrayName + "[" + ii + "]"; + var uniformLocation = gl.getUniformLocation(program, name); + locations.push(uniformLocation); + if (ii == test.usedUniformVector) { + continue; + } + // Locations > usedUnformVector may not exist. + // Locations <= usedUniformVector MUST exist. + if (ii <= test.usedUniformVector && (uniformLocation === undefined || uniformLocation === null)) { + allRequiredUniformLocationsQueryable = false; + } + gl.uniform4fv(uniformLocation, [1, 0, 0, 1]); + } + if (allRequiredUniformLocationsQueryable) { + testPassed("allRequiredUniformLocationsQueryable is true."); + } + else { + testFailed("allRequiredUniformLocationsQueryable should be true. Was false."); + } + var positionLoc = gl.getAttribLocation(program, "a_position"); + wtu.setupIndexedQuad(gl, 1, positionLoc); + wtu.clearAndDrawIndexedQuad(gl, 1); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + + // Check we can read & write each uniform. + // Note: uniforms past test.usedUniformVector might not exist. + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + gl.uniform4fv(locations[ii], [ii + 4, ii + 2, ii + 3, ii + 1]); + } + + var kEpsilon = 0.01; + var isSame = function(v1, v2) { + return Math.abs(v1 - v2) < kEpsilon; + }; + + for (var ii = 0; ii < test.maxUniformVectors; ++ii) { + var location = locations[ii]; + if (location) { + var value = gl.getUniform(program, locations[ii]); + if (!isSame(value[0], ii + 4) || + !isSame(value[1], ii + 2) || + !isSame(value[2], ii + 3) || + !isSame(value[3], ii + 1)) { + testFailed("location: " + ii + " was not correct value"); + break; + } + } + } +} + +function runOneTest(gl, test) { + testUniformOptimizationIssues(test); +}; + +function runTests(gl, tests) { + debug(""); + debug("Test drivers don't over optimize unused array elements"); + + for (var ii = 0; ii < tests.length; ++ii) { + runOneTest(gl, tests[ii]); + } +}; + +return { + setupTests : setupTests, + runTests : runTests +}; + +}()); |