tor-browser

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

webgl-multi-draw.html (35060B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <title>WebGL ANGLE_multi_draw Conformance Tests</title>
      6 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
      7 <script src="../../js/desktop-gl-constants.js"></script>
      8 <script src="../../js/js-test-pre.js"></script>
      9 <script src="../../js/webgl-test-utils.js"></script>
     10 <script src="../../js/tests/compositing-test.js"></script>
     11 <script src="../../js/tests/invalid-vertex-attrib-test.js"></script>
     12 </head>
     13 <body>
     14 <script id="vshaderIllegalDrawID" type="x-shader/x-vertex">
     15 attribute vec2 vPosition;
     16 varying vec4 color;
     17 void main()
     18 {
     19  color = vec4(1.0, 0.0, 0.0, 1.0);
     20  gl_Position = vec4(vPosition * 2.0 - 1.0, gl_DrawID, 1);
     21 }
     22 </script>
     23 <script id="vshaderDrawIDZero" type="x-shader/x-vertex">
     24 #extension GL_ANGLE_multi_draw : require
     25 attribute vec2 vPosition;
     26 varying vec4 color;
     27 void main()
     28 {
     29  if (gl_DrawID == 0) {
     30    color = vec4(0, 1, 0, 1);
     31  } else {
     32    color = vec4(1, 0, 0, 1);
     33  }
     34  gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1);
     35 }
     36 </script>
     37 <!-- The behavior of the shaders below is described in runPixelTests() -->
     38 <script id="vshaderWithDrawID" type="x-shader/x-vertex">
     39 #extension GL_ANGLE_multi_draw : require
     40 attribute vec2 vPosition;
     41 attribute float vInstance;
     42 varying vec4 color;
     43 void main()
     44 {
     45  // color_id = (gl_DrawID / 2) % 3
     46  float quad_id = float(gl_DrawID / 2);
     47  float color_id = quad_id - (3.0 * floor(quad_id / 3.0));
     48  if (color_id < 0.5) {
     49    color = vec4(1, 0, 0, 1);
     50  } else if (color_id < 1.5) {
     51    color = vec4(0, 1, 0, 1);
     52  } else {
     53    color = vec4(0, 0, 1, 1);
     54  }
     55  mat3 transform = mat3(1.0);
     56  // vInstance starts at 1.0 on instanced calls
     57  if (vInstance >= 1.0) {
     58    transform[0][0] = 0.5;
     59    transform[1][1] = 0.5;
     60  }
     61  if (vInstance == 1.0) {
     62  } else if (vInstance == 2.0) {
     63      transform[2][0] = 0.5;
     64  } else if (vInstance == 3.0) {
     65      transform[2][1] = 0.5;
     66  } else if (vInstance == 4.0) {
     67      transform[2][0] = 0.5;
     68      transform[2][1] = 0.5;
     69  }
     70  gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
     71 }
     72 </script>
     73 <script id="vshaderEmulatedDrawID" type="x-shader/x-vertex">
     74 uniform int drawID;
     75 attribute vec2 vPosition;
     76 attribute float vInstance;
     77 varying vec4 color;
     78 void main()
     79 {
     80  float quad_id = float(drawID / 2);
     81  float color_id = quad_id - (3.0 * floor(quad_id / 3.0));
     82  if (color_id == 0.0) {
     83    color = vec4(1, 0, 0, 1);
     84  } else if (color_id == 1.0) {
     85    color = vec4(0, 1, 0, 1);
     86  } else {
     87    color = vec4(0, 0, 1, 1);
     88  }
     89  mat3 transform = mat3(1.0);
     90  // vInstance starts at 1.0 on instanced calls
     91  if (vInstance >= 1.0) {
     92    transform[0][0] = 0.5;
     93    transform[1][1] = 0.5;
     94  }
     95  if (vInstance == 1.0) {
     96  } else if (vInstance == 2.0) {
     97      transform[2][0] = 0.5;
     98  } else if (vInstance == 3.0) {
     99      transform[2][1] = 0.5;
    100  } else if (vInstance == 4.0) {
    101      transform[2][0] = 0.5;
    102      transform[2][1] = 0.5;
    103  }
    104  gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
    105 }
    106 </script>
    107 <script id="vshaderNoDrawID" type="x-shader/x-vertex">
    108 attribute vec2 vPosition;
    109 attribute float vInstance;
    110 varying vec4 color;
    111 void main()
    112 {
    113  color = vec4(1.0, 0.0, 0.0, 1.0);
    114  mat3 transform = mat3(1.0);
    115  // vInstance starts at 1.0 on instanced calls
    116  if (vInstance >= 1.0) {
    117    transform[0][0] = 0.5;
    118    transform[1][1] = 0.5;
    119  }
    120  if (vInstance == 1.0) {
    121  } else if (vInstance == 2.0) {
    122      transform[2][0] = 0.5;
    123  } else if (vInstance == 3.0) {
    124      transform[2][1] = 0.5;
    125  } else if (vInstance == 4.0) {
    126      transform[2][0] = 0.5;
    127      transform[2][1] = 0.5;
    128  }
    129  gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
    130 }
    131 </script>
    132 <script id="fshader" type="x-shader/x-fragment">
    133 precision mediump float;
    134 varying vec4 color;
    135 void main() {
    136  gl_FragColor = color;
    137 }
    138 </script>
    139 <div id="description"></div>
    140 <canvas id="canvas" width="128" height="128"> </canvas>
    141 <div id="console"></div>
    142 
    143 <script>
    144 "use strict";
    145 description("This test verifies the functionality of the ANGLE_multi_draw extension, if it is available.");
    146 
    147 const wtu = WebGLTestUtils;
    148 const canvas = document.getElementById("canvas");
    149 const gl = wtu.create3DContext(canvas);
    150 const instancedExt = gl && gl.getExtension('ANGLE_instanced_arrays');
    151 const bufferUsageSet = [ gl.STATIC_DRAW, gl.DYNAMIC_DRAW ];
    152 
    153 // Check if the extension is either both enabled and supported or
    154 // not enabled and not supported.
    155 function runSupportedTest(extensionName, extensionEnabled) {
    156  const supported = gl.getSupportedExtensions();
    157  if (supported.indexOf(extensionName) >= 0) {
    158    if (extensionEnabled) {
    159      testPassed(extensionName + ' listed as supported and getExtension succeeded');
    160      return true;
    161    } else {
    162      testFailed(extensionName + ' listed as supported but getExtension failed');
    163    }
    164  } else {
    165    if (extensionEnabled) {
    166      testFailed(extensionName + ' not listed as supported but getExtension succeeded');
    167    } else {
    168      testPassed(extensionName + ' not listed as supported and getExtension failed -- this is legal');
    169    }
    170  }
    171  return false;
    172 }
    173 
    174 function runTest() {
    175  if (!gl) {
    176    return function() {
    177      testFailed('WebGL context does not exist');
    178    }
    179  }
    180 
    181  const extensionName = 'WEBGL_multi_draw';
    182  const ext = gl.getExtension(extensionName);
    183  if (!runSupportedTest(extensionName, ext)) {
    184    return;
    185  }
    186 
    187  doTest(ext, false);
    188  doTest(ext, true);
    189 }
    190 
    191 function doTest(ext, instanced) {
    192 
    193  function runValidationTests(bufferUsage) {
    194    const vertexBuffer = gl.createBuffer();
    195    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    196    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.2,0.2, 0.8,0.2, 0.5,0.8 ]), bufferUsage);
    197 
    198    const indexBuffer = gl.createBuffer();
    199    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    200    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([ 0, 1, 2, 0]), bufferUsage);
    201 
    202    const instanceBuffer = gl.createBuffer();
    203    gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
    204    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 2, 3 ]), bufferUsage);
    205 
    206    const program = wtu.setupProgram(gl, ["vshaderNoDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]);
    207    expectTrue(program != null, "can compile simple program");
    208 
    209    function setupDrawArrays() {
    210      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    211      gl.enableVertexAttribArray(0);
    212      gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    213    }
    214 
    215    function setupDrawElements() {
    216      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    217      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    218      gl.enableVertexAttribArray(0);
    219      gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    220    }
    221 
    222    function setupInstanced() {
    223      gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
    224      gl.enableVertexAttribArray(1);
    225      gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
    226      if (wtu.getDefault3DContextVersion() < 2) {
    227        instancedExt.vertexAttribDivisorANGLE(1, 1);
    228      } else {
    229        gl.vertexAttribDivisor(1, 1);
    230      }
    231    }
    232 
    233    function setupDrawArraysInstanced() {
    234      setupDrawArrays();
    235      setupInstanced();
    236    }
    237 
    238    function setupDrawElementsInstanced() {
    239      setupDrawElements();
    240      setupInstanced();
    241    }
    242 
    243    // Wrap a draw call in a function to setup the draw call, execute,
    244    // and check errors.
    245    // The `drawFunc` is one of the extension entrypoints being tested. It may
    246    // be undefined if that entrypoint is not supported on the context
    247    function makeDrawCheck(drawFunc, setup) {
    248      if (!drawFunc) {
    249        return function() {};
    250      }
    251      return function(f_args, expect, msg) {
    252        setup();
    253        drawFunc.apply(ext, f_args);
    254        wtu.glErrorShouldBe(gl, expect, drawFunc.name + " " + msg);
    255        gl.disableVertexAttribArray(0);
    256        gl.disableVertexAttribArray(1);
    257      }
    258    }
    259 
    260    const checkMultiDrawArrays = makeDrawCheck(
    261        ext.multiDrawArraysWEBGL, setupDrawArrays);
    262    const checkMultiDrawElements = makeDrawCheck(
    263        ext.multiDrawElementsWEBGL, setupDrawElements);
    264    const checkMultiDrawArraysInstanced = makeDrawCheck(
    265        ext.multiDrawArraysInstancedWEBGL, setupDrawArraysInstanced);
    266    const checkMultiDrawElementsInstanced = makeDrawCheck(
    267        ext.multiDrawElementsInstancedWEBGL, setupDrawElementsInstanced);
    268 
    269    gl.useProgram(program);
    270 
    271    // Check that drawing a single triangle works
    272    if (!instanced) {
    273      checkMultiDrawArrays(
    274        [gl.TRIANGLES, [0], 0, [3], 0, 1],
    275        gl.NO_ERROR, "with gl.TRIANGLES");
    276      checkMultiDrawElements(
    277        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, 1],
    278        gl.NO_ERROR, "with gl.TRIANGLES");
    279    } else {
    280      checkMultiDrawElementsInstanced(
    281        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 1],
    282        gl.NO_ERROR, "with gl.TRIANGLES");
    283      checkMultiDrawArraysInstanced(
    284        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, 1],
    285        gl.NO_ERROR, "with gl.TRIANGLES");
    286    }
    287 
    288    // Zero drawcount permitted
    289    if (!instanced) {
    290      checkMultiDrawArrays(
    291        [gl.TRIANGLES, [0], 0, [3], 0, 0],
    292        gl.NO_ERROR, "with drawcount == 0");
    293      checkMultiDrawElements(
    294        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, 0],
    295        gl.NO_ERROR, "with drawcount == 0");
    296    } else {
    297      checkMultiDrawElementsInstanced(
    298        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 0],
    299        gl.NO_ERROR, "with drawcount == 0");
    300      checkMultiDrawArraysInstanced(
    301        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, 0],
    302        gl.NO_ERROR, "with drawcount == 0");
    303    }
    304 
    305    // Check negative drawcount
    306    if (!instanced) {
    307      checkMultiDrawArrays(
    308        [gl.TRIANGLES, [0], 0, [3], 0, -1],
    309        gl.INVALID_VALUE, "with drawcount < 0");
    310      checkMultiDrawElements(
    311        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, -1],
    312        gl.INVALID_VALUE, "with drawcount < 0");
    313    } else {
    314      checkMultiDrawElementsInstanced(
    315        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, -1],
    316        gl.INVALID_VALUE, "with drawcount < 0");
    317      checkMultiDrawArraysInstanced(
    318        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, -1],
    319        gl.INVALID_VALUE, "with drawcount < 0");
    320    }
    321 
    322    // Check offsets greater than array length
    323    if (!instanced) {
    324      checkMultiDrawArrays(
    325        [gl.TRIANGLES, [0], 1, [3], 0, 1],
    326        gl.INVALID_OPERATION, "with firstsStart >= firstsList.length");
    327      checkMultiDrawArrays(
    328        [gl.TRIANGLES, [0], 0, [3], 1, 1],
    329        gl.INVALID_OPERATION, "with countsStart >= countsList.length");
    330 
    331      checkMultiDrawElements(
    332        [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, 1],
    333        gl.INVALID_OPERATION, "with countsStart >= countsList.length");
    334      checkMultiDrawElements(
    335        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, 1],
    336        gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length");
    337    } else {
    338      checkMultiDrawArraysInstanced(
    339        [gl.TRIANGLES, [0], 1, [3], 0, [1], 0, 1],
    340        gl.INVALID_OPERATION, "with firstsStart >= firstsList.length");
    341      checkMultiDrawArraysInstanced(
    342        [gl.TRIANGLES, [0], 0, [3], 1, [1], 0, 1],
    343        gl.INVALID_OPERATION, "with countsStart >= countsList.length");
    344      checkMultiDrawArraysInstanced(
    345        [gl.TRIANGLES, [0], 0, [3], 0, [1], 1, 1],
    346        gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length");
    347 
    348      checkMultiDrawElementsInstanced(
    349        [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 1],
    350        gl.INVALID_OPERATION, "with countsStart >= countsList.length");
    351      checkMultiDrawElementsInstanced(
    352        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, [1], 0, 1],
    353        gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length");
    354      checkMultiDrawElementsInstanced(
    355        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 1, 1],
    356        gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length");
    357    }
    358  }
    359 
    360  function runShaderTests(bufferUsage) {
    361    const illegalProgram = wtu.setupProgram(gl, ["vshaderIllegalDrawID", "fshader"], ["vPosition"], [0]);
    362    expectTrue(illegalProgram == null, "cannot compile program with gl_DrawID but no extension directive");
    363 
    364    const drawIDProgram = wtu.setupProgram(gl, ["vshaderDrawIDZero", "fshader"], ["vPosition"], [0]);
    365    wtu.setupProgram(gl, ["vshaderDrawIDZero", "fshader"], ["vPosition"], [0]);
    366    expectTrue(drawIDProgram !== null, "can compile program with gl_DrawID");
    367    gl.useProgram(drawIDProgram);
    368    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    369    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0,1, 0,1, 1,0, 1,1 ]), bufferUsage);
    370    gl.enableVertexAttribArray(0);
    371    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    372    gl.drawArrays(gl.TRIANGLES, 0, 6);
    373    wtu.checkCanvas(gl, [0, 255, 0, 255], "gl_DrawID is 0 for non-Multi* draw calls", 0);
    374  }
    375 
    376  function runPixelTests(bufferUsage, useSharedArrayBuffer) {
    377    // An array of quads is tiled across the screen.
    378    // gl_DrawID is checked by using it to select the color of the draw.
    379    // Instanced entrypoints are tested here scaling and then instancing the
    380    // array of quads over four quadrants on the screen.
    381 
    382    // These tests also include "manyDraw" tests which emulate a multiDraw with
    383    // a Javascript for-loop and gl_DrawID with a uniform constiable. They are
    384    // included to ensure the test is written correctly.
    385 
    386    const width = gl.canvas.width;
    387    const height = gl.canvas.height;
    388    const x_count = 8;
    389    const y_count = 8;
    390    const quad_count = x_count * y_count;
    391    const tri_count = quad_count * 2;
    392    const tileSize = [ 1/x_count, 1/y_count ];
    393    const tilePixelSize = [ Math.floor(width / x_count), Math.floor(height / y_count) ];
    394    const quadRadius = [ 0.25 * tileSize[0], 0.25 * tileSize[1] ];
    395    const pixelCheckSize = [ Math.floor(quadRadius[0] * width), Math.floor(quadRadius[1] * height) ];
    396 
    397    function getTileCenter(x, y) {
    398      return [ tileSize[0] * (0.5 + x), tileSize[1] * (0.5 + y) ];
    399    }
    400 
    401    function getQuadVertices(x, y) {
    402      const center = getTileCenter(x, y);
    403      return [
    404        [center[0] - quadRadius[0], center[1] - quadRadius[1], 0],
    405        [center[0] + quadRadius[0], center[1] - quadRadius[1], 0],
    406        [center[0] + quadRadius[0], center[1] + quadRadius[1], 0],
    407        [center[0] - quadRadius[0], center[1] + quadRadius[1], 0],
    408      ]
    409    }
    410 
    411    const indicesData = [];
    412    const verticesData = [];
    413    const nonIndexedVerticesData = [];
    414    {
    415      const is = new Uint16Array([0, 1, 2, 0, 2, 3]);
    416      for (let y = 0; y < y_count; ++y) {
    417        for (let x = 0; x < x_count; ++x) {
    418          const quadIndex = y * x_count + x;
    419          const starting_index = 4 * quadIndex;
    420          const vs = getQuadVertices(x, y);
    421          for (let i = 0; i < is.length; ++i) {
    422            indicesData.push(starting_index + is[i]);
    423          }
    424          for (let i = 0; i < vs.length; ++i) {
    425            for (let v = 0; v < vs[i].length; ++v) verticesData.push(vs[i][v]);
    426          }
    427          for (let i = 0; i < is.length; ++i) {
    428            for (let v = 0; v < vs[is[i]].length; ++v) nonIndexedVerticesData.push(vs[is[i]][v]);
    429          }
    430        }
    431      }
    432    }
    433 
    434    const indices = new Uint16Array(indicesData);
    435    const vertices = new Float32Array(verticesData);
    436    const nonIndexedVertices = new Float32Array(nonIndexedVerticesData);
    437 
    438    const indexBuffer = gl.createBuffer();
    439    const vertexBuffer = gl.createBuffer();
    440    const nonIndexedVertexBuffer = gl.createBuffer();
    441    const instanceBuffer = gl.createBuffer();
    442 
    443    gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer);
    444    gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage);
    445 
    446    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    447    gl.bufferData(gl.ARRAY_BUFFER, vertices, bufferUsage);
    448 
    449    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    450    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, bufferUsage);
    451 
    452    gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
    453    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), bufferUsage);
    454 
    455    function checkResult(config, msg) {
    456      const rects = [];
    457      const expected = [
    458        [255, 0, 0, 255],
    459        [0, 255, 0, 255],
    460        [0, 0, 255, 255],
    461      ];
    462      for (let y = 0; y < y_count; ++y) {
    463        for (let x = 0; x < x_count; ++x) {
    464          const center_x = x * tilePixelSize[0] + Math.floor(tilePixelSize[0] / 2);
    465          const center_y = y * tilePixelSize[1] + Math.floor(tilePixelSize[1] / 2);
    466          const quadID = y * x_count + x;
    467          const colorID = config.drawID ? quadID % 3 : 0;
    468          if (config.instanced) {
    469            rects.push(wtu.makeCheckRect(
    470                center_x / 2 - Math.floor(pixelCheckSize[0] / 4),
    471                center_y / 2 - Math.floor(pixelCheckSize[1] / 4),
    472                pixelCheckSize[0] / 2,
    473                pixelCheckSize[1] / 2,
    474                expected[colorID],
    475                msg + " (" + x + "," + y + ")", 0));
    476            rects.push(wtu.makeCheckRect(
    477                center_x / 2 - Math.floor(pixelCheckSize[0] / 4) + width / 2,
    478                center_y / 2 - Math.floor(pixelCheckSize[1] / 4),
    479                pixelCheckSize[0] / 2,
    480                pixelCheckSize[1] / 2,
    481                expected[colorID],
    482                msg + " (" + x + "," + y + ")", 0));
    483            rects.push(wtu.makeCheckRect(
    484                center_x / 2 - Math.floor(pixelCheckSize[0] / 4),
    485                center_y / 2 - Math.floor(pixelCheckSize[1] / 4) + height / 2,
    486                pixelCheckSize[0] / 2,
    487                pixelCheckSize[1] / 2,
    488                expected[colorID],
    489                msg + " (" + x + "," + y + ")", 0));
    490            rects.push(wtu.makeCheckRect(
    491                center_x / 2 - Math.floor(pixelCheckSize[0] / 4) + width / 2,
    492                center_y / 2 - Math.floor(pixelCheckSize[1] / 4) + height / 2,
    493                pixelCheckSize[0] / 2,
    494                pixelCheckSize[1] / 2,
    495                expected[colorID],
    496                msg + " (" + x + "," + y + ")", 0));
    497          } else {
    498            rects.push(wtu.makeCheckRect(
    499                center_x - Math.floor(pixelCheckSize[0] / 2),
    500                center_y - Math.floor(pixelCheckSize[1] / 2),
    501                pixelCheckSize[0],
    502                pixelCheckSize[1],
    503                expected[colorID],
    504                msg + " (" + x + "," + y + ")", 0));
    505          }
    506        }
    507      }
    508      wtu.checkCanvasRects(gl, rects);
    509    }
    510 
    511    function newIntArray(count) {
    512      if (!useSharedArrayBuffer) {
    513        return new Int32Array(count);
    514      }
    515      let sab = new SharedArrayBuffer(count * Int32Array.BYTES_PER_ELEMENT);
    516      return new Int32Array(sab);
    517    }
    518 
    519    const firsts = newIntArray(tri_count);
    520    const counts = newIntArray(tri_count);
    521    const offsets = newIntArray(tri_count);
    522    const instances = newIntArray(tri_count);
    523 
    524    for (let i = 0; i < firsts.length; ++i) firsts[i] = i * 3;
    525    counts.fill(3);
    526    for (let i = 0; i < offsets.length; ++i) offsets[i] = i * 3 * 2;
    527    instances.fill(4);
    528 
    529    const firstsOffset = 47;
    530    const countsOffset = firstsOffset + firsts.length;
    531    const offsetsOffset = countsOffset + counts.length;
    532    const instancesOffset = offsetsOffset + instances.length;
    533 
    534    const buffer = newIntArray(firstsOffset + firsts.length + counts.length + offsets.length + instances.length);
    535    buffer.set(firsts, firstsOffset);
    536    buffer.set(counts, countsOffset);
    537    buffer.set(offsets, offsetsOffset);
    538    buffer.set(instances, instancesOffset);
    539 
    540    let drawIDLocation;
    541 
    542    const multiDrawArrays = function() {
    543      ext.multiDrawArraysWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, tri_count);
    544    }
    545 
    546    const multiDrawArraysWithNonzeroOffsets = function() {
    547      ext.multiDrawArraysWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, tri_count);
    548    }
    549 
    550    const multiDrawElements = function() {
    551      ext.multiDrawElementsWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, tri_count);
    552    }
    553 
    554    const multiDrawElementsWithNonzeroOffsets = function() {
    555      ext.multiDrawElementsWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, tri_count);
    556    }
    557 
    558    const multiDrawArraysInstanced = function() {
    559      ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, instances, 0, tri_count);
    560    }
    561 
    562    const multiDrawArraysInstancedWithNonzeroOffsets = function() {
    563      ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, buffer, instancesOffset, tri_count);
    564    }
    565 
    566    const multiDrawElementsInstanced = function() {
    567      ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, instances, 0, tri_count);
    568    }
    569 
    570    const multiDrawElementsInstancedWithNonzeroOffsets = function() {
    571      ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, buffer, instancesOffset, tri_count);
    572    }
    573 
    574    const manyDrawArrays = function() {
    575      for (let i = 0; i < tri_count; ++i) {
    576        gl.drawArrays(gl.TRIANGLES, firsts[i], counts[i]);
    577      }
    578    }
    579 
    580    const manyDrawElements = function() {
    581      for (let i = 0; i < tri_count; ++i) {
    582        gl.drawElements(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i]);
    583      }
    584    }
    585 
    586    const manyDrawArraysEmulateDrawID = function() {
    587      for (let i = 0; i < tri_count; ++i) {
    588        gl.uniform1i(drawIDLocation, i);
    589        gl.drawArrays(gl.TRIANGLES, firsts[i], counts[i]);
    590      }
    591    }
    592 
    593    const manyDrawElementsEmulateDrawID = function() {
    594      for (let i = 0; i < tri_count; ++i) {
    595        gl.uniform1i(drawIDLocation, i);
    596        gl.drawElements(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i]);
    597      }
    598    }
    599 
    600    function drawArraysInstanced() {
    601      if (wtu.getDefault3DContextVersion() < 2) {
    602        instancedExt.drawArraysInstancedANGLE.apply(instancedExt, arguments);
    603      } else {
    604        gl.drawArraysInstanced.apply(gl, arguments);
    605      }
    606    }
    607 
    608    function drawElementsInstanced() {
    609      if (wtu.getDefault3DContextVersion() < 2) {
    610        instancedExt.drawElementsInstancedANGLE.apply(instancedExt, arguments);
    611      } else {
    612        gl.drawElementsInstanced.apply(gl, arguments);
    613      }
    614    }
    615 
    616    function vertexAttribDivisor(attrib, divisor) {
    617      if (wtu.getDefault3DContextVersion() < 2) {
    618        instancedExt.vertexAttribDivisorANGLE(attrib, divisor);
    619      } else {
    620        gl.vertexAttribDivisor(attrib, divisor);
    621      }
    622    }
    623 
    624    const manyDrawArraysInstanced = function() {
    625      for (let i = 0; i < tri_count; ++i) {
    626        drawArraysInstanced(gl.TRIANGLES, firsts[i], counts[i], 4);
    627      }
    628    }
    629 
    630    const manyDrawElementsInstanced = function() {
    631      for (let i = 0; i < tri_count; ++i) {
    632        drawElementsInstanced(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i], 4);
    633      }
    634    }
    635 
    636    const manyDrawArraysInstancedEmulateDrawID = function() {
    637      for (let i = 0; i < tri_count; ++i) {
    638        gl.uniform1i(drawIDLocation, i);
    639        drawArraysInstanced(gl.TRIANGLES, firsts[i], counts[i], 4);
    640      }
    641    }
    642 
    643    const manyDrawElementsInstancedEmulateDrawID = function() {
    644      for (let i = 0; i < tri_count; ++i) {
    645        gl.uniform1i(drawIDLocation, i);
    646        drawElementsInstanced(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i], 4);
    647      }
    648    }
    649 
    650    function checkDraw(config) {
    651      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    652 
    653      if (config.indexed) {
    654        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    655        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    656        gl.enableVertexAttribArray(0);
    657        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
    658      } else {
    659        gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer);
    660        gl.enableVertexAttribArray(0);
    661        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
    662      }
    663 
    664      if (config.instanced) {
    665        gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
    666        gl.enableVertexAttribArray(1);
    667        gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
    668        vertexAttribDivisor(1, 1);
    669      }
    670 
    671      config.drawFunc();
    672      wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
    673      checkResult(config, config.drawFunc.name + (
    674        config.instanced ? ' instanced' : ''
    675      ) + (
    676        config.drawID ? ' with gl_DrawID' : ''
    677      ) + (
    678        useSharedArrayBuffer ? ' and SharedArrayBuffer' : ''
    679      ));
    680 
    681      gl.disableVertexAttribArray(0);
    682      gl.disableVertexAttribArray(1);
    683    }
    684 
    685    const noDrawIDProgram = wtu.setupProgram(gl, ["vshaderNoDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]);
    686    expectTrue(noDrawIDProgram != null, "can compile simple program");
    687    if (noDrawIDProgram) {
    688      gl.useProgram(noDrawIDProgram);
    689 
    690      if (!instanced) {
    691        checkDraw({
    692          drawFunc: multiDrawArrays,
    693          drawID: false,
    694        });
    695        checkDraw({
    696          drawFunc: multiDrawArraysWithNonzeroOffsets,
    697          drawID: false,
    698        });
    699        checkDraw({
    700          drawFunc: multiDrawElements,
    701          indexed: true,
    702          drawID: false,
    703        });
    704        checkDraw({
    705          drawFunc: multiDrawElementsWithNonzeroOffsets,
    706          indexed: true,
    707          drawID: false,
    708        });
    709        checkDraw({
    710          drawFunc: manyDrawArrays,
    711          drawID: false,
    712        });
    713        checkDraw({
    714          drawFunc: manyDrawElements,
    715          indexed: true,
    716          drawID: false,
    717        });
    718      } else {
    719        checkDraw({
    720          drawFunc: multiDrawArraysInstanced,
    721          drawID: false,
    722          instanced: true,
    723        });
    724        checkDraw({
    725          drawFunc: multiDrawArraysInstancedWithNonzeroOffsets,
    726          drawID: false,
    727          instanced: true,
    728        });
    729        checkDraw({
    730          drawFunc: multiDrawElementsInstanced,
    731          indexed: true,
    732          drawID: false,
    733          instanced: true,
    734        });
    735        checkDraw({
    736          drawFunc: multiDrawElementsInstancedWithNonzeroOffsets,
    737          indexed: true,
    738          drawID: false,
    739          instanced: true,
    740        });
    741        checkDraw({
    742          drawFunc: manyDrawArraysInstanced,
    743          drawID: false,
    744          instanced: true,
    745        });
    746        checkDraw({
    747          drawFunc: manyDrawElementsInstanced,
    748          indexed: true,
    749          drawID: false,
    750          instanced: true,
    751        });
    752      }
    753    }
    754 
    755    const withDrawIDProgram = wtu.setupProgram(gl, ["vshaderWithDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]);
    756    expectTrue(withDrawIDProgram != null, "can compile program with ANGLE_multi_draw");
    757    if (withDrawIDProgram) {
    758      gl.useProgram(withDrawIDProgram);
    759 
    760      if (!instanced) {
    761        checkDraw({
    762          drawFunc: multiDrawArrays,
    763          drawID: true,
    764        });
    765        checkDraw({
    766          drawFunc: multiDrawArraysWithNonzeroOffsets,
    767          drawID: true,
    768        });
    769        checkDraw({
    770          drawFunc: multiDrawElements,
    771          indexed: true,
    772          drawID: true,
    773        });
    774        checkDraw({
    775          drawFunc: multiDrawElementsWithNonzeroOffsets,
    776          indexed: true,
    777          drawID: true,
    778        });
    779      } else {
    780        checkDraw({
    781          drawFunc: multiDrawArraysInstanced,
    782          drawID: true,
    783          instanced: true,
    784        });
    785        checkDraw({
    786          drawFunc: multiDrawArraysInstancedWithNonzeroOffsets,
    787          drawID: true,
    788          instanced: true,
    789        });
    790        checkDraw({
    791          drawFunc: multiDrawElementsInstanced,
    792          indexed: true,
    793          drawID: true,
    794          instanced: true,
    795        });
    796        checkDraw({
    797          drawFunc: multiDrawElementsInstancedWithNonzeroOffsets,
    798          indexed: true,
    799          drawID: true,
    800          instanced: true,
    801        });
    802      }
    803    }
    804 
    805    const emulatedDrawIDProgram = wtu.setupProgram(gl, ["vshaderEmulatedDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]);
    806    expectTrue(emulatedDrawIDProgram != null, "can compile program to emulate gl_DrawID");
    807    drawIDLocation = gl.getUniformLocation(emulatedDrawIDProgram, "drawID");
    808    if (emulatedDrawIDProgram) {
    809      gl.useProgram(emulatedDrawIDProgram);
    810 
    811      if (!instanced) {
    812        checkDraw({
    813          drawFunc: manyDrawArraysEmulateDrawID,
    814          drawID: true,
    815        });
    816        checkDraw({
    817          drawFunc: manyDrawElementsEmulateDrawID,
    818          indexed: true,
    819          drawID: true,
    820        });
    821      } else {
    822        checkDraw({
    823          drawFunc: manyDrawArraysInstancedEmulateDrawID,
    824          drawID: true,
    825          instanced: true,
    826        });
    827        checkDraw({
    828          drawFunc: manyDrawElementsInstancedEmulateDrawID,
    829          indexed: true,
    830          drawID: true,
    831          instanced: true,
    832        });
    833      }
    834    }
    835  }
    836 
    837  for (let i = 0; i < bufferUsageSet.length; i++) {
    838    let bufferUsage = bufferUsageSet[i];
    839    debug("Testing with BufferUsage = " + bufferUsage);
    840    runValidationTests(bufferUsage);
    841    runShaderTests(bufferUsage);
    842    runPixelTests(bufferUsage, false);
    843  }
    844 
    845  // Run a subset of the pixel tests with SharedArrayBuffer if supported.
    846  if (window.SharedArrayBuffer) {
    847    runPixelTests(bufferUsageSet[0], true);
    848  }
    849 }
    850 
    851 function waitForComposite() {
    852  debug('wait for composite');
    853  return new Promise(resolve => wtu.waitForComposite(resolve));
    854 }
    855 
    856 async function testPreserveDrawingBufferFalse(gl, drawFn) {
    857  debug('');
    858  debug('test preserveDrawingBuffer: false');
    859 
    860  if (drawFn(gl)) {
    861    debug('skipped: extension does not exist');
    862    return;
    863  }
    864 
    865  wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255],
    866      "canvas should be red");
    867 
    868  // enable scissor here, before compositing, to make sure it's correctly
    869  // ignored and restored
    870  gl.scissor(0, 10, 10, 10);
    871  gl.enable(gl.SCISSOR_TEST);
    872 
    873  await waitForComposite();
    874 
    875  // scissor was set earlier
    876  gl.clearColor(0, 0, 1, 1);
    877  gl.clear(gl.COLOR_BUFFER_BIT);
    878 
    879  wtu.checkCanvasRect(gl, 0, 10, 10, 10, [0, 0, 255, 255],
    880      "cleared corner should be blue, stencil should be preserved");
    881  wtu.checkCanvasRect(gl, 0, 0, 10, 10, [0, 0, 0, 0],
    882      "remainder of buffer should be cleared");
    883 
    884  gl.disable(gl.SCISSOR_TEST);
    885  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
    886 }
    887 
    888 async function testPreserveDrawingBufferTrue(gl, drawFn) {
    889  debug('');
    890  debug('test preserveDrawingBuffer: true');
    891  if (drawFn(gl)) {
    892    debug('skipped: extension does not exist');
    893    return;
    894  }
    895 
    896  wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255],
    897      "canvas should be red");
    898 
    899  await waitForComposite();
    900 
    901  wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255],
    902      "canvas should be red");
    903  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
    904 }
    905 
    906 async function doCompositingTests([glPreserveDrawingBufferFalse, glPreserveDrawingBufferTrue], drawFn) {
    907  debug('');
    908  debug(drawFn.name);
    909 
    910  await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn);
    911  await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn);
    912 }
    913 
    914 async function runDrawTests(testFn) {
    915  function drawArrays(gl) {
    916    gl.drawArrays(gl.TRIANGLES, 0, 6);
    917  }
    918 
    919  function drawElements(gl) {
    920    gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
    921  }
    922 
    923  function drawArraysInstanced(gl) {
    924    gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 1);
    925  }
    926 
    927  function drawElementsInstanced(gl) {
    928    gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1);
    929  }
    930 
    931  function multiDrawArraysWEBGL(gl) {
    932    const ext = gl.getExtension('WEBGL_multi_draw');
    933    if (!ext) {
    934      throw 'Should not have run this test without WEBGL_multi_draw';
    935    }
    936 
    937    ext.multiDrawArraysWEBGL(
    938        gl.TRIANGLES,
    939        [0], 0, // firsts
    940        [6], 0, // counts
    941        1,      // drawCount
    942    );
    943  }
    944 
    945  function multiDrawElementsWEBGL(gl) {
    946    const ext = gl.getExtension('WEBGL_multi_draw');
    947    if (!ext) {
    948      throw 'Should not have run this test without WEBGL_multi_draw';
    949    }
    950 
    951    ext.multiDrawElementsWEBGL(
    952        gl.TRIANGLES,
    953        [6], 0,   // counts
    954        gl.UNSIGNED_BYTE,
    955        [0], 0,   // offsets
    956        1,        // drawCount
    957    );
    958  }
    959 
    960  function multiDrawArraysInstancedWEBGL(gl) {
    961    const ext = gl.getExtension('WEBGL_multi_draw');
    962    if (!ext) {
    963      throw 'Should not have run this test without WEBGL_multi_draw';
    964    }
    965    ext.multiDrawArraysInstancedWEBGL(
    966        gl.TRIANGLES,
    967        [0], 0, // firsts
    968        [6], 0, // counts
    969        [1], 0, // instances
    970        1,      // drawCount
    971    );
    972  }
    973 
    974  function multiDrawElementsInstancedWEBGL(gl) {
    975    const ext = gl.getExtension('WEBGL_multi_draw');
    976    if (!ext) {
    977      throw 'Should not have run this test without WEBGL_multi_draw';
    978    }
    979    ext.multiDrawElementsInstancedWEBGL(
    980        gl.TRIANGLES,
    981        [6], 0, // counts
    982        gl.UNSIGNED_BYTE,
    983        [0], 0, // offsets
    984        [1], 0, // instances
    985        1,      // drawCount
    986    );
    987  }
    988 
    989  await testFn(drawArrays);             // sanity check
    990  await testFn(drawElements);           // sanity check
    991 
    992  // It's only legal to call testFn if the extension is supported,
    993  // since the invalid vertex attrib tests, in particular, expect the
    994  // draw function to have an effect.
    995  if (gl.getExtension('WEBGL_multi_draw')) {
    996    await testFn(multiDrawArraysWEBGL);
    997    await testFn(multiDrawElementsWEBGL);
    998    await testFn(multiDrawArraysInstancedWEBGL);
    999    await testFn(multiDrawElementsInstancedWEBGL);
   1000  }
   1001 }
   1002 
   1003 async function runCompositingTests() {
   1004  const compositingTestFn = createCompositingTestFn({
   1005    webglVersion: 1,
   1006    shadersFn(gl) {
   1007      const vs = `\
   1008      //#extension GL_ANGLE_multi_draw : enable
   1009      attribute vec4 position;
   1010      void main() {
   1011        gl_Position = position;
   1012      }
   1013      `;
   1014      const fs = `\
   1015      precision mediump float;
   1016      void main() {
   1017        gl_FragColor = vec4(1, 0, 0, 1);
   1018      }
   1019      `;
   1020      return [vs, fs];
   1021    },
   1022  });
   1023  await runDrawTests(compositingTestFn);
   1024 }
   1025 
   1026 async function runInvalidAttribTests(gl) {
   1027  const invalidAttribTestFn = createInvalidAttribTestFn(gl);
   1028  await runDrawTests(invalidAttribTestFn);
   1029 }
   1030 
   1031 function testSideEffects() {
   1032  debug("")
   1033  debug("Testing that ANGLE_instanced_arrays is implicitly enabled")
   1034  const canvas = document.createElement("canvas");
   1035  const gl = wtu.create3DContext(canvas, undefined, 1);
   1036  if (!gl) {
   1037    testFailed('WebGL context creation failed');
   1038    return;
   1039  }
   1040  gl.enableVertexAttribArray(0);
   1041  const VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE;
   1042  gl.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE);
   1043  wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "divisor enum unknown");
   1044  const ext = gl.getExtension("WEBGL_multi_draw");
   1045  if (ext) {
   1046    debug("WEBGL_multi_draw enabled");
   1047    gl.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE);
   1048    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "divisor enum known");
   1049  }
   1050 }
   1051 
   1052 async function main() {
   1053  runTest();
   1054  testSideEffects();
   1055  await runInvalidAttribTests(gl);
   1056  await runCompositingTests();
   1057  finishTest();
   1058 }
   1059 main();
   1060 
   1061 var successfullyParsed = true;
   1062 </script>
   1063 </body>
   1064 </html>