tor-browser

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

glsl-generator.js (40685B)


      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 GLSLGenerator = (function() {
      7 
      8 var vertexShaderTemplate = [
      9  "attribute vec4 aPosition;",
     10  "",
     11  "varying vec4 vColor;",
     12  "",
     13  "$(extra)",
     14  "$(emu)",
     15  "",
     16  "void main()",
     17  "{",
     18  "   gl_Position = aPosition;",
     19  "   vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
     20  "   vec4 color = vec4(",
     21  "       texcoord,",
     22  "       texcoord.x * texcoord.y,",
     23  "       (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
     24  "   $(test)",
     25  "}"
     26 ].join("\n");
     27 
     28 var fragmentShaderTemplate = [
     29  "precision mediump float;",
     30  "",
     31  "varying vec4 vColor;",
     32  "",
     33  "$(extra)",
     34  "$(emu)",
     35  "",
     36  "void main()",
     37  "{",
     38  "   $(test)",
     39  "}"
     40 ].join("\n");
     41 
     42 var baseVertexShader = [
     43  "attribute vec4 aPosition;",
     44  "",
     45  "varying vec4 vColor;",
     46  "",
     47  "void main()",
     48  "{",
     49  "   gl_Position = aPosition;",
     50  "   vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));",
     51  "   vColor = vec4(",
     52  "       texcoord,",
     53  "       texcoord.x * texcoord.y,",
     54  "       (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);",
     55  "}"
     56 ].join("\n");
     57 
     58 var baseVertexShaderWithColor = [
     59  "attribute vec4 aPosition;",
     60  "attribute vec4 aColor;",
     61  "",
     62  "varying vec4 vColor;",
     63  "",
     64  "void main()",
     65  "{",
     66  "   gl_Position = aPosition;",
     67  "   vColor = aColor;",
     68  "}"
     69 ].join("\n");
     70 
     71 var baseFragmentShader = [
     72  "precision mediump float;",
     73  "varying vec4 vColor;",
     74  "",
     75  "void main()",
     76  "{",
     77  "   gl_FragColor = vColor;",
     78  "}"
     79 ].join("\n");
     80 
     81 var types = [
     82  { type: "float",
     83    code: [
     84      "float $(func)_emu($(args)) {",
     85      "  return $(func)_base($(baseArgs));",
     86      "}"].join("\n")
     87  },
     88  { type: "vec2",
     89    code: [
     90      "vec2 $(func)_emu($(args)) {",
     91      "  return vec2(",
     92      "      $(func)_base($(baseArgsX)),",
     93      "      $(func)_base($(baseArgsY)));",
     94      "}"].join("\n")
     95  },
     96  { type: "vec3",
     97    code: [
     98      "vec3 $(func)_emu($(args)) {",
     99      "  return vec3(",
    100      "      $(func)_base($(baseArgsX)),",
    101      "      $(func)_base($(baseArgsY)),",
    102      "      $(func)_base($(baseArgsZ)));",
    103      "}"].join("\n")
    104  },
    105  { type: "vec4",
    106    code: [
    107      "vec4 $(func)_emu($(args)) {",
    108      "  return vec4(",
    109      "      $(func)_base($(baseArgsX)),",
    110      "      $(func)_base($(baseArgsY)),",
    111      "      $(func)_base($(baseArgsZ)),",
    112      "      $(func)_base($(baseArgsW)));",
    113      "}"].join("\n")
    114  }
    115 ];
    116 
    117 var bvecTypes = [
    118  { type: "bvec2",
    119    code: [
    120      "bvec2 $(func)_emu($(args)) {",
    121      "  return bvec2(",
    122      "      $(func)_base($(baseArgsX)),",
    123      "      $(func)_base($(baseArgsY)));",
    124      "}"].join("\n")
    125  },
    126  { type: "bvec3",
    127    code: [
    128      "bvec3 $(func)_emu($(args)) {",
    129      "  return bvec3(",
    130      "      $(func)_base($(baseArgsX)),",
    131      "      $(func)_base($(baseArgsY)),",
    132      "      $(func)_base($(baseArgsZ)));",
    133      "}"].join("\n")
    134  },
    135  { type: "bvec4",
    136    code: [
    137      "vec4 $(func)_emu($(args)) {",
    138      "  return bvec4(",
    139      "      $(func)_base($(baseArgsX)),",
    140      "      $(func)_base($(baseArgsY)),",
    141      "      $(func)_base($(baseArgsZ)),",
    142      "      $(func)_base($(baseArgsW)));",
    143      "}"].join("\n")
    144  }
    145 ];
    146 
    147 var replaceRE = /\$\((\w+)\)/g;
    148 
    149 var replaceParams = function(str) {
    150  var args = arguments;
    151  return str.replace(replaceRE, function(str, p1, offset, s) {
    152    for (var ii = 1; ii < args.length; ++ii) {
    153      if (args[ii][p1] !== undefined) {
    154        return args[ii][p1];
    155      }
    156    }
    157    throw "unknown string param '" + p1 + "'";
    158  });
    159 };
    160 
    161 var generateReferenceShader = function(
    162    shaderInfo, template, params, typeInfo, test) {
    163  var input = shaderInfo.input;
    164  var output = shaderInfo.output;
    165  var feature = params.feature;
    166  var testFunc = params.testFunc;
    167  var emuFunc = params.emuFunc || "";
    168  var extra = params.extra || '';
    169  var args = params.args || "$(type) value";
    170  var type = typeInfo.type;
    171  var typeCode = typeInfo.code;
    172 
    173  var baseArgs = params.baseArgs || "value$(field)";
    174  var baseArgsX = replaceParams(baseArgs, {field: ".x"});
    175  var baseArgsY = replaceParams(baseArgs, {field: ".y"});
    176  var baseArgsZ = replaceParams(baseArgs, {field: ".z"});
    177  var baseArgsW = replaceParams(baseArgs, {field: ".w"});
    178  var baseArgs = replaceParams(baseArgs, {field: ""});
    179 
    180  test = replaceParams(test, {
    181    input: input,
    182    output: output,
    183    func: feature + "_emu"
    184  });
    185  emuFunc = replaceParams(emuFunc, {
    186    func: feature
    187  });
    188  args = replaceParams(args, {
    189    type: type
    190  });
    191  typeCode = replaceParams(typeCode, {
    192    func: feature,
    193    type: type,
    194    args: args,
    195    baseArgs: baseArgs,
    196    baseArgsX: baseArgsX,
    197    baseArgsY: baseArgsY,
    198    baseArgsZ: baseArgsZ,
    199    baseArgsW: baseArgsW
    200  });
    201  var shader = replaceParams(template, {
    202    extra: extra,
    203    emu: emuFunc + "\n\n" + typeCode,
    204    test: test
    205  });
    206  return shader;
    207 };
    208 
    209 var generateTestShader = function(
    210    shaderInfo, template, params, test) {
    211  var input = shaderInfo.input;
    212  var output = shaderInfo.output;
    213  var feature = params.feature;
    214  var testFunc = params.testFunc;
    215  var extra = params.extra || '';
    216 
    217  test = replaceParams(test, {
    218    input: input,
    219    output: output,
    220    func: feature
    221  });
    222  var shader = replaceParams(template, {
    223    extra: extra,
    224    emu: '',
    225    test: test
    226  });
    227  return shader;
    228 };
    229 
    230 function _reportResults(refData, refImg, testData, testImg, tolerance,
    231                        width, height, ctx, imgData, wtu, canvas2d, consoleDiv) {
    232  var same = true;
    233  var firstFailure = null;
    234  for (var yy = 0; yy < height; ++yy) {
    235    for (var xx = 0; xx < width; ++xx) {
    236      var offset = (yy * width + xx) * 4;
    237      var imgOffset = ((height - yy - 1) * width + xx) * 4;
    238      imgData.data[imgOffset + 0] = 0;
    239      imgData.data[imgOffset + 1] = 0;
    240      imgData.data[imgOffset + 2] = 0;
    241      imgData.data[imgOffset + 3] = 255;
    242      if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance ||
    243          Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance ||
    244          Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance ||
    245          Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) {
    246        var detail = 'at (' + xx + ',' + yy + '): ref=(' +
    247            refData[offset + 0] + ',' +
    248            refData[offset + 1] + ',' +
    249            refData[offset + 2] + ',' +
    250            refData[offset + 3] + ')  test=(' +
    251            testData[offset + 0] + ',' +
    252            testData[offset + 1] + ',' +
    253            testData[offset + 2] + ',' +
    254            testData[offset + 3] + ') tolerance=' + tolerance;
    255        consoleDiv.appendChild(document.createTextNode(detail));
    256        consoleDiv.appendChild(document.createElement('br'));
    257        if (!firstFailure) {
    258          firstFailure = ": " + detail;
    259        }
    260        imgData.data[imgOffset] = 255;
    261        same = false;
    262      }
    263    }
    264  }
    265 
    266  var diffImg = null;
    267  if (!same) {
    268    ctx.putImageData(imgData, 0, 0);
    269    diffImg = wtu.makeImageFromCanvas(canvas2d);
    270  }
    271 
    272  var div = document.createElement("div");
    273  div.className = "testimages";
    274  wtu.insertImage(div, "ref", refImg);
    275  wtu.insertImage(div, "test", testImg);
    276  if (diffImg) {
    277    wtu.insertImage(div, "diff", diffImg);
    278  }
    279  div.appendChild(document.createElement('br'));
    280 
    281  consoleDiv.appendChild(div);
    282 
    283  if (!same) {
    284    testFailed("images are different" + (firstFailure ? firstFailure : ""));
    285  } else {
    286    testPassed("images are the same");
    287  }
    288 
    289  consoleDiv.appendChild(document.createElement('hr'));
    290 }
    291 
    292 var runFeatureTest = function(params) {
    293  var wtu = WebGLTestUtils;
    294  var gridRes = params.gridRes;
    295  var vertexTolerance = params.tolerance || 0;
    296  var fragmentTolerance = params.tolerance || 1;
    297  if ('fragmentTolerance' in params)
    298    fragmentTolerance = params.fragmentTolerance;
    299 
    300  description("Testing GLSL feature: " + params.feature);
    301 
    302  var width = 32;
    303  var height = 32;
    304 
    305  var consoleDiv = document.getElementById("console");
    306  var canvas = document.createElement('canvas');
    307  canvas.width = width;
    308  canvas.height = height;
    309  var gl = wtu.create3DContext(canvas, { premultipliedAlpha: false });
    310  if (!gl) {
    311    testFailed("context does not exist");
    312    finishTest();
    313    return;
    314  }
    315 
    316  var canvas2d = document.createElement('canvas');
    317  canvas2d.width = width;
    318  canvas2d.height = height;
    319  var ctx = canvas2d.getContext("2d");
    320  var imgData = ctx.getImageData(0, 0, width, height);
    321 
    322  var shaderInfos = [
    323    { type: "vertex",
    324      input: "color",
    325      output: "vColor",
    326      vertexShaderTemplate: vertexShaderTemplate,
    327      fragmentShaderTemplate: baseFragmentShader,
    328      tolerance: vertexTolerance
    329    },
    330    { type: "fragment",
    331      input: "vColor",
    332      output: "gl_FragColor",
    333      vertexShaderTemplate: baseVertexShader,
    334      fragmentShaderTemplate: fragmentShaderTemplate,
    335      tolerance: fragmentTolerance
    336    }
    337  ];
    338  for (var ss = 0; ss < shaderInfos.length; ++ss) {
    339    var shaderInfo = shaderInfos[ss];
    340    var tests = params.tests;
    341    var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
    342    // Test vertex shaders
    343    for (var ii = 0; ii < tests.length; ++ii) {
    344      var type = testTypes[ii];
    345      if (params.simpleEmu) {
    346        type = {
    347          type: type.type,
    348          code: params.simpleEmu
    349        };
    350      }
    351      debug("");
    352      var str = replaceParams(params.testFunc, {
    353        func: params.feature,
    354        type: type.type,
    355        arg0: type.type
    356      });
    357      var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
    358      debug(passMsg);
    359 
    360      var referenceVertexShaderSource = generateReferenceShader(
    361          shaderInfo,
    362          shaderInfo.vertexShaderTemplate,
    363          params,
    364          type,
    365          tests[ii]);
    366      var referenceFragmentShaderSource = generateReferenceShader(
    367          shaderInfo,
    368          shaderInfo.fragmentShaderTemplate,
    369          params,
    370          type,
    371          tests[ii]);
    372      var testVertexShaderSource = generateTestShader(
    373          shaderInfo,
    374          shaderInfo.vertexShaderTemplate,
    375          params,
    376          tests[ii]);
    377      var testFragmentShaderSource = generateTestShader(
    378          shaderInfo,
    379          shaderInfo.fragmentShaderTemplate,
    380          params,
    381          tests[ii]);
    382 
    383 
    384      debug("");
    385      var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
    386      var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
    387      var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
    388      var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
    389      debug("");
    390 
    391      if (parseInt(wtu.getUrlOptions().dumpShaders)) {
    392        var vRefInfo = {
    393          shader: referenceVertexShader,
    394          shaderSuccess: true,
    395          label: "reference vertex shader",
    396          source: referenceVertexShaderSource
    397        };
    398        var fRefInfo = {
    399          shader: referenceFragmentShader,
    400          shaderSuccess: true,
    401          label: "reference fragment shader",
    402          source: referenceFragmentShaderSource
    403        };
    404        wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
    405 
    406        var vTestInfo = {
    407          shader: testVertexShader,
    408          shaderSuccess: true,
    409          label: "test vertex shader",
    410          source: testVertexShaderSource
    411        };
    412        var fTestInfo = {
    413          shader: testFragmentShader,
    414          shaderSuccess: true,
    415          label: "test fragment shader",
    416          source: testFragmentShaderSource
    417        };
    418        wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
    419      }
    420 
    421      var refData = draw(
    422          referenceVertexShader, referenceFragmentShader);
    423      var refImg = wtu.makeImageFromCanvas(canvas);
    424      if (ss == 0) {
    425        var testData = draw(
    426            testVertexShader, referenceFragmentShader);
    427      } else {
    428        var testData = draw(
    429            referenceVertexShader, testFragmentShader);
    430      }
    431      var testImg = wtu.makeImageFromCanvas(canvas);
    432 
    433      _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
    434                     width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
    435    }
    436  }
    437 
    438  finishTest();
    439 
    440  function draw(vertexShader, fragmentShader) {
    441    var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
    442 
    443    var posLoc = gl.getAttribLocation(program, "aPosition");
    444    wtu.setupIndexedQuad(gl, gridRes, posLoc);
    445 
    446    gl.useProgram(program);
    447    wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
    448    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
    449 
    450    var img = new Uint8Array(width * height * 4);
    451    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
    452    return img;
    453  }
    454 
    455 };
    456 
    457 var runBasicTest = function(params) {
    458  var wtu = WebGLTestUtils;
    459  var gridRes = params.gridRes;
    460  var vertexTolerance = params.tolerance || 0;
    461  var fragmentTolerance = vertexTolerance;
    462  if ('fragmentTolerance' in params)
    463    fragmentTolerance = params.fragmentTolerance || 0;
    464 
    465  description("Testing : " + document.getElementsByTagName("title")[0].innerText);
    466 
    467  var width = 32;
    468  var height = 32;
    469 
    470  var consoleDiv = document.getElementById("console");
    471  var canvas = document.createElement('canvas');
    472  canvas.width = width;
    473  canvas.height = height;
    474  var gl = wtu.create3DContext(canvas);
    475  if (!gl) {
    476    testFailed("context does not exist");
    477    finishTest();
    478    return;
    479  }
    480 
    481  var canvas2d = document.createElement('canvas');
    482  canvas2d.width = width;
    483  canvas2d.height = height;
    484  var ctx = canvas2d.getContext("2d");
    485  var imgData = ctx.getImageData(0, 0, width, height);
    486 
    487  var shaderInfos = [
    488    { type: "vertex",
    489      input: "color",
    490      output: "vColor",
    491      vertexShaderTemplate: vertexShaderTemplate,
    492      fragmentShaderTemplate: baseFragmentShader,
    493      tolerance: vertexTolerance
    494    },
    495    { type: "fragment",
    496      input: "vColor",
    497      output: "gl_FragColor",
    498      vertexShaderTemplate: baseVertexShader,
    499      fragmentShaderTemplate: fragmentShaderTemplate,
    500      tolerance: fragmentTolerance
    501    }
    502  ];
    503  for (var ss = 0; ss < shaderInfos.length; ++ss) {
    504    var shaderInfo = shaderInfos[ss];
    505    var tests = params.tests;
    506 //    var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
    507    // Test vertex shaders
    508    for (var ii = 0; ii < tests.length; ++ii) {
    509      var test = tests[ii];
    510      debug("");
    511      var passMsg = "Testing: " + test.name + " in " + shaderInfo.type + " shader";
    512      debug(passMsg);
    513 
    514      function genShader(shaderInfo, template, shader, subs) {
    515        shader = replaceParams(shader, subs, {
    516            input: shaderInfo.input,
    517            output: shaderInfo.output
    518          });
    519        shader = replaceParams(template, subs, {
    520            test: shader,
    521            emu: "",
    522            extra: ""
    523          });
    524        return shader;
    525      }
    526 
    527      var referenceVertexShaderSource = genShader(
    528          shaderInfo,
    529          shaderInfo.vertexShaderTemplate,
    530          test.reference.shader,
    531          test.reference.subs);
    532      var referenceFragmentShaderSource = genShader(
    533          shaderInfo,
    534          shaderInfo.fragmentShaderTemplate,
    535          test.reference.shader,
    536          test.reference.subs);
    537      var testVertexShaderSource = genShader(
    538          shaderInfo,
    539          shaderInfo.vertexShaderTemplate,
    540          test.test.shader,
    541          test.test.subs);
    542      var testFragmentShaderSource = genShader(
    543          shaderInfo,
    544          shaderInfo.fragmentShaderTemplate,
    545          test.test.shader,
    546          test.test.subs);
    547 
    548      debug("");
    549      var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'reference');
    550      var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'reference');
    551      var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true, 'test');
    552      var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true, 'test');
    553      debug("");
    554 
    555      if (parseInt(wtu.getUrlOptions().dumpShaders)) {
    556        var vRefInfo = {
    557          shader: referenceVertexShader,
    558          shaderSuccess: true,
    559          label: "reference vertex shader",
    560          source: referenceVertexShaderSource
    561        };
    562        var fRefInfo = {
    563          shader: referenceFragmentShader,
    564          shaderSuccess: true,
    565          label: "reference fragment shader",
    566          source: referenceFragmentShaderSource
    567        };
    568        wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
    569 
    570        var vTestInfo = {
    571          shader: testVertexShader,
    572          shaderSuccess: true,
    573          label: "test vertex shader",
    574          source: testVertexShaderSource
    575        };
    576        var fTestInfo = {
    577          shader: testFragmentShader,
    578          shaderSuccess: true,
    579          label: "test fragment shader",
    580          source: testFragmentShaderSource
    581        };
    582        wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
    583      }
    584 
    585      var refData = draw(referenceVertexShader, referenceFragmentShader);
    586      var refImg = wtu.makeImageFromCanvas(canvas);
    587      if (ss == 0) {
    588        var testData = draw(testVertexShader, referenceFragmentShader);
    589      } else {
    590        var testData = draw(referenceVertexShader, testFragmentShader);
    591      }
    592      var testImg = wtu.makeImageFromCanvas(canvas);
    593 
    594      _reportResults(refData, refImg, testData, testImg, shaderInfo.tolerance,
    595                     width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
    596    }
    597  }
    598 
    599  finishTest();
    600 
    601  function draw(vertexShader, fragmentShader) {
    602    var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
    603 
    604    var posLoc = gl.getAttribLocation(program, "aPosition");
    605    wtu.setupIndexedQuad(gl, gridRes, posLoc);
    606 
    607    gl.useProgram(program);
    608    wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
    609    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
    610 
    611    var img = new Uint8Array(width * height * 4);
    612    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
    613    return img;
    614  }
    615 
    616 };
    617 
    618 var runReferenceImageTest = function(params) {
    619  var wtu = WebGLTestUtils;
    620  var gridRes = params.gridRes;
    621  var vertexTolerance = params.tolerance || 0;
    622  var fragmentTolerance = vertexTolerance;
    623  if ('fragmentTolerance' in params)
    624    fragmentTolerance = params.fragmentTolerance || 0;
    625 
    626  description("Testing GLSL feature: " + params.feature);
    627 
    628  var width = 32;
    629  var height = 32;
    630 
    631  var consoleDiv = document.getElementById("console");
    632  var canvas = document.createElement('canvas');
    633  canvas.width = width;
    634  canvas.height = height;
    635  var gl = wtu.create3DContext(canvas, { antialias: false, premultipliedAlpha: false });
    636  if (!gl) {
    637    testFailed("context does not exist");
    638    finishTest();
    639    return;
    640  }
    641 
    642  var canvas2d = document.createElement('canvas');
    643  canvas2d.width = width;
    644  canvas2d.height = height;
    645  var ctx = canvas2d.getContext("2d");
    646  var imgData = ctx.getImageData(0, 0, width, height);
    647 
    648  // State for reference images for vertex shader tests.
    649  // These are drawn with the same tessellated grid as the test vertex
    650  // shader so that the interpolation is identical. The grid is reused
    651  // from test to test; the colors are changed.
    652 
    653  var indexedQuadForReferenceVertexShader =
    654    wtu.setupIndexedQuad(gl, gridRes, 0);
    655  var referenceVertexShaderProgram =
    656    wtu.setupProgram(gl, [ baseVertexShaderWithColor, baseFragmentShader ],
    657                     ["aPosition", "aColor"]);
    658  var referenceVertexShaderColorBuffer = gl.createBuffer();
    659 
    660  var shaderInfos = [
    661    { type: "vertex",
    662      input: "color",
    663      output: "vColor",
    664      vertexShaderTemplate: vertexShaderTemplate,
    665      fragmentShaderTemplate: baseFragmentShader,
    666      tolerance: vertexTolerance
    667    },
    668    { type: "fragment",
    669      input: "vColor",
    670      output: "gl_FragColor",
    671      vertexShaderTemplate: baseVertexShader,
    672      fragmentShaderTemplate: fragmentShaderTemplate,
    673      tolerance: fragmentTolerance
    674    }
    675  ];
    676  for (var ss = 0; ss < shaderInfos.length; ++ss) {
    677    var shaderInfo = shaderInfos[ss];
    678    var tests = params.tests;
    679    var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types);
    680    // Test vertex shaders
    681    for (var ii = 0; ii < tests.length; ++ii) {
    682      var type = testTypes[ii];
    683      var isVertex = (ss == 0);
    684      debug("");
    685      var str = replaceParams(params.testFunc, {
    686        func: params.feature,
    687        type: type.type,
    688        arg0: type.type
    689      });
    690      var passMsg = "Testing: " + str + " in " + shaderInfo.type + " shader";
    691      debug(passMsg);
    692 
    693      var referenceVertexShaderSource = generateReferenceShader(
    694          shaderInfo,
    695          shaderInfo.vertexShaderTemplate,
    696          params,
    697          type,
    698          tests[ii].source);
    699      var referenceFragmentShaderSource = generateReferenceShader(
    700          shaderInfo,
    701          shaderInfo.fragmentShaderTemplate,
    702          params,
    703          type,
    704          tests[ii].source);
    705      var testVertexShaderSource = generateTestShader(
    706          shaderInfo,
    707          shaderInfo.vertexShaderTemplate,
    708          params,
    709          tests[ii].source);
    710      var testFragmentShaderSource = generateTestShader(
    711          shaderInfo,
    712          shaderInfo.fragmentShaderTemplate,
    713          params,
    714          tests[ii].source);
    715      var referenceTextureOrArray = generateReferenceImage(
    716          gl,
    717          tests[ii].generator,
    718          isVertex ? gridRes : width,
    719          isVertex ? gridRes : height,
    720          isVertex);
    721 
    722      debug("");
    723      var testVertexShader = wtu.loadShader(gl, testVertexShaderSource, gl.VERTEX_SHADER, testFailed, true);
    724      var testFragmentShader = wtu.loadShader(gl, testFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed, true);
    725      debug("");
    726 
    727 
    728      if (parseInt(wtu.getUrlOptions().dumpShaders)) {
    729        var vRefInfo = {
    730          shader: referenceVertexShader,
    731          shaderSuccess: true,
    732          label: "reference vertex shader",
    733          source: referenceVertexShaderSource
    734        };
    735        var fRefInfo = {
    736          shader: referenceFragmentShader,
    737          shaderSuccess: true,
    738          label: "reference fragment shader",
    739          source: referenceFragmentShaderSource
    740        };
    741        wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vRefInfo, fRefInfo);
    742 
    743        var vTestInfo = {
    744          shader: testVertexShader,
    745          shaderSuccess: true,
    746          label: "test vertex shader",
    747          source: testVertexShaderSource
    748        };
    749        var fTestInfo = {
    750          shader: testFragmentShader,
    751          shaderSuccess: true,
    752          label: "test fragment shader",
    753          source: testFragmentShaderSource
    754        };
    755        wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vTestInfo, fTestInfo);
    756      }
    757 
    758      var refData;
    759      if (isVertex) {
    760        refData = drawVertexReferenceImage(referenceTextureOrArray);
    761      } else {
    762        refData = drawFragmentReferenceImage(referenceTextureOrArray);
    763      }
    764      var refImg = wtu.makeImageFromCanvas(canvas);
    765      var testData;
    766      if (isVertex) {
    767        var referenceFragmentShader = wtu.loadShader(gl, referenceFragmentShaderSource, gl.FRAGMENT_SHADER, testFailed);
    768        testData = draw(
    769          testVertexShader, referenceFragmentShader);
    770      } else {
    771        var referenceVertexShader = wtu.loadShader(gl, referenceVertexShaderSource, gl.VERTEX_SHADER, testFailed);
    772        testData = draw(
    773          referenceVertexShader, testFragmentShader);
    774      }
    775      var testImg = wtu.makeImageFromCanvas(canvas);
    776      var testTolerance = shaderInfo.tolerance;
    777      // Provide per-test tolerance so that we can increase it only for those desired.
    778      if ('tolerance' in tests[ii])
    779        testTolerance = tests[ii].tolerance || 0;
    780      _reportResults(refData, refImg, testData, testImg, testTolerance,
    781                     width, height, ctx, imgData, wtu, canvas2d, consoleDiv);
    782    }
    783  }
    784 
    785  finishTest();
    786 
    787  function draw(vertexShader, fragmentShader) {
    788    var program = wtu.createProgram(gl, vertexShader, fragmentShader, testFailed);
    789 
    790    var posLoc = gl.getAttribLocation(program, "aPosition");
    791    wtu.setupIndexedQuad(gl, gridRes, posLoc);
    792 
    793    gl.useProgram(program);
    794    wtu.clearAndDrawIndexedQuad(gl, gridRes, [0, 0, 255, 255]);
    795    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
    796 
    797    var img = new Uint8Array(width * height * 4);
    798    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
    799    return img;
    800  }
    801 
    802  function drawVertexReferenceImage(colors) {
    803    gl.bindBuffer(gl.ARRAY_BUFFER, indexedQuadForReferenceVertexShader[0]);
    804    gl.enableVertexAttribArray(0);
    805    gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
    806    gl.bindBuffer(gl.ARRAY_BUFFER, referenceVertexShaderColorBuffer);
    807    gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW);
    808    gl.enableVertexAttribArray(1);
    809    gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0);
    810    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexedQuadForReferenceVertexShader[1]);
    811    gl.useProgram(referenceVertexShaderProgram);
    812    wtu.clearAndDrawIndexedQuad(gl, gridRes);
    813    gl.disableVertexAttribArray(0);
    814    gl.disableVertexAttribArray(1);
    815    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
    816 
    817    var img = new Uint8Array(width * height * 4);
    818    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
    819    return img;
    820  }
    821 
    822  function drawFragmentReferenceImage(texture) {
    823    var program = wtu.setupTexturedQuad(gl);
    824 
    825    gl.activeTexture(gl.TEXTURE0);
    826    gl.bindTexture(gl.TEXTURE_2D, texture);
    827    var texLoc = gl.getUniformLocation(program, "tex");
    828    gl.uniform1i(texLoc, 0);
    829    wtu.clearAndDrawUnitQuad(gl);
    830    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw");
    831 
    832    var img = new Uint8Array(width * height * 4);
    833    gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img);
    834    return img;
    835  }
    836 
    837  /**
    838   * Creates and returns either a Uint8Array (for vertex shaders) or
    839   * WebGLTexture (for fragment shaders) containing the reference
    840   * image for the function being tested. Exactly how the function is
    841   * evaluated, and the size of the returned texture or array, depends on
    842   * whether we are testing a vertex or fragment shader. If a fragment
    843   * shader, the function is evaluated at the pixel centers. If a
    844   * vertex shader, the function is evaluated at the triangle's
    845   * vertices.
    846   *
    847   * @param {!WebGLRenderingContext} gl The WebGLRenderingContext to use to generate texture objects.
    848   * @param {!function(number,number,number,number): !Array.<number>} generator The reference image generator function.
    849   * @param {number} width The width of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
    850   * @param {number} height The height of the texture to generate if testing a fragment shader; the grid resolution if testing a vertex shader.
    851   * @param {boolean} isVertex True if generating a reference image for a vertex shader; false if for a fragment shader.
    852   * @return {!WebGLTexture|!Uint8Array} The texture object or array that was generated.
    853   */
    854  function generateReferenceImage(
    855    gl,
    856    generator,
    857    width,
    858    height,
    859    isVertex) {
    860 
    861    // Note: the math in this function must match that in the vertex and
    862    // fragment shader templates above.
    863    function computeTexCoord(x) {
    864      return x * 0.5 + 0.5;
    865    }
    866 
    867    function computeVertexColor(texCoordX, texCoordY) {
    868      return [ texCoordX,
    869               texCoordY,
    870               texCoordX * texCoordY,
    871               (1.0 - texCoordX) * texCoordY * 0.5 + 0.5 ];
    872    }
    873 
    874    /**
    875     * Computes fragment color according to the algorithm used for interpolation
    876     * in OpenGL (GLES 2.0 spec 3.5.1, OpenGL 4.3 spec 14.6.1).
    877     */
    878    function computeInterpolatedColor(texCoordX, texCoordY) {
    879      // Calculate grid line indexes below and to the left from texCoord.
    880      var gridBottom = Math.floor(texCoordY * gridRes);
    881      if (gridBottom == gridRes) {
    882        --gridBottom;
    883      }
    884      var gridLeft = Math.floor(texCoordX * gridRes);
    885      if (gridLeft == gridRes) {
    886        --gridLeft;
    887      }
    888 
    889      // Calculate coordinates relative to the grid cell.
    890      var cellX = texCoordX * gridRes - gridLeft;
    891      var cellY = texCoordY * gridRes - gridBottom;
    892 
    893      // Barycentric coordinates inside either triangle ACD or ABC
    894      // are used as weights for the vertex colors in the corners:
    895      // A--B
    896      // |\ |
    897      // | \|
    898      // D--C
    899 
    900      var aColor = computeVertexColor(gridLeft / gridRes, (gridBottom + 1) / gridRes);
    901      var bColor = computeVertexColor((gridLeft + 1) / gridRes, (gridBottom + 1) / gridRes);
    902      var cColor = computeVertexColor((gridLeft + 1) / gridRes, gridBottom / gridRes);
    903      var dColor = computeVertexColor(gridLeft / gridRes, gridBottom / gridRes);
    904 
    905      // Calculate weights.
    906      var a, b, c, d;
    907 
    908      if (cellX + cellY < 1) {
    909        // In bottom triangle ACD.
    910        a = cellY; // area of triangle C-D-(cellX, cellY) relative to ACD
    911        c = cellX; // area of triangle D-A-(cellX, cellY) relative to ACD
    912        d = 1 - a - c;
    913        b = 0;
    914      } else {
    915        // In top triangle ABC.
    916        a = 1 - cellX; // area of the triangle B-C-(cellX, cellY) relative to ABC
    917        c = 1 - cellY; // area of the triangle A-B-(cellX, cellY) relative to ABC
    918        b = 1 - a - c;
    919        d = 0;
    920      }
    921 
    922      var interpolated = [];
    923      for (var ii = 0; ii < aColor.length; ++ii) {
    924        interpolated.push(a * aColor[ii] + b * bColor[ii] + c * cColor[ii] + d * dColor[ii]);
    925      }
    926      return interpolated;
    927    }
    928 
    929    function clamp(value, minVal, maxVal) {
    930      return Math.max(minVal, Math.min(value, maxVal));
    931    }
    932 
    933    // Evaluates the function at clip coordinates (px,py), storing the
    934    // result in the array "pixel". Each channel's result is clamped
    935    // between 0 and 255.
    936    function evaluateAtClipCoords(px, py, pixel, colorFunc) {
    937      var tcx = computeTexCoord(px);
    938      var tcy = computeTexCoord(py);
    939 
    940      var color = colorFunc(tcx, tcy);
    941 
    942      var output = generator(color[0], color[1], color[2], color[3]);
    943 
    944      // Multiply by 256 to get even distribution for all values between 0 and 1.
    945      // Use rounding rather than truncation to more closely match the GPU's behavior.
    946      pixel[0] = clamp(Math.round(256 * output[0]), 0, 255);
    947      pixel[1] = clamp(Math.round(256 * output[1]), 0, 255);
    948      pixel[2] = clamp(Math.round(256 * output[2]), 0, 255);
    949      pixel[3] = clamp(Math.round(256 * output[3]), 0, 255);
    950    }
    951 
    952    function generateFragmentReference() {
    953      var data = new Uint8Array(4 * width * height);
    954 
    955      var horizTexel = 1.0 / width;
    956      var vertTexel = 1.0 / height;
    957      var halfHorizTexel = 0.5 * horizTexel;
    958      var halfVertTexel = 0.5 * vertTexel;
    959 
    960      var pixel = new Array(4);
    961 
    962      for (var yi = 0; yi < height; ++yi) {
    963        for (var xi = 0; xi < width; ++xi) {
    964          // The function must be evaluated at pixel centers.
    965 
    966          // Compute desired position in clip space
    967          var px = -1.0 + 2.0 * (halfHorizTexel + xi * horizTexel);
    968          var py = -1.0 + 2.0 * (halfVertTexel + yi * vertTexel);
    969 
    970          evaluateAtClipCoords(px, py, pixel, computeInterpolatedColor);
    971          var index = 4 * (width * yi + xi);
    972          data[index + 0] = pixel[0];
    973          data[index + 1] = pixel[1];
    974          data[index + 2] = pixel[2];
    975          data[index + 3] = pixel[3];
    976        }
    977      }
    978 
    979      var texture = gl.createTexture();
    980      gl.bindTexture(gl.TEXTURE_2D, texture);
    981      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    982      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    983      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    984      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    985      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
    986                    gl.RGBA, gl.UNSIGNED_BYTE, data);
    987      return texture;
    988    }
    989 
    990    function generateVertexReference() {
    991      // We generate a Uint8Array which contains the evaluation of the
    992      // function at the vertices of the triangle mesh. It is expected
    993      // that the width and the height are identical, and equivalent
    994      // to the grid resolution.
    995      if (width != height) {
    996        throw "width and height must be equal";
    997      }
    998 
    999      var texSize = 1 + width;
   1000      var data = new Uint8Array(4 * texSize * texSize);
   1001 
   1002      var step = 2.0 / width;
   1003 
   1004      var pixel = new Array(4);
   1005 
   1006      for (var yi = 0; yi < texSize; ++yi) {
   1007        for (var xi = 0; xi < texSize; ++xi) {
   1008          // The function is evaluated at the triangles' vertices.
   1009 
   1010          // Compute desired position in clip space
   1011          var px = -1.0 + (xi * step);
   1012          var py = -1.0 + (yi * step);
   1013 
   1014          evaluateAtClipCoords(px, py, pixel, computeVertexColor);
   1015          var index = 4 * (texSize * yi + xi);
   1016          data[index + 0] = pixel[0];
   1017          data[index + 1] = pixel[1];
   1018          data[index + 2] = pixel[2];
   1019          data[index + 3] = pixel[3];
   1020        }
   1021      }
   1022 
   1023      return data;
   1024    }
   1025 
   1026    //----------------------------------------------------------------------
   1027    // Body of generateReferenceImage
   1028    //
   1029 
   1030    if (isVertex) {
   1031      return generateVertexReference();
   1032    } else {
   1033      return generateFragmentReference();
   1034    }
   1035  }
   1036 };
   1037 
   1038 return {
   1039  /**
   1040   * runs a bunch of GLSL tests using the passed in parameters
   1041   * The parameters are:
   1042   *
   1043   * feature:
   1044   *    the name of the function being tested (eg, sin, dot,
   1045   *    normalize)
   1046   *
   1047   * testFunc:
   1048   *    The prototype of function to be tested not including the
   1049   *    return type.
   1050   *
   1051   * emuFunc:
   1052   *    A base function that can be used to generate emulation
   1053   *    functions. Example for 'ceil'
   1054   *
   1055   *      float $(func)_base(float value) {
   1056   *        float m = mod(value, 1.0);
   1057   *        return m != 0.0 ? (value + 1.0 - m) : value;
   1058   *      }
   1059   *
   1060   * args:
   1061   *    The arguments to the function
   1062   *
   1063   * baseArgs: (optional)
   1064   *    The arguments when a base function is used to create an
   1065   *    emulation function. For example 'float sign_base(float v)'
   1066   *    is used to implemenent vec2 sign_emu(vec2 v).
   1067   *
   1068   * simpleEmu:
   1069   *    if supplied, the code that can be used to generate all
   1070   *    functions for all types.
   1071   *
   1072   *    Example for 'normalize':
   1073   *
   1074   *        $(type) $(func)_emu($(args)) {
   1075   *           return value / length(value);
   1076   *        }
   1077   *
   1078   * gridRes: (optional)
   1079   *    The resolution of the mesh to generate. The default is a
   1080   *    1x1 grid but many vertex shaders need a higher resolution
   1081   *    otherwise the only values passed in are the 4 corners
   1082   *    which often have the same value.
   1083   *
   1084   * tests:
   1085   *    The code for each test. It is assumed the tests are for
   1086   *    float, vec2, vec3, vec4 in that order.
   1087   *
   1088   * tolerance: (optional)
   1089   *    Allow some tolerance in the comparisons. The tolerance is applied to
   1090   *    both vertex and fragment shaders. The default tolerance is 0, meaning
   1091   *    the values have to be identical.
   1092   *
   1093   * fragmentTolerance: (optional)
   1094   *    Specify a tolerance which only applies to fragment shaders. The
   1095   *    fragment-only tolerance will override the shared tolerance for
   1096   *    fragment shaders if both are specified. Fragment shaders usually
   1097   *    use mediump float precision so they sometimes require higher tolerance
   1098   *    than vertex shaders which use highp by default.
   1099   */
   1100  runFeatureTest: runFeatureTest,
   1101 
   1102  /*
   1103   * Runs a bunch of GLSL tests using the passed in parameters
   1104   *
   1105   * The parameters are:
   1106   *
   1107   * tests:
   1108   *    Array of tests. For each test the following parameters are expected
   1109   *
   1110   *    name:
   1111   *       some description of the test
   1112   *    reference:
   1113   *       parameters for the reference shader (see below)
   1114   *    test:
   1115   *       parameters for the test shader (see below)
   1116   *
   1117   *    The parameter for the reference and test shaders are
   1118   *
   1119   *    shader: the GLSL for the shader
   1120   *    subs: any substitutions you wish to define for the shader.
   1121   *
   1122   *    Each shader is created from a basic template that
   1123   *    defines an input and an output. You can see the
   1124   *    templates at the top of this file. The input and output
   1125   *    change depending on whether or not we are generating
   1126   *    a vertex or fragment shader.
   1127   *
   1128   *    All this code function does is a bunch of string substitutions.
   1129   *    A substitution is defined by $(name). If name is found in
   1130   *    the 'subs' parameter it is replaced. 4 special names exist.
   1131   *
   1132   *    'input' the input to your GLSL. Always a vec4. All change
   1133   *    from 0 to 1 over the quad to be drawn.
   1134   *
   1135   *    'output' the output color. Also a vec4
   1136   *
   1137   *    'emu' a place to insert extra stuff
   1138   *    'extra' a place to insert extra stuff.
   1139   *
   1140   *    You can think of the templates like this
   1141   *
   1142   *       $(extra)
   1143   *       $(emu)
   1144   *
   1145   *       void main() {
   1146   *          // do math to calculate input
   1147   *          ...
   1148   *
   1149   *          $(shader)
   1150   *       }
   1151   *
   1152   *    Your shader first has any subs you provided applied as well
   1153   *    as 'input' and 'output'
   1154   *
   1155   *    It is then inserted into the template which is also provided
   1156   *    with your subs.
   1157   *
   1158   * gridRes: (optional)
   1159   *    The resolution of the mesh to generate. The default is a
   1160   *    1x1 grid but many vertex shaders need a higher resolution
   1161   *    otherwise the only values passed in are the 4 corners
   1162   *    which often have the same value.
   1163   *
   1164   * tolerance: (optional)
   1165   *    Allow some tolerance in the comparisons. The tolerance is applied to
   1166   *    both vertex and fragment shaders. The default tolerance is 0, meaning
   1167   *    the values have to be identical.
   1168   *
   1169   * fragmentTolerance: (optional)
   1170   *    Specify a tolerance which only applies to fragment shaders. The
   1171   *    fragment-only tolerance will override the shared tolerance for
   1172   *    fragment shaders if both are specified. Fragment shaders usually
   1173   *    use mediump float precision so they sometimes require higher tolerance
   1174   *    than vertex shaders which use highp.
   1175   */
   1176  runBasicTest: runBasicTest,
   1177 
   1178  /**
   1179   * Runs a bunch of GLSL tests using the passed in parameters. The
   1180   * expected results are computed as a reference image in JavaScript
   1181   * instead of on the GPU. The parameters are:
   1182   *
   1183   * feature:
   1184   *    the name of the function being tested (eg, sin, dot,
   1185   *    normalize)
   1186   *
   1187   * testFunc:
   1188   *    The prototype of function to be tested not including the
   1189   *    return type.
   1190   *
   1191   * args:
   1192   *    The arguments to the function
   1193   *
   1194   * gridRes: (optional)
   1195   *    The resolution of the mesh to generate. The default is a
   1196   *    1x1 grid but many vertex shaders need a higher resolution
   1197   *    otherwise the only values passed in are the 4 corners
   1198   *    which often have the same value.
   1199   *
   1200   * tests:
   1201   *    Array of tests. It is assumed the tests are for float, vec2,
   1202   *    vec3, vec4 in that order. For each test the following
   1203   *    parameters are expected:
   1204   *
   1205   *       source: the GLSL source code for the tests
   1206   *
   1207   *       generator: a JavaScript function taking four parameters
   1208   *       which evaluates the same function as the GLSL source,
   1209   *       returning its result as a newly allocated array.
   1210   *
   1211   *       tolerance: (optional) a per-test tolerance.
   1212   *
   1213   * extra: (optional)
   1214   *    Extra GLSL code inserted at the top of each test's shader.
   1215   *
   1216   * tolerance: (optional)
   1217   *    Allow some tolerance in the comparisons. The tolerance is applied to
   1218   *    both vertex and fragment shaders. The default tolerance is 0, meaning
   1219   *    the values have to be identical.
   1220   *
   1221   * fragmentTolerance: (optional)
   1222   *    Specify a tolerance which only applies to fragment shaders. The
   1223   *    fragment-only tolerance will override the shared tolerance for
   1224   *    fragment shaders if both are specified. Fragment shaders usually
   1225   *    use mediump float precision so they sometimes require higher tolerance
   1226   *    than vertex shaders which use highp.
   1227   */
   1228  runReferenceImageTest: runReferenceImageTest,
   1229 
   1230  none: false
   1231 };
   1232 
   1233 }());