tor-browser

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

webgl-multi-draw-instanced-base-vertex-base-instance.html (35571B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <title>WebGL ANGLE_base_vertex_base_instance 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="vshaderBaseInstanceWithoutExt" type="x-shader/x-vertex">#version 300 es
     15 layout(location = 0) in vec2 vPosition;
     16 out 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_BaseInstance, 1);
     21 }
     22 </script>
     23 <!-- Check gl_InstanceID starts at 0 regardless of gl_BaseInstance -->
     24 <script id="vshaderInstanceIDCheck" type="x-shader/x-vertex">#version 300 es
     25 layout(location = 0) in vec2 vPosition;
     26 out vec4 color;
     27 void main()
     28 {
     29  if (gl_InstanceID == 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 <script id="vshaderBaseVertexWithoutExt" type="x-shader/x-vertex">#version 300 es
     38 layout(location = 0) in vec2 vPosition;
     39 out vec4 color;
     40 void main()
     41 {
     42  color = vec4(1.0, 0.0, 0.0, 1.0);
     43  gl_Position = vec4(vPosition * 2.0 - 1.0, gl_BaseVertex, 1);
     44 }
     45 </script>
     46 <script id="vshaderWithExt" type="x-shader/x-vertex">#version 300 es
     47 #extension GL_ANGLE_base_vertex_base_instance : require
     48 layout(location = 0) in vec2 vPosition;
     49 out vec4 color;
     50 void main()
     51 {
     52  color = vec4(0, 1, 0, 1);
     53  gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1);
     54 }
     55 </script>
     56 <!-- Check gl_VertexID starts at gl_BaseVertex -->
     57 <script id="vshaderVertexIDCheck" type="x-shader/x-vertex">#version 300 es
     58 layout(location = 0) in vec2 vPosition;
     59 out vec4 color;
     60 void main()
     61 {
     62  if (gl_VertexID >= 3) {
     63    color = vec4(0, 1, 0, 1);
     64  } else {
     65    color = vec4(1, 0, 0, 1);
     66  }
     67  gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1);
     68 }
     69 </script>
     70 <script id="vshaderSimple" type="x-shader/x-vertex">#version 300 es
     71  layout(location = 0) in vec2 vPosition;
     72  layout(location = 1) in float vInstance;
     73  out vec4 color;
     74  void main()
     75  {
     76    if (vInstance <= 0.0) {
     77      color = vec4(1.0, 0.0, 0.0, 1.0);
     78    } else if (vInstance <= 1.0) {
     79      color = vec4(0.0, 1.0, 0.0, 1.0);
     80    } else if (vInstance <= 2.0) {
     81      color = vec4(0.0, 0.0, 1.0, 1.0);
     82    } else {
     83      color = vec4(0.0, 0.0, 0.0, 1.0);
     84    }
     85 
     86    gl_Position = vec4(vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
     87  }
     88 </script>
     89 <script id="fshader" type="x-shader/x-fragment">#version 300 es
     90  precision mediump float;
     91  in vec4 color;
     92  out vec4 oColor;
     93  void main() {
     94    oColor = color;
     95  }
     96 </script>
     97 <div id="description"></div>
     98 <canvas id="canvas" width="128" height="128"> </canvas>
     99 <div id="console"></div>
    100 
    101 <script>
    102 "use strict";
    103 description("This test verifies the functionality of the WEBGL_[multi]_draw_basevertex_base_instance extension, if it is available.");
    104 
    105 const wtu = WebGLTestUtils;
    106 const canvas = document.getElementById("canvas");
    107 canvas.style.backgroundColor = '#000';
    108 canvas.style.imageRendering = 'pixelated'; // Because Chrome doesn't support crisp-edges.
    109 canvas.style.imageRendering = 'crisp-edges';
    110 const attribs = {
    111  antialias: false,
    112 };
    113 const gl = wtu.create3DContext(canvas, attribs, 2);
    114 
    115 const width = gl.canvas.width;
    116 const height = gl.canvas.height;
    117 const x_count = 8;
    118 const y_count = 8;
    119 const quad_count = x_count * y_count;
    120 const tri_count = quad_count * 2;
    121 const tileSize = [ 1/x_count, 1/y_count ];
    122 const tilePixelSize = [ Math.floor(width / x_count), Math.floor(height / y_count) ];
    123 const quadRadius = [ 0.25 * tileSize[0], 0.25 * tileSize[1] ];
    124 const pixelCheckSize = [ Math.floor(quadRadius[0] * width), Math.floor(quadRadius[1] * height) ];
    125 const bufferUsageSet = [ gl.STATIC_DRAW, gl.DYNAMIC_DRAW ];
    126 
    127 function getTileCenter(x, y) {
    128  return [ tileSize[0] * (0.5 + x), tileSize[1] * (0.5 + y) ];
    129 }
    130 
    131 function getQuadVertices(x, y) {
    132  const center = getTileCenter(x, y);
    133  return [
    134    [center[0] - quadRadius[0], center[1] - quadRadius[1], 0],
    135    [center[0] + quadRadius[0], center[1] - quadRadius[1], 0],
    136    [center[0] + quadRadius[0], center[1] + quadRadius[1], 0],
    137    [center[0] - quadRadius[0], center[1] + quadRadius[1], 0],
    138  ];
    139 }
    140 
    141 const indicesData = [];
    142 let verticesData = [];
    143 let nonIndexedVerticesData = [];
    144 const instanceIDsData = Array.from(Array(x_count).keys());
    145 const is = new Uint16Array([0, 1, 2, 0, 2, 3]);
    146 // Rects in the same column are within a vertex array, testing gl_VertexID, gl_BaseVertex
    147 // Rects in the same row are drawn by instancing, testing gl_InstanceID, gl_BaseInstance
    148 for (let y = 0; y < y_count; ++y) {
    149  // v3 ---- v2
    150  // |       |
    151  // |       |
    152  // v0 ---- v1
    153 
    154  // Get only one column of quad vertices as our geometry
    155  // Rely on BaseInstance to duplicate on x axis
    156  const vs = getQuadVertices(0, y);
    157 
    158  for (let i = 0; i < vs.length; ++i) {
    159    verticesData = verticesData.concat(vs[i]);
    160  }
    161 
    162  for (let i = 0; i < is.length; ++i) {
    163    nonIndexedVerticesData = nonIndexedVerticesData.concat(vs[is[i]]);
    164  }
    165 }
    166 
    167 // Build the indicesData used by drawElements*
    168 for (let i = 0; i < y_count; ++i) {
    169  let oi = 6 * i;
    170  let ov = 4 * i;
    171  for (let j = 0; j < is.length; ++j) {
    172    indicesData[oi + j] = is[j] + ov;
    173  }
    174 }
    175 
    176 const indices = new Uint16Array(indicesData);
    177 const vertices = new Float32Array(verticesData);
    178 const nonIndexedVertices = new Float32Array(nonIndexedVerticesData);
    179 const instanceIDs = new Float32Array(instanceIDsData);
    180 
    181 const indexBuffer = gl.createBuffer();
    182 const vertexBuffer = gl.createBuffer();
    183 const nonIndexedVertexBuffer = gl.createBuffer();
    184 const instanceIDBuffer = gl.createBuffer();
    185 
    186 const drawArraysDrawCount = x_count / 2;
    187 let drawArraysParams = {
    188  drawCount: drawArraysDrawCount,
    189  firsts: new Uint32Array(drawArraysDrawCount).fill(0),
    190  counts: new Uint32Array(drawArraysDrawCount).fill(y_count * 6),
    191  instances: new Uint32Array(drawArraysDrawCount).fill(2),
    192  baseInstances: new Uint32Array(drawArraysDrawCount)
    193 };
    194 
    195 for (let i = 0; i < x_count / 2; ++i) {
    196  drawArraysParams.baseInstances[i] = i * 2;
    197 }
    198 
    199 const drawElementsDrawCount = x_count * y_count / 2;
    200 let drawElementsParams = {
    201  drawCount: drawElementsDrawCount,
    202  offsets: new Uint32Array(drawElementsDrawCount).fill(0),
    203  counts: new Uint32Array(drawElementsDrawCount).fill(6),
    204  instances: new Uint32Array(drawElementsDrawCount).fill(2),
    205  baseVertices: new Uint32Array(drawElementsDrawCount),
    206  baseInstances: new Uint32Array(drawElementsDrawCount)
    207 };
    208 
    209 let b = 0;
    210 for (let v = 0; v < y_count; ++v) {
    211  for (let i = 0; i < x_count; i+=2) {
    212    drawElementsParams.baseVertices[b] = v * 4;
    213    drawElementsParams.baseInstances[b] = i;
    214    ++b;
    215  }
    216 }
    217 
    218 function setupGeneralBuffers(bufferUsage) {
    219  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    220  gl.bufferData(gl.ARRAY_BUFFER, vertices, bufferUsage);
    221 
    222  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    223  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, bufferUsage);
    224 
    225  gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer);
    226  gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage);
    227 
    228  gl.bindBuffer(gl.ARRAY_BUFFER, instanceIDBuffer);
    229  gl.bufferData(gl.ARRAY_BUFFER, instanceIDs, bufferUsage);
    230 }
    231 
    232 // Check if the extension is either both enabled and supported or
    233 // not enabled and not supported.
    234 function runSupportedTest(extensionName, extensionEnabled) {
    235  const supported = gl.getSupportedExtensions();
    236  if (supported.indexOf(extensionName) >= 0) {
    237    if (extensionEnabled) {
    238      testPassed(extensionName + ' listed as supported and getExtension succeeded');
    239      return true;
    240    } else {
    241      testFailed(extensionName + ' listed as supported but getExtension failed');
    242    }
    243  } else {
    244    if (extensionEnabled) {
    245      testFailed(extensionName + ' not listed as supported but getExtension succeeded');
    246    } else {
    247      testPassed(extensionName + ' not listed as supported and getExtension failed -- this is legal');
    248    }
    249  }
    250  return false;
    251 }
    252 
    253 function runTest() {
    254  if (!gl) {
    255    return function() {
    256      testFailed('WebGL context does not exist');
    257    }
    258  }
    259 
    260  doTest('WEBGL_draw_instanced_base_vertex_base_instance', false);
    261  doTest('WEBGL_multi_draw_instanced_base_vertex_base_instance', true);
    262 
    263  testGlslBuiltins();
    264 }
    265 
    266 // -
    267 
    268 function* range(n) {
    269  for (let i = 0; i < n; i++) {
    270    yield i;
    271  }
    272 }
    273 
    274 function crossCombine(...args) {
    275  function crossCombine2(listA, listB) {
    276    const listC = [];
    277    for (const a of listA) {
    278      for (const b of listB) {
    279        const c = Object.assign({}, a, b);
    280        listC.push(c);
    281      }
    282    }
    283    return listC;
    284  }
    285 
    286  let res = [{}];
    287  while (args.length) {
    288    const next = args.shift();
    289    next[0].defined;
    290    res = crossCombine2(res, next);
    291  }
    292  return res;
    293 }
    294 
    295 // -
    296 
    297 const PASSTHROUGH_FRAG_SRC = `\
    298 #version 300 es
    299 precision mediump float;
    300 in vec4 v_color;
    301 out vec4 o_color;
    302 
    303 void main() {
    304  o_color = v_color;
    305 }
    306 `;
    307 
    308 function testGlslBuiltins() {
    309  const EXT = gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance');
    310 
    311  const vertid_prog = (() => {
    312      const vert_src = `\
    313 #version 300 es
    314 #line 405
    315 layout(location = 0) in int a_vertex_id; // Same as gl_VertexID
    316 out vec4 v_color;
    317 
    318 void main() {
    319  gl_Position = vec4(0,0,0,1);
    320  gl_PointSize = 1.0;
    321  v_color = vec4(float(gl_VertexID), float(a_vertex_id),0,0);
    322  v_color /= 255.0;
    323 }
    324 `;
    325      const prog = wtu.setupProgram(gl, [vert_src, PASSTHROUGH_FRAG_SRC],
    326              undefined, undefined, /*logShaders*/ true);
    327      expectTrue(!!prog, `make_vertid_prog failed`);
    328      return prog;
    329    })();
    330 
    331  const instid_prog = (() => {
    332      const vert_src = `\
    333 #version 300 es
    334 #line 425
    335 layout(location = 0) in int a_vertex_id; // Same as gl_VertexID
    336 layout(location = 1) in int a_instance_div1; // Same as base_instance+gl_InstanceID
    337 layout(location = 2) in int a_instance_div2; // Same as base_instance+floor(gl_InstanceID/2)
    338 layout(location = 3) in int a_instance_div3; // Same as base_instance+floor(gl_InstanceID/3)
    339 out vec4 v_color;
    340 
    341 void main() {
    342  gl_Position = vec4(0,0,0,1);
    343  gl_PointSize = 1.0;
    344  v_color = vec4(float(gl_InstanceID), float(a_instance_div1),
    345                 float(a_instance_div2), float(a_instance_div3));
    346  v_color /= 255.0;
    347 }
    348 `;
    349      const prog = wtu.setupProgram(gl, [vert_src, PASSTHROUGH_FRAG_SRC],
    350              undefined, undefined, /*logShaders*/ true);
    351      expectTrue(!!prog, `make_instid_prog failed`);
    352      return prog;
    353    })();
    354 
    355  const COUNT_UP_DATA = new Int32Array(1000);
    356  for (const i in COUNT_UP_DATA) {
    357    COUNT_UP_DATA[i] = i;
    358  }
    359 
    360  const vertex_id_buf = gl.createBuffer();
    361  gl.bindBuffer(gl.ARRAY_BUFFER, vertex_id_buf);
    362  gl.bufferData(gl.ARRAY_BUFFER, COUNT_UP_DATA, gl.STATIC_DRAW);
    363  gl.enableVertexAttribArray(0);
    364  gl.vertexAttribIPointer(0, 1, gl.INT, 0, 0);
    365 
    366  gl.enableVertexAttribArray(1);
    367  gl.vertexAttribIPointer(1, 1, gl.INT, 0, 0);
    368  gl.vertexAttribDivisor(1, 1);
    369 
    370  gl.enableVertexAttribArray(2);
    371  gl.vertexAttribIPointer(2, 1, gl.INT, 0, 0);
    372  gl.vertexAttribDivisor(2, 2);
    373 
    374  gl.enableVertexAttribArray(3);
    375  gl.vertexAttribIPointer(3, 1, gl.INT, 0, 0);
    376  gl.vertexAttribDivisor(3, 3);
    377 
    378  const index_buf = gl.createBuffer();
    379  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buf);
    380  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, COUNT_UP_DATA, gl.STATIC_DRAW);
    381 
    382  gl.canvas.width = gl.canvas.height = 1;
    383  gl.canvas.style.width = gl.canvas.style.height = '1em';
    384  gl.viewport(0, 0, 1, 1);
    385 
    386  const expect_pixel = (() => {
    387    const was = new Uint8Array(4);
    388    return (desc, subtest, expected) => {
    389      gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, was);
    390      if (!areArraysEqual(was, expected)) {
    391        testFailed(`${subtest}: Expected [${expected}], was [${was}]. desc: ${JSON.stringify(desc)}`);
    392      } else {
    393        debug(`${subtest}: Was [${was}] as expected.`);
    394      }
    395    };
    396  })();
    397 
    398  // Common setup complete
    399  // -
    400  // Create testcases
    401 
    402  const DRAW_FUNC_COMBINER = [{
    403    name: 'drawArraysInstanced',
    404    draw: desc => {
    405      if (desc.base_vert) return false;
    406      if (desc.base_inst) return false;
    407      gl.drawArraysInstanced(gl[desc.mode], desc.first_vert,
    408          desc.vert_count, desc.inst_count);
    409      return true;
    410    },
    411  }, {
    412    name: 'drawElementsInstanced',
    413    draw: desc => {
    414      if (desc.base_vert) return false;
    415      if (desc.base_inst) return false;
    416      gl.drawElementsInstanced(gl[desc.mode], desc.vert_count,
    417          gl.UNSIGNED_INT, 4*desc.first_vert, desc.inst_count);
    418      return true;
    419    },
    420  }, {
    421    name: 'drawArraysInstancedBaseInstanceWEBGL',
    422    draw: desc => {
    423      if (desc.base_vert) return false;
    424      if (!EXT) return false;
    425      EXT.drawArraysInstancedBaseInstanceWEBGL(gl[desc.mode],
    426        desc.first_vert, desc.vert_count, desc.inst_count,
    427        desc.base_inst);
    428      return true;
    429    },
    430  }, {
    431    name: 'drawElementsInstancedBaseVertexBaseInstanceWEBGL',
    432    draw: desc => {
    433      if (!EXT) return false;
    434      EXT.drawElementsInstancedBaseVertexBaseInstanceWEBGL(
    435          gl[desc.mode], desc.vert_count, gl.UNSIGNED_INT, 4*desc.first_vert,
    436          desc.inst_count, desc.base_vert, desc.base_inst);
    437      return true;
    438    },
    439  }];
    440 
    441  // -
    442 
    443  function make_key_combiner(key, vals) {
    444    const ret = [];
    445    for (const v of vals) {
    446      const cur = {};
    447      cur[key] = v;
    448      ret.push(cur);
    449    }
    450    return ret;
    451  }
    452 
    453  const TEST_DESCS = crossCombine(
    454    DRAW_FUNC_COMBINER,
    455    make_key_combiner('base_vert', [0,1,2]),
    456    make_key_combiner('vert_count', [0,1,2]),
    457    make_key_combiner('base_inst', [0,1,2]),
    458    make_key_combiner('inst_count', range(10)),
    459    make_key_combiner('first_vert', [0,1,2]),
    460  );
    461  console.log('TEST_DESCS', TEST_DESCS);
    462 
    463  // -
    464  // Run testcases
    465 
    466  gl.disable(gl.DEPTH_TEST);
    467  gl.disable(gl.STENCIL_TEST);
    468  gl.disable(gl.BLEND);
    469 
    470  for (const desc of TEST_DESCS) {
    471    gl.disable(gl.SCISSOR_TEST);
    472    gl.clearBufferfv(gl.COLOR, 0, [1,0,0,1]);
    473 
    474    // From OpenGL ES 3.2 spec section 10.5
    475    // https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf
    476    // The index of any element transferred to the GL by DrawArraysOneInstance
    477    // is referred to as its vertex ID, and may be read by a vertex shader as gl_VertexID.
    478    // The vertex ID of the ith element transferred is first + i.
    479    const last_gl_vert_id = desc.base_vert + desc.first_vert + desc.vert_count - 1;
    480    const last_vert_id = last_gl_vert_id;
    481    const last_inst_id = desc.inst_count - 1;
    482    const last_inst_div1 = desc.base_inst + last_inst_id;
    483    const last_inst_div2 = desc.base_inst + Math.floor(last_inst_id / 2);
    484    const last_inst_div3 = desc.base_inst + Math.floor(last_inst_id / 3);
    485 
    486    gl.useProgram(vertid_prog);
    487    if (!desc.draw(desc)) continue;
    488    debug('\ndesc: ' + JSON.stringify(desc));
    489 
    490    wtu.glErrorAssert(gl, 0);
    491    if (!desc.vert_count || !desc.inst_count) {
    492      expect_pixel(desc, 'vertid_prog', [255, 0, 0, 255]);
    493      continue;
    494    }
    495 
    496    expect_pixel(desc, 'vertid_prog', [last_gl_vert_id, last_vert_id, 0, 0]);
    497 
    498    gl.useProgram(instid_prog);
    499    desc.draw(desc);
    500    expect_pixel(desc, 'instid_prog', [last_inst_id, last_inst_div1, last_inst_div2, last_inst_div3]);
    501  }
    502 }
    503 
    504 // -
    505 
    506 function doTest(extensionName, multiDraw) {
    507  const ext = gl.getExtension(extensionName);
    508  if (!runSupportedTest(extensionName, ext)) {
    509    return;
    510  }
    511 
    512  function getShaderSource(countX, countY, config) {
    513    const vs = [
    514      '#version 300 es',
    515      config.isMultiDraw ? '#extension GL_ANGLE_multi_draw : require' : '',
    516      '#define kCountX ' + countX.toString(),
    517      '#define kCountY ' + countY.toString(),
    518      'layout(location = 0) in vec2 vPosition;',
    519      'layout(location = 1) in float vInstanceID;',
    520      'out vec4 color;',
    521      'void main()',
    522      '{',
    523      '  const float xStep = 1.0 / float(kCountX);',
    524      '  const float yStep = 1.0 / float(kCountY);',
    525      '  float xID = vInstanceID;',
    526      '  float xColor = 1.0 - xStep * xID;',
    527      '  float yID = floor(float(gl_VertexID) / ' + (config.isDrawArrays ? '6.0' : '4.0') + ' + 0.01);',
    528      '  color = vec4(xColor, 1.0 - yStep * yID, 1.0',
    529      '  , 1.0);',
    530      '  mat3 transform = mat3(1.0);',
    531      '  transform[2][0] = xID * xStep;',
    532      '  gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1.0);',
    533      '}'
    534    ].join('\n');
    535 
    536    const fs = document.getElementById('fshader').text.trim();
    537 
    538    return [vs, fs];
    539  }
    540 
    541  function runValidationTests(bufferUsage) {
    542    const vertexBuffer = gl.createBuffer();
    543    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    544    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.2,0.2, 0.8,0.2, 0.5,0.8 ]), bufferUsage);
    545 
    546    const indexBuffer = gl.createBuffer();
    547    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    548    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([ 0, 1, 2 ]), bufferUsage);
    549 
    550    const instanceBuffer = gl.createBuffer();
    551    gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
    552    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 2 ]), bufferUsage);
    553 
    554    const program = wtu.setupProgram(gl, ['vshaderSimple', 'fshader'], ['vPosition, vInstanceID'], [0, 1], true);
    555    expectTrue(program != null, "can compile simple program");
    556 
    557    function setupInstanced() {
    558      gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
    559      gl.enableVertexAttribArray(1);
    560      gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
    561      gl.vertexAttribDivisor(1, 1);
    562    }
    563 
    564    setupInstanced();
    565 
    566    function setupDrawArrays() {
    567      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    568      gl.enableVertexAttribArray(0);
    569      gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    570    }
    571 
    572    function setupDrawElements() {
    573      gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    574      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    575      gl.enableVertexAttribArray(0);
    576      gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    577    }
    578 
    579    function makeDrawValidationCheck(drawFunc, setup) {
    580      if (!drawFunc) {
    581        return function() {};
    582      }
    583      return function(f_args, expect, msg) {
    584        setup();
    585        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    586        drawFunc.apply(ext, f_args);
    587        wtu.glErrorShouldBe(gl, expect, drawFunc.name + " " + msg);
    588        gl.disableVertexAttribArray(0);
    589      }
    590    }
    591 
    592    if (!multiDraw) {
    593      const checkDrawArraysInstancedBaseInstance = makeDrawValidationCheck(
    594        ext.drawArraysInstancedBaseInstanceWEBGL, setupDrawArrays);
    595      const checkDrawElementsInstancedBaseVertexBaseInstance = makeDrawValidationCheck(
    596        ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL, setupDrawElements);
    597      checkDrawArraysInstancedBaseInstance(
    598        [gl.TRIANGLES, 0, 3, 1, 1],
    599        gl.NO_ERROR, "with gl.TRIANGLES"
    600      );
    601      checkDrawElementsInstancedBaseVertexBaseInstance(
    602        [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 0, 0],
    603        gl.NO_ERROR, "with gl.TRIANGLES"
    604      );
    605 
    606      checkDrawArraysInstancedBaseInstance(
    607        [gl.TRIANGLES, 0, 3, 1, 3],
    608        [gl.NO_ERROR, gl.INVALID_OPERATION],
    609        "with baseInstance leading to out of bounds"
    610      );
    611      checkDrawElementsInstancedBaseVertexBaseInstance(
    612        [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 2, 0],
    613        [gl.NO_ERROR, gl.INVALID_OPERATION],
    614        "with baseVertex leading to out of bounds"
    615      );
    616      checkDrawElementsInstancedBaseVertexBaseInstance(
    617        [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 0, 3],
    618        [gl.NO_ERROR, gl.INVALID_OPERATION],
    619        "with baseInstance leading to out of bounds"
    620      );
    621      checkDrawElementsInstancedBaseVertexBaseInstance(
    622        [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 2, 3],
    623        [gl.NO_ERROR, gl.INVALID_OPERATION],
    624        "with both baseVertex and baseInstance leading to out of bounds"
    625      );
    626    } else {
    627      const checkMultiDrawArraysInstancedBaseInstance = makeDrawValidationCheck(
    628        ext.multiDrawArraysInstancedBaseInstanceWEBGL, setupDrawArrays);
    629      const checkMultiDrawElementsInstancedBaseVertexBaseInstance = makeDrawValidationCheck(
    630        ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL, setupDrawElements);
    631 
    632      // Check that drawing a single triangle works
    633      checkMultiDrawArraysInstancedBaseInstance(
    634        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, 1],
    635        gl.NO_ERROR, "with gl.TRIANGLES"
    636      );
    637      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    638        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 1],
    639        gl.NO_ERROR, "with gl.TRIANGLES"
    640      );
    641 
    642      checkMultiDrawArraysInstancedBaseInstance(
    643        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [3], 0, 1],
    644        [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseInstance leads to out of bounds"
    645      );
    646      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    647        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [2], 0, [0], 0, 1],
    648        [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseVertex leads to out of bounds"
    649      );
    650      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    651        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [3], 0, 1],
    652        [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseInstance leads to out of bounds"
    653      );
    654      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    655        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [2], 0, [3], 0, 1],
    656        [gl.NO_ERROR, gl.INVALID_OPERATION],
    657        "with both baseVertex and baseInstance lead to out of bounds"
    658      );
    659 
    660      // Zero drawcount permitted
    661      checkMultiDrawArraysInstancedBaseInstance(
    662        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, 0],
    663        gl.NO_ERROR, "with drawcount == 0"
    664      );
    665      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    666        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 0],
    667        gl.NO_ERROR, "with drawcount == 0"
    668      );
    669 
    670      // Check negative drawcount
    671      checkMultiDrawArraysInstancedBaseInstance(
    672        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, -1],
    673        gl.INVALID_VALUE, "with drawcount < 0"
    674      );
    675      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    676        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, -1],
    677        gl.INVALID_VALUE, "with drawcount < 0"
    678      );
    679 
    680      // Check offsets greater than array length
    681      checkMultiDrawArraysInstancedBaseInstance(
    682        [gl.TRIANGLES, [0], 1, [3], 0, [1], 0, [0], 0, 1],
    683        gl.INVALID_OPERATION, "with firstsStart >= firstsList.length"
    684      );
    685      checkMultiDrawArraysInstancedBaseInstance(
    686        [gl.TRIANGLES, [0], 0, [3], 1, [1], 0, [0], 0, 1],
    687        gl.INVALID_OPERATION, "with countsStart >= countsList.length"
    688      );
    689      checkMultiDrawArraysInstancedBaseInstance(
    690        [gl.TRIANGLES, [0], 0, [3], 0, [1], 1, [0], 0, 1],
    691        gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length"
    692      );
    693      checkMultiDrawArraysInstancedBaseInstance(
    694        [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 1, 1],
    695        gl.INVALID_OPERATION, "with baseInstancesStart >= baseInstancesList.length"
    696      );
    697 
    698      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    699        [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 1],
    700        gl.INVALID_OPERATION, "with countsStart >= countsList.length"
    701      );
    702      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    703        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, [1], 0, [0], 0, [0], 0, 1],
    704        gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length"
    705      );
    706      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    707        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 1, [0], 0, [0], 0, 1],
    708        gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length"
    709      );
    710      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    711        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 1, [0], 0, 1],
    712        gl.INVALID_OPERATION, "with baseVerticesStart >= baseVerticesList.length"
    713      );
    714      checkMultiDrawElementsInstancedBaseVertexBaseInstance(
    715        [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 1, 1],
    716        gl.INVALID_OPERATION, "with baseInstancesStart >= baseInstancesList.length"
    717      );
    718    }
    719  }
    720 
    721  function runShaderTests(bufferUsage) {
    722    let badProgram;
    723 
    724    badProgram = wtu.setupProgram(gl, ["vshaderBaseInstanceWithoutExt", "fshader"]);
    725    expectTrue(!badProgram, "cannot compile program with gl_BaseInstance but no extension directive");
    726    badProgram = wtu.setupProgram(gl, ["vshaderBaseVertexWithoutExt", "fshader"]);
    727    expectTrue(!badProgram, "cannot compile program with gl_BaseVertex but no extension directive");
    728 
    729    badProgram = wtu.setupProgram(gl, ["vshaderWithExt", "fshader"]);
    730    expectTrue(!badProgram, "cannot compile program with #extension GL_ANGLE_base_vertex_base_instance");
    731 
    732    const x = Math.floor(width * 0.4);
    733    const y = Math.floor(height * 0.4);
    734    const xSize = Math.floor(width * 0.2);
    735    const ySize = Math.floor(height * 0.2);
    736 
    737    // gl_InstanceID
    738    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    739    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0.5,1, 0,1, 0.5,0, 1,1 ]), bufferUsage);
    740    gl.enableVertexAttribArray(0);
    741    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    742 
    743    const instanceIDProgram = wtu.setupProgram(gl, ["vshaderInstanceIDCheck", "fshader"], ["vPosition"], [0]);
    744    expectTrue(instanceIDProgram !== null, "can compile program with gl_InstanceID");
    745    gl.useProgram(instanceIDProgram);
    746 
    747    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    748    if (!multiDraw) {
    749      ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, 6, 1, 5);
    750    } else {
    751      ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, [0], 0, [6], 0, [1], 0, [5], 0, 1);
    752    }
    753 
    754    wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_InstanceID should always starts from 0");
    755 
    756    // gl_VertexID
    757    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
    758    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0.5,1, 0,1, 0.5,0, 1,1, 0,0, 1,0, 0.5,1, 0,1 ]), bufferUsage);
    759    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());
    760    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 3, 4, 5]), bufferUsage);
    761    gl.enableVertexAttribArray(0);
    762    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
    763 
    764    const vertexIDProgram = wtu.setupProgram(gl, ["vshaderVertexIDCheck", "fshader"], ["vPosition"], [0]);
    765    expectTrue(vertexIDProgram !== null, "can compile program with gl_VertexID");
    766    gl.useProgram(vertexIDProgram);
    767 
    768    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    769    if (!multiDraw) {
    770      ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1, 3, 0);
    771    } else {
    772      ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, [6], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [3], 0, [0], 0, 1);
    773    }
    774 
    775    wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_VertexID should always starts from 0");
    776  }
    777 
    778  function runPixelTests() {
    779 
    780    function checkResult(config) {
    781      const rects = [];
    782      const expected = [
    783        [255, 0, 0, 255],
    784        [0, 255, 0, 255],
    785        [0, 0, 255, 255],
    786      ];
    787      const msg = config.drawFunc.name + (
    788        config.useBaseVertexBuiltin ? ' gl_BaseVertex' : ''
    789      ) + (
    790        config.useBaseInstanceBuiltin ? ' gl_BaseInstance' : ' InstanceIDArray'
    791      );
    792      for (let y = 0; y < y_count; ++y) {
    793        for (let x = 0; x < x_count; ++x) {
    794          const center_x = x * tilePixelSize[0] + Math.floor(tilePixelSize[0] / 2);
    795          const center_y = y * tilePixelSize[1] + Math.floor(tilePixelSize[1] / 2);
    796 
    797          rects.push(wtu.makeCheckRect(
    798            center_x - Math.floor(pixelCheckSize[0] / 2),
    799            center_y - Math.floor(pixelCheckSize[1] / 2),
    800            pixelCheckSize[0],
    801            pixelCheckSize[1],
    802            [
    803              256.0 * (1.0 - x / x_count),
    804              256.0 * (1.0 - y / y_count),
    805              (!config.isDrawArrays && config.useBaseVertexBuiltin) ? 256.0 * (1.0 - y / y_count) : 255.0,
    806              255.0
    807            ],
    808            msg + ' (' + x + ',' + y + ')', 1.0
    809          ));
    810        }
    811      }
    812      wtu.checkCanvasRects(gl, rects);
    813    }
    814 
    815    // Draw functions variations
    816 
    817    function drawArraysInstancedBaseInstance() {
    818      const countPerDraw = y_count * 6;
    819      for (let x = 0; x < x_count; x += 2) {
    820        ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, countPerDraw, 2, x);
    821      }
    822    }
    823 
    824    function multiDrawArraysInstancedBaseInstance() {
    825      ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, drawArraysParams.firsts, 0, drawArraysParams.counts, 0, drawArraysParams.instances, 0, drawArraysParams.baseInstances, 0, drawArraysParams.drawCount);
    826    }
    827 
    828    function drawElementsInstancedBaseVertexBaseInstance() {
    829      const countPerDraw = 6;
    830      for (let v = 0; v < y_count; ++v) {
    831        for (let x = 0; x < x_count; x += 2) {
    832          ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, countPerDraw, gl.UNSIGNED_SHORT, 0, 2, v * 4, x);
    833        }
    834      }
    835    }
    836 
    837    function multiDrawElementsInstancedBaseVertexBaseInstance() {
    838      ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, drawElementsParams.counts, 0, gl.UNSIGNED_SHORT, drawElementsParams.offsets, 0, drawElementsParams.instances, 0, drawElementsParams.baseVertices, 0, drawElementsParams.baseInstances, 0, drawElementsParams.drawCount);
    839    }
    840 
    841    function checkDraw(config) {
    842      const program = wtu.setupProgram(
    843        gl,
    844        getShaderSource(x_count, y_count, config),
    845        !config.useBaseInstanceBuiltin ? ['vPosition'] : ['vPosition', 'vInstanceID']
    846      );
    847 
    848      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    849 
    850      if (config.isDrawArrays) {
    851        gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer);
    852        gl.enableVertexAttribArray(0);
    853        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
    854      } else {
    855        gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
    856        gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    857        gl.enableVertexAttribArray(0);
    858        gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
    859      }
    860 
    861      if (!config.useBaseInstanceBuiltin) {
    862        gl.bindBuffer(gl.ARRAY_BUFFER, instanceIDBuffer);
    863        gl.enableVertexAttribArray(1);
    864        gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0);
    865        gl.vertexAttribDivisor(1, 1);
    866      }
    867 
    868      config.drawFunc();
    869      wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
    870 
    871      checkResult(config);
    872    }
    873 
    874    checkDraw({
    875      drawFunc: multiDraw ? multiDrawArraysInstancedBaseInstance : drawArraysInstancedBaseInstance,
    876      isDrawArrays: true,
    877      isMultiDraw: multiDraw,
    878      useBaseVertexBuiltin: false,
    879      useBaseInstanceBuiltin: false
    880    });
    881 
    882    checkDraw({
    883      drawFunc: multiDraw ? multiDrawElementsInstancedBaseVertexBaseInstance : drawElementsInstancedBaseVertexBaseInstance,
    884      isDrawArrays: false,
    885      isMultiDraw: multiDraw,
    886      useBaseVertexBuiltin: false,
    887      useBaseInstanceBuiltin: false
    888    });
    889  }
    890 
    891  for (let i = 0; i < bufferUsageSet.length; i++) {
    892    let bufferUsage = bufferUsageSet[i];
    893    debug("Testing with BufferUsage = " + bufferUsage);
    894    setupGeneralBuffers(bufferUsage);
    895    runValidationTests(bufferUsage);
    896    runShaderTests(bufferUsage);
    897    runPixelTests();
    898  }
    899 }
    900 
    901 
    902 async function runDrawTests(testFn) {
    903    function drawArrays(gl) {
    904      gl.drawArrays(gl.TRIANGLES, 0, 6);
    905    }
    906 
    907    function drawElements(gl) {
    908      gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
    909    }
    910 
    911    function drawArraysInstanced(gl) {
    912      gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 1);
    913    }
    914 
    915    function drawElementsInstanced(gl) {
    916      gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1);
    917    }
    918 
    919    function drawArraysInstancedBaseInstanceWEBGL(gl) {
    920      const ext = gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance');
    921      if (!ext) {
    922        throw 'Should not have run this test without WEBGL_draw_instanced_base_vertex_base_instance';
    923      }
    924 
    925      ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, 6, 1, 0);
    926    }
    927 
    928    function drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl) {
    929      const ext = gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance');
    930      if (!ext) {
    931        throw 'Should not have run this test without WEBGL_draw_instanced_base_vertex_base_instance';
    932      }
    933 
    934      ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1, 0, 0);
    935    }
    936 
    937    function multiDrawArraysInstancedBaseInstanceWEBGL(gl) {
    938      const ext = gl.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance');
    939      if (!ext) {
    940        throw 'Should not have run this test without WEBGL_multi_draw_instanced_base_vertex_base_instance';
    941      }
    942      ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, [0], 0, [6], 0, [1], 0, [0], 0, 1);
    943    }
    944 
    945    function multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl) {
    946      const ext = gl.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance');
    947      if (!ext) {
    948        throw 'Should not have run this test without WEBGL_multi_draw_instanced_base_vertex_base_instance';
    949      }
    950      ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(
    951          gl.TRIANGLES,
    952          [6], 0,   // counts
    953          gl.UNSIGNED_BYTE,
    954          [0], 0,   // offsets
    955          [1], 0,   // instances
    956          [0], 0,   // baseVerts
    957          [0], 0,   // baseInstances
    958          1,        // drawCount
    959      );
    960    }
    961 
    962    await testFn(drawArrays);             // sanity check
    963    await testFn(drawElements);           // sanity check
    964    await testFn(drawArraysInstanced);    // sanity check
    965    await testFn(drawElementsInstanced);  // sanity check
    966 
    967    // It's only legal to call testFn if the extension is supported,
    968    // since the invalid vertex attrib tests, in particular, expect the
    969    // draw function to have an effect.
    970    if (gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance')) {
    971      await testFn(drawArraysInstancedBaseInstanceWEBGL);
    972      await testFn(drawElementsInstancedBaseVertexBaseInstanceWEBGL);
    973    }
    974    if (gl.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')) {
    975      await testFn(multiDrawArraysInstancedBaseInstanceWEBGL);
    976      await testFn(multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL);
    977    }
    978 }
    979 
    980 async function runCompositingTests() {
    981    const compositingTestFn = createCompositingTestFn({
    982      webglVersion: 2,
    983      shadersFn(gl) {
    984        const vs = `\
    985        #version 300 es
    986        layout(location = 0) in vec4 position;
    987        void main() {
    988          gl_Position = position;
    989        }
    990        `;
    991        const fs = `\
    992        #version 300 es
    993        precision highp float;
    994        out vec4 fragColor;
    995        void main() {
    996          fragColor = vec4(1, 0, 0, 1);
    997        }
    998        `;
    999        return [vs, fs];
   1000      },
   1001    });
   1002    await runDrawTests(compositingTestFn);
   1003 }
   1004 
   1005 async function runInvalidAttribTests(gl) {
   1006  const invalidAttribTestFn = createInvalidAttribTestFn(gl);
   1007  await runDrawTests(invalidAttribTestFn);
   1008 }
   1009 
   1010 async function main() {
   1011  runTest();
   1012  await runInvalidAttribTests(gl);
   1013  await runCompositingTests();
   1014  finishTest();
   1015 }
   1016 main();
   1017 
   1018 var successfullyParsed = true;
   1019 </script>
   1020 </body>
   1021 </html>