tor-browser

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

ogles-utils.js (23290B)


      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 OpenGLESTestRunner = (function(){
      7 var wtu = WebGLTestUtils;
      8 var gl;
      9 
     10 var HALF_GRID_MAX_SIZE = 32;
     11 var KNOWN_ATTRIBS = [
     12  "gtf_Vertex",
     13  "gtf_Color"
     14 ];
     15 
     16 var GTFPIXELTOLERANCE = 24;
     17 var GTFACCEPTABLEFAILURECONT = 10;
     18 var GTFAMDPIXELTOLERANCE = 12;
     19 var GTFSCORETOLERANCE = 0.65;
     20 var GTFNCCTOLARANCEZERO = 0.25;
     21 var GTFKERNALSIZE = 5;
     22 
     23 function log(msg) {
     24  // debug(msg);
     25 }
     26 
     27 function compareImages(refData, tstData, width, height, diff) {
     28  function isPixelSame(offset) {
     29    // First do simple check
     30    if (Math.abs(refData[offset + 0] - tstData[offset + 0]) <= GTFPIXELTOLERANCE &&
     31        Math.abs(refData[offset + 1] - tstData[offset + 1]) <= GTFPIXELTOLERANCE &&
     32        Math.abs(refData[offset + 2] - tstData[offset + 2]) <= GTFPIXELTOLERANCE) {
     33      return true;
     34    }
     35 
     36    // TODO: Implement crazy check that's used in OpenGL ES 2.0 conformance tests.
     37    // NOTE: on Desktop things seem to be working. Maybe the more complex check
     38    // is needed for embedded systems?
     39    return false;
     40  }
     41 
     42  var same = true;
     43  for (var yy = 0; yy < height; ++yy) {
     44    for (var xx = 0; xx < width; ++xx) {
     45      var offset = (yy * width + xx) * 4;
     46      var diffOffset = ((height - yy - 1) * width + xx) * 4;
     47      diff[diffOffset + 0] = 0;
     48      diff[diffOffset + 1] = 0;
     49      diff[diffOffset + 2] = 0;
     50      diff[diffOffset + 3] = 255;
     51      if (!isPixelSame(offset)) {
     52        diff[diffOffset] = 255;
     53        if (same) {
     54          same = false;
     55          testFailed("pixel @ (" + xx + ", " + yy + " was [" +
     56                     tstData[offset + 0] + "," +
     57                     tstData[offset + 1] + "," +
     58                     tstData[offset + 2] + "," +
     59                     tstData[offset + 3] + "] expected [" +
     60                     refData[offset + 0] + "," +
     61                     refData[offset + 1] + "," +
     62                     refData[offset + 2] + "," +
     63                     refData[offset + 3] + "]")
     64        }
     65      }
     66    }
     67  }
     68  return same;
     69 }
     70 
     71 function persp(fovy, aspect, n, f) {
     72  var dz = f - n;
     73  var rad = fovy / 2.0 * 3.14159265 / 180;
     74 
     75  var s = Math.sin(rad);
     76  if (dz == 0 || s == 0 || aspect == 0)
     77    return;
     78 
     79  var cot = Math.cos(rad) / s;
     80 
     81  return [
     82    cot / aspect,
     83    0.0,
     84    0.0,
     85    0.0,
     86 
     87    0.0,
     88    cot,
     89    0.0,
     90    0.0,
     91 
     92    0.0,
     93    0.0,
     94    -(f + n) / dz,
     95    -1.0,
     96 
     97    0.0,
     98    0.0,
     99    -2.0 * f * n / dz,
    100    0.0
    101  ];
    102 }
    103 
    104 function setAttribs(attribs, buffers) {
    105  for (var name in attribs) {
    106    var buffer = buffers[name];
    107    if (!buffer) {
    108      testFailed("no buffer for attrib:" + name);
    109      continue;
    110    }
    111    var loc = attribs[name];
    112    log("setup attrib: " + loc + " as " + name);
    113    var buf = gl.createBuffer();
    114    gl.bindBuffer(gl.ARRAY_BUFFER, buf);
    115    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(buffer.data), gl.STATIC_DRAW);
    116    gl.enableVertexAttribArray(loc);
    117    gl.vertexAttribPointer(loc, buffer.numComponents, gl.FLOAT, false, 0, 0);
    118  }
    119 }
    120 
    121 function drawSquare(attribs) {
    122  var buffers = {
    123    "gtf_Vertex": {
    124      data: [
    125        1.0, -1.0, -2.0,
    126        1.0, 1.0, -2.0,
    127        -1.0, -1.0, -2.0,
    128        -1.0, 1.0, -2.0
    129      ],
    130      numComponents: 3
    131    },
    132    "gtf_Color": {
    133      data: [
    134        0.5, 1.0, 0.0,
    135        0.0, 1.0, 1.0,
    136        1.0, 0.0, 0.0,
    137        0.5, 0.0, 1.0
    138      ],
    139      numComponents: 3,
    140    },
    141    "gtf_SecondaryColor": {
    142      data: [
    143        0.5, 0.0, 1.0,
    144        1.0, 0.0, 0.0,
    145        0.0, 1.0, 1.0,
    146        0.5, 1.0, 0.0
    147      ],
    148      numComponents: 3,
    149    },
    150    "gtf_Normal": {
    151      data: [
    152        0.5, 0.0, 1.0,
    153        1.0, 0.0, 0.0,
    154        0.0, 1.0, 1.0,
    155        0.5, 1.0, 0.0
    156      ],
    157      numComponents: 3,
    158    },
    159    "gtf_MultiTexCoord0": {
    160      data: [
    161        1.0, 0.0,
    162        1.0, 1.0,
    163        0.0, 0.0,
    164        0.0, 1.0
    165      ],
    166      numComponents: 2,
    167    },
    168    "gtf_FogCoord": {
    169      data: [
    170        0.0,
    171        1.0,
    172        0.0,
    173        1.0
    174      ],
    175      numComponents: 1,
    176    }
    177  };
    178  setAttribs(attribs, buffers);
    179  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    180 }
    181 
    182 function drawFrontBackSquare(attribs) {
    183  var front = {
    184    "gtf_Vertex": {
    185      data: [
    186        1.0, -1.0, -2.0,
    187        1.0, 0.0, -2.0,
    188        -1.0, -1.0, -2.0,
    189        -1.0, 0.0, -2.0
    190      ],
    191      numComponents: 3
    192    },
    193    "gtf_Color": {
    194      data: [
    195        0.0, 1.0, 0.0,
    196        0.0, 1.0, 0.0,
    197        0.0, 1.0, 0.0,
    198        0.0, 1.0, 0.0
    199      ],
    200      numComponents: 3,
    201    },
    202    "gtf_MultiTexCoord0": {
    203      data: [
    204        1.0, 0.0,
    205        1.0, 0.5,
    206        0.0, 0.0,
    207        0.0, 0.5
    208      ],
    209      numComponents: 2,
    210    }
    211  };
    212  setAttribs(attribs, front);
    213  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    214 
    215  var back = {
    216    "gtf_Vertex": {
    217      data: [
    218        1.0, 1.0, -2.0,
    219        1.0, 0.0, -2.0,
    220        -1.0, 1.0, -2.0,
    221        -1.0, 0.0, -2.0
    222      ],
    223      numComponents: 3
    224    },
    225    "gtf_Color": {
    226      data: [
    227        1.0, 0.0, 0.0,
    228        1.0, 0.0, 0.0,
    229        1.0, 0.0, 0.0,
    230        1.0, 0.0, 0.0
    231      ],
    232      numComponents: 3,
    233    },
    234    "gtf_MultiTexCoord0": {
    235      data: [
    236        1.0, 0.1,
    237        1.0, 0.5,
    238        0.0, 0.1,
    239        0.0, 0.5
    240      ],
    241      numComponents: 2,
    242    }
    243  };
    244  setAttribs(attribs, back);
    245  gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
    246 }
    247 
    248 function drawGrid(attribs, width, height) {
    249  var n = Math.min(Math.floor(Math.max(width, height) / 4), HALF_GRID_MAX_SIZE);
    250 
    251  var numVertices = (n + n) * (n + n) * 6;
    252 
    253  var gridVertices   = [];
    254  var gridColors     = [];
    255  var gridSecColors  = [];
    256  var gridNormals    = [];
    257  var gridFogCoords  = [];
    258  var gridTexCoords0 = [];
    259 
    260  var currentVertex    = 0;
    261  var currentColor     = 0;
    262  var currentSecColor  = 0;
    263  var currentTexCoord0 = 0;
    264  var currentNormal    = 0;
    265  var currentFogCoord  = 0;
    266 
    267  var z = -2.0;
    268  for(var i = -n; i < n; ++i)
    269  {
    270      var x1 = i / n;
    271      var x2 = (i + 1) / n;
    272      for(var j = -n; j < n; ++j)
    273      {
    274          var y1 = j / n;
    275          var y2 = (j + 1) / n;
    276 
    277          // VERTEX 0
    278          gridVertices[currentVertex++] = x1;
    279          gridVertices[currentVertex++] = y1;
    280          gridVertices[currentVertex++] = z;
    281          gridColors[currentColor++] = 1.0 - (x1 + y1 + 2.0) / 4.0;
    282          gridColors[currentColor++] = (x1 + 1.0) / 2.0;
    283          gridColors[currentColor++] = (y1 + 1.0) / 2.0;
    284          gridSecColors[currentSecColor++] = 1.0 - (x2 + y2 + 2.0) / 4.0;
    285          gridSecColors[currentSecColor++] = (x2 + 1.0) / 2.0;
    286          gridSecColors[currentSecColor++] = (y2 + 1.0) / 2.0;
    287          gridTexCoords0[currentTexCoord0++] = (x1 + 1.0) / 2.0;
    288          gridTexCoords0[currentTexCoord0++] = (y1 + 1.0) / 2.0;
    289          gridNormals[currentNormal++] = 1.0 - (x2 + y2 + 2.0) / 4.0;
    290          gridNormals[currentNormal++] = (x2 + 1.0) / 2.0;
    291          gridNormals[currentNormal++] = (y2 + 1.0) / 2.0;
    292          gridFogCoords[currentFogCoord++] = (y1 + 1.0) / 2.0;
    293 
    294          // VERTEX 1
    295          gridVertices[currentVertex++] = x2;
    296          gridVertices[currentVertex++] = y1;
    297          gridVertices[currentVertex++] = z;
    298          gridColors[currentColor++] = 1.0 - (x2 + y1 + 2.0) / 4.0;
    299          gridColors[currentColor++] = (x2 + 1.0) / 2.0;
    300          gridColors[currentColor++] = (y1 + 1.0) / 2.0;
    301          gridSecColors[currentSecColor++] = 1.0 - (x1 + y2 + 2.0) / 4.0;
    302          gridSecColors[currentSecColor++] = (x1 + 1.0) / 2.0;
    303          gridSecColors[currentSecColor++] = (y2 + 1.0) / 2.0;
    304          gridTexCoords0[currentTexCoord0++] = (x2 + 1.0) / 2.0;
    305          gridTexCoords0[currentTexCoord0++] = (y1 + 1.0) / 2.0;
    306          gridNormals[currentNormal++] = 1.0 - (x1 + y2 + 2.0) / 4.0;
    307          gridNormals[currentNormal++] = (x1 + 1.0) / 2.0;
    308          gridNormals[currentNormal++] = (y2 + 1.0) / 2.0;
    309          gridFogCoords[currentFogCoord++] = (y1 + 1.0) / 2.0;
    310 
    311          // VERTEX 2
    312          gridVertices[currentVertex++] = x2;
    313          gridVertices[currentVertex++] = y2;
    314          gridVertices[currentVertex++] = z;
    315          gridColors[currentColor++] = 1.0 - (x2 + y2 + 2.0) / 4.0;
    316          gridColors[currentColor++] = (x2 + 1.0) / 2.0;
    317          gridColors[currentColor++] = (y2 + 1.0) / 2.0;
    318          gridSecColors[currentSecColor++] = 1.0 - (x1 + y1 + 2.0) / 4.0;
    319          gridSecColors[currentSecColor++] = (x1 + 1.0) / 2.0;
    320          gridSecColors[currentSecColor++] = (y1 + 1.0) / 2.0;
    321          gridTexCoords0[currentTexCoord0++] = (x2 + 1.0) / 2.0;
    322          gridTexCoords0[currentTexCoord0++] = (y2 + 1.0) / 2.0;
    323          gridNormals[currentNormal++] = 1.0 - (x1 + y1 + 2.0) / 4.0;
    324          gridNormals[currentNormal++] = (x1 + 1.0) / 2.0;
    325          gridNormals[currentNormal++] = (y1 + 1.0) / 2.0;
    326          gridFogCoords[currentFogCoord++] = (y2 + 1.0) / 2.0;
    327 
    328          // VERTEX 2
    329          gridVertices[currentVertex++] = x2;
    330          gridVertices[currentVertex++] = y2;
    331          gridVertices[currentVertex++] = z;
    332          gridColors[currentColor++] = 1.0 - (x2 + y2 + 2.0) / 4.0;
    333          gridColors[currentColor++] = (x2 + 1.0) / 2.0;
    334          gridColors[currentColor++] = (y2 + 1.0) / 2.0;
    335          gridSecColors[currentSecColor++] = 1.0 - (x1 + y1 + 2.0) / 4.0;
    336          gridSecColors[currentSecColor++] = (x1 + 1.0) / 2.0;
    337          gridSecColors[currentSecColor++] = (y1 + 1.0) / 2.0;
    338          gridTexCoords0[currentTexCoord0++] = (x2 + 1.0) / 2.0;
    339          gridTexCoords0[currentTexCoord0++] = (y2 + 1.0) / 2.0;
    340          gridNormals[currentNormal++] = 1.0 - (x1 + y1 + 2.0) / 4.0;
    341          gridNormals[currentNormal++] = (x1 + 1.0) / 2.0;
    342          gridNormals[currentNormal++] = (y1 + 1.0) / 2.0;
    343          gridFogCoords[currentFogCoord++] = (y2 + 1.0) / 2.0;
    344 
    345          // VERTEX 3
    346          gridVertices[currentVertex++] = x1;
    347          gridVertices[currentVertex++] = y2;
    348          gridVertices[currentVertex++] = z;
    349          gridColors[currentColor++] = 1.0 - (x1 + y2 + 2.0) / 4.0;
    350          gridColors[currentColor++] = (x1 + 1.0) / 2.0;
    351          gridColors[currentColor++] = (y2 + 1.0) / 2.0;
    352          gridSecColors[currentSecColor++] = 1.0 - (x2 + y1 + 2.0) / 4.0;
    353          gridSecColors[currentSecColor++] = (x2 + 1.0) / 2.0;
    354          gridSecColors[currentSecColor++] = (y1 + 1.0) / 2.0;
    355          gridTexCoords0[currentTexCoord0++] = (x1 + 1.0) / 2.0;
    356          gridTexCoords0[currentTexCoord0++] = (y2 + 1.0) / 2.0;
    357          gridNormals[currentNormal++] = 1.0 - (x2 + y1 + 2.0) / 4.0;
    358          gridNormals[currentNormal++] = (x2 + 1.0) / 2.0;
    359          gridNormals[currentNormal++] = (y1 + 1.0) / 2.0;
    360          gridFogCoords[currentFogCoord++] = (y2 + 1.0) / 2.0;
    361 
    362          // VERTEX 0
    363          gridVertices[currentVertex++] = x1;
    364          gridVertices[currentVertex++] = y1;
    365          gridVertices[currentVertex++] = z;
    366          gridColors[currentColor++] = 1.0 - (x1 + y1 + 2.0) / 4.0;
    367          gridColors[currentColor++] = (x1 + 1.0) / 2.0;
    368          gridColors[currentColor++] = (y1 + 1.0) / 2.0;
    369          gridSecColors[currentSecColor++] = 1.0 - (x2 + y2 + 2.0) / 4.0;
    370          gridSecColors[currentSecColor++] = (x2 + 1.0) / 2.0;
    371          gridSecColors[currentSecColor++] = (y2 + 1.0) / 2.0;
    372          gridTexCoords0[currentTexCoord0++] = (x1 + 1.0) / 2.0;
    373          gridTexCoords0[currentTexCoord0++] = (y1 + 1.0) / 2.0;
    374          gridNormals[currentNormal++] = 1.0 - (x2 + y2 + 2.0) / 4.0;
    375          gridNormals[currentNormal++] = (x2 + 1.0) / 2.0;
    376          gridNormals[currentNormal++] = (y2 + 1.0) / 2.0;
    377          gridFogCoords[currentFogCoord++] = (y1 + 1.0) / 2.0;
    378      }
    379  }
    380 
    381  var buffers = {
    382    "gtf_Vertex":         { data: gridVertices,   numComponents: 3 },
    383    "gtf_Color":          { data: gridColors,     numComponents: 3 },
    384    "gtf_SecondaryColor": { data: gridSecColors,  numComponents: 3 },
    385    "gtf_Normal":         { data: gridNormals,    numComponents: 3 },
    386    "gtf_FogCoord":       { data: gridFogCoords,  numComponents: 1 },
    387    "gtf_MultiTexCoord0": { data: gridTexCoords0, numComponents: 2 }
    388  };
    389  setAttribs(attribs, buffers);
    390  gl.drawArrays(gl.TRIANGLES, 0, numVertices);
    391 }
    392 
    393 var MODEL_FUNCS = {
    394  square:          drawSquare,
    395  frontbacksquare: drawFrontBackSquare,
    396  grid:            drawGrid
    397 };
    398 
    399 function drawWithProgram(program, programInfo, test) {
    400  gl.useProgram(program);
    401  var attribs = { };
    402 
    403  var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
    404  for (var ii = 0; ii < numAttribs; ++ii) {
    405    var info = gl.getActiveAttrib(program, ii);
    406    var name = info.name;
    407    var location = gl.getAttribLocation(program, name);
    408    attribs[name] = location;
    409 
    410    if (KNOWN_ATTRIBS.indexOf(name) < 0) {
    411      testFailed("unknown attrib:" + name)
    412    }
    413  }
    414 
    415  var uniforms = { };
    416  var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
    417  for (var ii = 0; ii < numUniforms; ++ii) {
    418    var info = gl.getActiveUniform(program, ii);
    419    var name = info.name;
    420    if (name.match(/\[0\]$/)) {
    421      name = name.substr(0, name.length - 3);
    422    }
    423    var location = gl.getUniformLocation(program, name);
    424    uniforms[name] = {location: location};
    425  }
    426 
    427  var getUniformLocation = function(name) {
    428    var uniform = uniforms[name];
    429    if (uniform) {
    430      uniform.used = true;
    431      return uniform.location;
    432    }
    433    return null;
    434  }
    435 
    436  // Set known uniforms
    437  var loc = getUniformLocation("gtf_ModelViewProjectionMatrix");
    438  if (loc)  {
    439    gl.uniformMatrix4fv(
    440      loc,
    441      false,
    442      persp(60, 1, 1, 30));
    443  }
    444  var loc = getUniformLocation("viewportwidth");
    445  if (loc) {
    446    gl.uniform1f(loc, gl.canvas.width);
    447  }
    448  var loc = getUniformLocation("viewportheight");
    449  if (loc) {
    450    gl.uniform1f(loc, gl.canvas.height);
    451  }
    452 
    453  // Set test specific uniforms
    454  for (var name in programInfo.uniforms) {
    455    var location = getUniformLocation(name);
    456    if (!location) {
    457      continue;
    458    }
    459    var uniform = programInfo.uniforms[name];
    460    var type = uniform.type;
    461    var value = uniform.value;
    462    var transpose = uniform.transpose;
    463    if (transpose !== undefined) {
    464      log("gl." + type + '("' + name + '", ' + transpose + ", " + value + ")");
    465      gl[type](location, transpose, value);
    466    } else if (!type.match("v$")) {
    467      var args = [location];
    468      for (var ii = 0; ii < value.length; ++ii) {
    469        args.push(value[ii]);
    470      }
    471      gl[type].apply(gl, args);
    472      log("gl." + type + '("' + name + '", ' + args.slice(1) + ")");
    473    } else {
    474      log("gl." + type + '("' + name + '", ' + value + ")");
    475      gl[type](location, value);
    476    }
    477    var err = gl.getError();
    478    if (err != gl.NO_ERROR) {
    479      testFailed(wtu.glEnumToString(gl, err) + " generated setting uniform: " + name);
    480    }
    481  }
    482 
    483  // Filter out specified built-in uniforms
    484  if (programInfo.builtin_uniforms) {
    485    var num_builtins_found = 0;
    486    var valid_values = programInfo.builtin_uniforms.valid_values;
    487    for (var index in valid_values) {
    488      var uniform = uniforms[valid_values[index]];
    489      if (uniform) {
    490        ++num_builtins_found;
    491        uniform.builtin = true;
    492      }
    493    }
    494 
    495    var min_required = programInfo.builtin_uniforms.min_required;
    496    if (num_builtins_found < min_required) {
    497      testFailed("only found " + num_builtins_found + " of " + min_required +
    498                 " required built-in uniforms: " + valid_values);
    499    }
    500  }
    501 
    502  // Check for unset uniforms
    503  for (var name in uniforms) {
    504    var uniform = uniforms[name];
    505    if (!uniform.used && !uniform.builtin) {
    506      testFailed("uniform " + name + " never set");
    507    }
    508  }
    509 
    510 
    511  for (var state in test.state) {
    512    var fields = test.state[state];
    513    switch (state) {
    514    case 'depthrange':
    515      gl.depthRange(fields.near, fields.far);
    516      break;
    517    default:
    518      testFailed("unknown state: " + state)
    519    }
    520  }
    521 
    522  gl.clearColor(0, 0, 0, 0);
    523  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    524 
    525  var model = test.model || "square";
    526  var fn = MODEL_FUNCS[model];
    527  if (!fn) {
    528    testFailed("unknown model type: " + model)
    529  } else {
    530    log("draw as: " + model)
    531    fn(attribs, gl.canvas.width, gl.canvas.height);
    532  }
    533 
    534  var pixels = new Uint8Array(gl.canvas.width * gl.canvas.height * 4);
    535  gl.readPixels(0, 0, gl.canvas.width, gl.canvas.height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
    536  return {
    537    width: gl.canvas.width,
    538    height: gl.canvas.height,
    539    pixels: pixels,
    540    img: wtu.makeImageFromCanvas(gl.canvas)
    541  };
    542 }
    543 
    544 function runProgram(programInfo, test, label, callback) {
    545  var shaders = [];
    546  var source = [];
    547  var count = 0;
    548 
    549  function loadShader(path, type, index) {
    550    wtu.loadTextFileAsync(path, function(success, text) {
    551      addShader(success, text, type, path, index);
    552    });
    553  }
    554 
    555  function addShader(success, text, type, path, index) {
    556    ++count;
    557    if (!success) {
    558      testFailed("could not load: " + path);
    559    } else {
    560      var shader = wtu.loadShader(gl, text, type);
    561      shaders.push(shader);
    562      source[index] = text;
    563    }
    564    if (count == 2) {
    565      var result;
    566      if (shaders.length == 2) {
    567        debug("");
    568        if (!quietMode()) {
    569        var consoleDiv = document.getElementById("console");
    570          wtu.addShaderSources(
    571              gl, consoleDiv, label + " vertex shader", shaders[0], source[0],
    572              programInfo.vertexShader);
    573          wtu.addShaderSources(
    574              gl, consoleDiv, label + " fragment shader", shaders[1], source[1],
    575              programInfo.fragmentShader);
    576        }
    577        var program = wtu.createProgram(gl, shaders[0], shaders[1]);
    578        result = drawWithProgram(program, programInfo, test);
    579      }
    580      callback(result);
    581    }
    582  }
    583 
    584  loadShader(programInfo.vertexShader, gl.VERTEX_SHADER, 0);
    585  loadShader(programInfo.fragmentShader, gl.FRAGMENT_SHADER, 1);
    586 }
    587 
    588 function compareResults(expected, actual) {
    589  var width = expected.width;
    590  var height = expected.height;
    591  var canvas = document.createElement("canvas");
    592  canvas.width = width;
    593  canvas.height = height;
    594  var ctx = canvas.getContext("2d");
    595  var imgData = ctx.getImageData(0, 0, width, height);
    596  var tolerance = 0;
    597 
    598  var expData = expected.pixels;
    599  var actData = actual.pixels;
    600 
    601  var same = compareImages(expData, actData, width, height, imgData.data);
    602 
    603  var console = document.getElementById("console");
    604  var diffImg = null;
    605  if (!same) {
    606    ctx.putImageData(imgData, 0, 0);
    607    diffImg = wtu.makeImageFromCanvas(canvas);
    608  }
    609 
    610  if (!quietMode()) {
    611    var div = document.createElement("div");
    612    div.className = "testimages";
    613    wtu.insertImage(div, "reference", expected.img);
    614    wtu.insertImage(div, "test", actual.img);
    615    if (diffImg) {
    616      wtu.insertImage(div, "diff", diffImg);
    617    }
    618    div.appendChild(document.createElement('br'));
    619 
    620    console.appendChild(div);
    621  }
    622 
    623  if (!same) {
    624    testFailed("images are different");
    625  } else {
    626    testPassed("images are the same");
    627  }
    628 
    629  if (!quietMode())
    630    console.appendChild(document.createElement('hr'));
    631 }
    632 
    633 function runCompareTest(test, callback) {
    634  debug("");
    635  debug("test: " + test.name);
    636  var results = [];
    637  var count = 0;
    638 
    639  function storeResults(index) {
    640    return function(result) {
    641      results[index] = result;
    642      ++count;
    643      if (count == 2) {
    644        compareResults(results[0], results[1]);
    645        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
    646        callback();
    647      }
    648    }
    649  }
    650 
    651  runProgram(test.referenceProgram, test, "reference", storeResults(0));
    652  runProgram(test.testProgram, test, "test", storeResults(1));
    653 }
    654 
    655 function runBuildTest(test, callback) {
    656  debug("");
    657  debug("test: " + test.name);
    658 
    659  var shaders = [null, null];
    660  var source = ["",""];
    661  var success = [undefined, undefined];
    662  var count = 0;
    663 
    664  function loadShader(path, type, index) {
    665    if (path == "empty") {
    666      shaders[index] = gl.createShader();
    667      success[index] = true;
    668      source[index] = "/* empty */";
    669      attachAndLink();
    670    } else {
    671      wtu.loadTextFileAsync(path, function(loadSuccess, text) {
    672        if (!loadSuccess) {
    673          success[index] = false;
    674          source[index] = "/* could not load */";
    675          testFailed("could not load:" + path);
    676        } else {
    677          source[index] = text;
    678          shaders[index] = wtu.loadShader(gl, text, type, function(index) {
    679            return function(msg) {
    680              success[index] = false
    681            }
    682          }(index));
    683          if (success[index] === undefined) {
    684            success[index] = true;
    685          }
    686        }
    687        attachAndLink();
    688      });
    689    }
    690  }
    691 
    692  function attachAndLink() {
    693    ++count;
    694    if (count == 2) {
    695      if (!quietMode()) {
    696        debug("");
    697        var c = document.getElementById("console");
    698        wtu.addShaderSource(
    699            c, "vertex shader", source[0], test.testProgram.vertexShader);
    700        debug("compile: " + (success[0] ? "success" : "fail"));
    701        wtu.addShaderSource(
    702            c, "fragment shader", source[1], test.testProgram.fragmentShader);
    703        debug("compile: " + (success[1] ? "success" : "fail"));
    704      }
    705      compileSuccess = (success[0] && success[1]);
    706      if (!test.compstat) {
    707        if (compileSuccess) {
    708          testFailed("expected compile failure but was successful");
    709        } else {
    710          testPassed("expected compile failure and it failed");
    711        }
    712      } else {
    713        if (compileSuccess) {
    714          testPassed("expected compile success and it was successful");
    715        } else {
    716          testFailed("expected compile success but it failed");
    717        }
    718        var linkSuccess = true;
    719        var program = wtu.createProgram(gl, shaders[0], shaders[1], function() {
    720          linkSuccess = false;
    721        });
    722        if (linkSuccess !== test.linkstat) {
    723          testFailed("expected link to " + (test.linkstat ? "succeed" : "fail"));
    724        } else {
    725          testPassed("shaders compiled and linked as expected.");
    726        }
    727      }
    728      callback();
    729    }
    730  }
    731 
    732  loadShader(test.testProgram.vertexShader, gl.VERTEX_SHADER, 0);
    733  loadShader(test.testProgram.fragmentShader, gl.FRAGMENT_SHADER, 1);
    734 }
    735 
    736 var testPatterns = {
    737  compare: runCompareTest,
    738  build: runBuildTest,
    739 
    740  dummy: null  // just here to mark the end
    741 };
    742 
    743 function LogGLCall(functionName, args) {
    744  console.log("gl." + functionName + "(" +
    745            WebGLDebugUtils.glFunctionArgsToString(functionName, args) + ")");
    746 }
    747 
    748 // Runs the tests async since they will load shaders.
    749 function run(obj) {
    750  description();
    751 
    752  var canvas = document.getElementById("example");
    753  gl = wtu.create3DContext(canvas);
    754  if (window.WebGLDebugUtils) {
    755    gl = WebGLDebugUtils.makeDebugContext(gl, undefined, LogGLCall);
    756  }
    757  if (!gl) {
    758    testFailed("context does not exist");
    759    finishTest();
    760    return;
    761  }
    762 
    763  if (gl.canvas.width != 500 || gl.canvas.height != 500) {
    764    testFailed("canvas must be 500x500 pixels: Several shaders are hard coded to this size.");
    765  }
    766 
    767  var tests = obj.tests;
    768  var ndx = 0;
    769 
    770  function runNextTest() {
    771    if (ndx < tests.length) {
    772      var test = tests[ndx++];
    773      var fn = testPatterns[test.pattern];
    774      if (!fn) {
    775        testFailed("test pattern: " + test.pattern + " not supoprted")
    776        runNextTest();
    777      } else {
    778        fn(test, runNextTest);
    779      }
    780    } else {
    781      finishTest();
    782    }
    783  }
    784  runNextTest();
    785 }
    786 
    787 return {
    788  run: run,
    789 };
    790 }());