tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

no-over-optimizations-on-uniform-array.js (8974B)


      1 /*
      2 Copyright (c) 2019 The Khronos Group Inc.
      3 Use of this source code is governed by an MIT-style license that can be
      4 found in the LICENSE.txt file.
      5 */
      6 NoOverOptimizeOnUniformArrayTester = (function(){
      7 
      8 var vshader = [
      9    "attribute vec4 a_position;",
     10    "void main()",
     11    "{",
     12    "    gl_Position = a_position;",
     13    "}"
     14 ].join('\n');
     15 
     16 var fshader_max = [
     17    "precision mediump float;",
     18    "uniform vec4 colora[$(maxUniformVectors)];",
     19    "void main()",
     20    "{",
     21    "    gl_FragColor = vec4(colora[$(usedUniformVector)]);",
     22    "}"
     23 ].join('\n');
     24 
     25 var fshader_max_ab_ab = [
     26    "precision mediump float;",
     27    "uniform vec4 $(decl1);",
     28    "uniform vec4 $(decl2);",
     29    "void main()",
     30    "{",
     31    "gl_FragColor = vec4($(usage1) + $(usage2));",
     32    "}"
     33 ].join('\n');
     34 
     35 // MaxInt32 is 2^32-1. We need +1 of that to test overflow conditions
     36 var MaxInt32PlusOne = 4294967296;
     37 
     38 function setupTests(gl) {
     39    var tests = [];
     40    var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
     41 
     42    // This test is to test drivers the have bugs related to optimizing
     43    // an array of uniforms when only 1 of those uniforms is used.
     44    tests.push({
     45        desc: "using last element",
     46        maxUniformVectors: maxUniformVectors,
     47        usedUniformVector: maxUniformVectors - 1,
     48        shader: "fshader-max",
     49        color: [0, 1, 0, 1],
     50        arrayName: "colora",
     51        extraName: "colorb",
     52    });
     53    tests.push({
     54        desc: "using first element",
     55        maxUniformVectors: maxUniformVectors,
     56        usedUniformVector: 0,
     57        shader: "fshader-max",
     58        color: [0, 1, 0, 1],
     59        arrayName: "colora",
     60        extraName: "colorb",
     61    });
     62 
     63    // Generate test shaders. We're trying to force the driver to
     64    // overflow from 1 array into the next if it optimizes. So for example if it was C
     65    //
     66    //   int big[4];
     67    //   int little[1];
     68    //   big[5] = 124;
     69    //
     70    // Would end up setting little[0] instead of big. Some drivers optimize
     71    // where if you only use say 'big[3]' it will actually only allocate just 1 element
     72    // for big.
     73    //
     74    // But, some drivers have a bug where the fact that they optimized big to 1 element
     75    // does not get passed down to glUniform so when setting the uniform 'big[3]' they
     76    // overwrite memory.
     77    //
     78    // If the driver crashes, yea. We found a bug. We can block the driver.
     79    // Otherwise we try various combinations so that setting 'little[0]' first
     80    // and then setting all elements of 'big' we hope it will overwrite 'little[0]'
     81    // which will show the bug and again we can block the driver.
     82    //
     83    // We don't know how the driver will order, in memory, the various uniforms
     84    // or for that matter we don't even know if they will be contiguous in memory
     85    // but to hopefully expose any bugs we try various combinations.
     86    //
     87    //    It could be the compiler orders uniforms alphabetically.
     88    //    It could be it orders them in order of declaration.
     89    //    It could be it orders them in order of usage.
     90    //
     91    // We also test using only first element of big or just the last element of big.
     92    //
     93    for (var nameOrder = 0; nameOrder < 2; ++nameOrder) {
     94        var name1 = nameOrder ? "colora" : "colorb";
     95        var name2 = nameOrder ? "colorb" : "colora";
     96        for (var last = 0; last < 2; ++last) {
     97            var usedUniformVector = last ? maxUniformVectors - 2 : 0;
     98            for (var declOrder = 0; declOrder < 2; ++declOrder) {
     99                var bigName    = declOrder ? name1 : name2;
    100                var littleName = declOrder ? name2 : name1;
    101                var decl1 = bigName + "[" + (maxUniformVectors - 1) + "]";
    102                var decl2 = littleName + "[1]";
    103                if (declOrder) {
    104                    var t = decl1;
    105                    decl1 = decl2;
    106                    decl2 = t;
    107                }
    108                for (var usageOrder = 0; usageOrder < 2; ++usageOrder) {
    109                    var usage1 = bigName + "[" + usedUniformVector + "]";
    110                    var usage2 = littleName + "[0]";
    111                    if (usageOrder) {
    112                        var t = usage1;
    113                        usage1 = usage2;
    114                        usage2 = t;
    115                    }
    116                    var fSrc = wtu.replaceParams(fshader_max_ab_ab, {
    117                        decl1: decl1,
    118                        decl2: decl2,
    119                        usage1: usage1,
    120                        usage2: usage2,
    121                    });
    122                    var desc = "testing: " + name1 + ":" + name2 + " using " + (last ? "last" : "first") +
    123                        " creating uniforms " + decl1 + " " + decl2 + " and accessing " + usage1 + " " + usage2;
    124                    tests.push({
    125                        desc: desc,
    126                        maxUniformVectors: maxUniformVectors - 1,
    127                        usedUniformVector: usedUniformVector,
    128                        source: fSrc,
    129                        color: [0, 0, 0, 1],
    130                        arrayName: bigName,
    131                        extraName: littleName,
    132                    });
    133                }
    134            }
    135        }
    136    }
    137    return tests;
    138 };
    139 
    140 function testUniformOptimizationIssues(test) {
    141    debug("");
    142    debug(test.desc);
    143    var fshader = test.source;
    144    if (!fshader) {
    145        fshader = wtu.replaceParams(fshader_max, test);
    146    }
    147 
    148    var consoleElem = document.getElementById("console");
    149    wtu.addShaderSource(
    150        consoleElem, "vertex shader", vshader);
    151    wtu.addShaderSource(
    152        consoleElem, "fragment shader", fshader);
    153 
    154    var program = wtu.loadProgram(gl, vshader, fshader);
    155    gl.useProgram(program);
    156 
    157    var colorbLocation = gl.getUniformLocation(program, test.extraName + "[0]");
    158    if (colorbLocation) {
    159        gl.uniform4fv(colorbLocation, [0, 1, 0, 0]);
    160    }
    161 
    162    // Ensure that requesting an array uniform past MaxInt32PlusOne returns no uniform
    163    var nameMaxInt32PlusOne = test.arrayName + "[" + (test.usedUniformVector + MaxInt32PlusOne) + "]";
    164    assertMsg(gl.getUniformLocation(program, nameMaxInt32PlusOne) === null,
    165        "Requesting " + nameMaxInt32PlusOne + " uniform should return a null uniform location");
    166 
    167    // Set just the used uniform
    168    var name = test.arrayName + "[" + test.usedUniformVector + "]";
    169    var uniformLocation = gl.getUniformLocation(program, name);
    170    gl.uniform4fv(uniformLocation, test.color);
    171    wtu.setupIndexedQuad(gl, 1);
    172    wtu.clearAndDrawIndexedQuad(gl, 1);
    173    wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
    174 
    175    // Set all the unused uniforms
    176    var locations = [];
    177    var allRequiredUniformLocationsQueryable = true;
    178    for (var ii = 0; ii < test.maxUniformVectors; ++ii) {
    179        var name = test.arrayName + "[" + ii + "]";
    180        var uniformLocation = gl.getUniformLocation(program, name);
    181        locations.push(uniformLocation);
    182        if (ii == test.usedUniformVector) {
    183            continue;
    184        }
    185        // Locations > usedUnformVector may not exist.
    186        // Locations <= usedUniformVector MUST exist.
    187        if (ii <= test.usedUniformVector && (uniformLocation === undefined || uniformLocation === null)) {
    188            allRequiredUniformLocationsQueryable = false;
    189        }
    190        gl.uniform4fv(uniformLocation, [1, 0, 0, 1]);
    191    }
    192    if (allRequiredUniformLocationsQueryable) {
    193        testPassed("allRequiredUniformLocationsQueryable is true.");
    194    }
    195    else {
    196        testFailed("allRequiredUniformLocationsQueryable should be true. Was false.");
    197    }
    198    var positionLoc = gl.getAttribLocation(program, "a_position");
    199    wtu.setupIndexedQuad(gl, 1, positionLoc);
    200    wtu.clearAndDrawIndexedQuad(gl, 1);
    201    wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
    202 
    203    // Check we can read & write each uniform.
    204    // Note: uniforms past test.usedUniformVector might not exist.
    205    for (var ii = 0; ii < test.maxUniformVectors; ++ii) {
    206        gl.uniform4fv(locations[ii], [ii + 4, ii + 2, ii + 3, ii + 1]);
    207    }
    208 
    209    var kEpsilon = 0.01;
    210    var isSame = function(v1, v2) {
    211        return Math.abs(v1 - v2) < kEpsilon;
    212    };
    213 
    214    for (var ii = 0; ii < test.maxUniformVectors; ++ii) {
    215        var location = locations[ii];
    216        if (location) {
    217            var value = gl.getUniform(program, locations[ii]);
    218            if (!isSame(value[0], ii + 4) ||
    219                !isSame(value[1], ii + 2) ||
    220                !isSame(value[2], ii + 3) ||
    221                !isSame(value[3], ii + 1)) {
    222                testFailed("location: " + ii + " was not correct value");
    223                break;
    224            }
    225        }
    226    }
    227 }
    228 
    229 function runOneTest(gl, test) {
    230    testUniformOptimizationIssues(test);
    231 };
    232 
    233 function runTests(gl, tests) {
    234    debug("");
    235    debug("Test drivers don't over optimize unused array elements");
    236 
    237    for (var ii = 0; ii < tests.length; ++ii) {
    238        runOneTest(gl, tests[ii]);
    239    }
    240 };
    241 
    242 return {
    243    setupTests : setupTests,
    244    runTests : runTests
    245 };
    246 
    247 }());