Backed out changeset 7272b7396c78 (bug 1932758) for causing fenix debug failures...
[gecko.git] / dom / canvas / test / webgl-conf / checkout / conformance2 / extensions / webgl-multi-draw-instanced-base-vertex-base-instance.html
blob37f3e237627313cf1797dec9be98c93179418e24
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()
19 color = vec4(1.0, 0.0, 0.0, 1.0);
20 gl_Position = vec4(vPosition * 2.0 - 1.0, gl_BaseInstance, 1);
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()
29 if (gl_InstanceID == 0) {
30 color = vec4(0, 1, 0, 1);
31 } else {
32 color = vec4(1, 0, 0, 1);
34 gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1);
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()
42 color = vec4(1.0, 0.0, 0.0, 1.0);
43 gl_Position = vec4(vPosition * 2.0 - 1.0, gl_BaseVertex, 1);
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()
52 color = vec4(0, 1, 0, 1);
53 gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1);
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()
62 if (gl_VertexID >= 3) {
63 color = vec4(0, 1, 0, 1);
64 } else {
65 color = vec4(1, 0, 0, 1);
67 gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1);
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()
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);
86 gl_Position = vec4(vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
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;
96 </script>
97 <div id="description"></div>
98 <canvas id="canvas" width="128" height="128"> </canvas>
99 <div id="console"></div>
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.");
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,
113 const gl = wtu.create3DContext(canvas, attribs, 2);
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 ];
127 function getTileCenter(x, y) {
128 return [ tileSize[0] * (0.5 + x), tileSize[1] * (0.5 + y) ];
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],
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
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);
158 for (let i = 0; i < vs.length; ++i) {
159 verticesData = verticesData.concat(vs[i]);
162 for (let i = 0; i < is.length; ++i) {
163 nonIndexedVerticesData = nonIndexedVerticesData.concat(vs[is[i]]);
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;
176 const indices = new Uint16Array(indicesData);
177 const vertices = new Float32Array(verticesData);
178 const nonIndexedVertices = new Float32Array(nonIndexedVerticesData);
179 const instanceIDs = new Float32Array(instanceIDsData);
181 const indexBuffer = gl.createBuffer();
182 const vertexBuffer = gl.createBuffer();
183 const nonIndexedVertexBuffer = gl.createBuffer();
184 const instanceIDBuffer = gl.createBuffer();
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)
195 for (let i = 0; i < x_count / 2; ++i) {
196 drawArraysParams.baseInstances[i] = i * 2;
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)
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;
218 function setupGeneralBuffers(bufferUsage) {
219 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
220 gl.bufferData(gl.ARRAY_BUFFER, vertices, bufferUsage);
222 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
223 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, bufferUsage);
225 gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer);
226 gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage);
228 gl.bindBuffer(gl.ARRAY_BUFFER, instanceIDBuffer);
229 gl.bufferData(gl.ARRAY_BUFFER, instanceIDs, bufferUsage);
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');
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');
250 return false;
253 function runTest() {
254 if (!gl) {
255 return function() {
256 testFailed('WebGL context does not exist');
260 doTest('WEBGL_draw_instanced_base_vertex_base_instance', false);
261 doTest('WEBGL_multi_draw_instanced_base_vertex_base_instance', true);
263 testGlslBuiltins();
266 // -
268 function* range(n) {
269 for (let i = 0; i < n; i++) {
270 yield i;
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);
283 return listC;
286 let res = [{}];
287 while (args.length) {
288 const next = args.shift();
289 next[0].defined;
290 res = crossCombine2(res, next);
292 return res;
295 // -
297 const PASSTHROUGH_FRAG_SRC = `\
298 #version 300 es
299 precision mediump float;
300 in vec4 v_color;
301 out vec4 o_color;
303 void main() {
304 o_color = v_color;
308 function testGlslBuiltins() {
309 const EXT = gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance');
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;
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;
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 })();
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;
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;
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 })();
355 const COUNT_UP_DATA = new Int32Array(1000);
356 for (const i in COUNT_UP_DATA) {
357 COUNT_UP_DATA[i] = i;
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);
366 gl.enableVertexAttribArray(1);
367 gl.vertexAttribIPointer(1, 1, gl.INT, 0, 0);
368 gl.vertexAttribDivisor(1, 1);
370 gl.enableVertexAttribArray(2);
371 gl.vertexAttribIPointer(2, 1, gl.INT, 0, 0);
372 gl.vertexAttribDivisor(2, 2);
374 gl.enableVertexAttribArray(3);
375 gl.vertexAttribIPointer(3, 1, gl.INT, 0, 0);
376 gl.vertexAttribDivisor(3, 3);
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);
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);
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.`);
396 })();
398 // Common setup complete
399 // -
400 // Create testcases
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;
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;
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;
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;
441 // -
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);
450 return ret;
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]),
461 console.log('TEST_DESCS', TEST_DESCS);
463 // -
464 // Run testcases
466 gl.disable(gl.DEPTH_TEST);
467 gl.disable(gl.STENCIL_TEST);
468 gl.disable(gl.BLEND);
470 for (const desc of TEST_DESCS) {
471 gl.disable(gl.SCISSOR_TEST);
472 gl.clearBufferfv(gl.COLOR, 0, [1,0,0,1]);
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);
486 gl.useProgram(vertid_prog);
487 if (!desc.draw(desc)) continue;
488 debug('\ndesc: ' + JSON.stringify(desc));
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;
496 expect_pixel(desc, 'vertid_prog', [last_gl_vert_id, last_vert_id, 0, 0]);
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]);
504 // -
506 function doTest(extensionName, multiDraw) {
507 const ext = gl.getExtension(extensionName);
508 if (!runSupportedTest(extensionName, ext)) {
509 return;
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);',
534 ].join('\n');
536 const fs = document.getElementById('fshader').text.trim();
538 return [vs, fs];
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);
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);
550 const instanceBuffer = gl.createBuffer();
551 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
552 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 2 ]), bufferUsage);
554 const program = wtu.setupProgram(gl, ['vshaderSimple', 'fshader'], ['vPosition, vInstanceID'], [0, 1], true);
555 expectTrue(program != null, "can compile simple program");
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);
564 setupInstanced();
566 function setupDrawArrays() {
567 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
568 gl.enableVertexAttribArray(0);
569 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
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);
579 function makeDrawValidationCheck(drawFunc, setup) {
580 if (!drawFunc) {
581 return function() {};
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);
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"
601 checkDrawElementsInstancedBaseVertexBaseInstance(
602 [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 0, 0],
603 gl.NO_ERROR, "with gl.TRIANGLES"
606 checkDrawArraysInstancedBaseInstance(
607 [gl.TRIANGLES, 0, 3, 1, 3],
608 [gl.NO_ERROR, gl.INVALID_OPERATION],
609 "with baseInstance leading to out of bounds"
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"
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"
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"
626 } else {
627 const checkMultiDrawArraysInstancedBaseInstance = makeDrawValidationCheck(
628 ext.multiDrawArraysInstancedBaseInstanceWEBGL, setupDrawArrays);
629 const checkMultiDrawElementsInstancedBaseVertexBaseInstance = makeDrawValidationCheck(
630 ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL, setupDrawElements);
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
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"
685 checkMultiDrawArraysInstancedBaseInstance(
686 [gl.TRIANGLES, [0], 0, [3], 1, [1], 0, [0], 0, 1],
687 gl.INVALID_OPERATION, "with countsStart >= countsList.length"
689 checkMultiDrawArraysInstancedBaseInstance(
690 [gl.TRIANGLES, [0], 0, [3], 0, [1], 1, [0], 0, 1],
691 gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length"
693 checkMultiDrawArraysInstancedBaseInstance(
694 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 1, 1],
695 gl.INVALID_OPERATION, "with baseInstancesStart >= baseInstancesList.length"
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"
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"
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"
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"
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"
721 function runShaderTests(bufferUsage) {
722 let badProgram;
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");
729 badProgram = wtu.setupProgram(gl, ["vshaderWithExt", "fshader"]);
730 expectTrue(!badProgram, "cannot compile program with #extension GL_ANGLE_base_vertex_base_instance");
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);
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);
743 const instanceIDProgram = wtu.setupProgram(gl, ["vshaderInstanceIDCheck", "fshader"], ["vPosition"], [0]);
744 expectTrue(instanceIDProgram !== null, "can compile program with gl_InstanceID");
745 gl.useProgram(instanceIDProgram);
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);
754 wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_InstanceID should always starts from 0");
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);
764 const vertexIDProgram = wtu.setupProgram(gl, ["vshaderVertexIDCheck", "fshader"], ["vPosition"], [0]);
765 expectTrue(vertexIDProgram !== null, "can compile program with gl_VertexID");
766 gl.useProgram(vertexIDProgram);
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);
775 wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_VertexID should always starts from 0");
778 function runPixelTests() {
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],
787 const msg = config.drawFunc.name + (
788 config.useBaseVertexBuiltin ? ' gl_BaseVertex' : ''
789 ) + (
790 config.useBaseInstanceBuiltin ? ' gl_BaseInstance' : ' InstanceIDArray'
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);
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],
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
808 msg + ' (' + x + ',' + y + ')', 1.0
812 wtu.checkCanvasRects(gl, rects);
815 // Draw functions variations
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);
824 function multiDrawArraysInstancedBaseInstance() {
825 ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, drawArraysParams.firsts, 0, drawArraysParams.counts, 0, drawArraysParams.instances, 0, drawArraysParams.baseInstances, 0, drawArraysParams.drawCount);
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);
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);
841 function checkDraw(config) {
842 const program = wtu.setupProgram(
844 getShaderSource(x_count, y_count, config),
845 !config.useBaseInstanceBuiltin ? ['vPosition'] : ['vPosition', 'vInstanceID']
848 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
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);
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);
868 config.drawFunc();
869 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
871 checkResult(config);
874 checkDraw({
875 drawFunc: multiDraw ? multiDrawArraysInstancedBaseInstance : drawArraysInstancedBaseInstance,
876 isDrawArrays: true,
877 isMultiDraw: multiDraw,
878 useBaseVertexBuiltin: false,
879 useBaseInstanceBuiltin: false
882 checkDraw({
883 drawFunc: multiDraw ? multiDrawElementsInstancedBaseVertexBaseInstance : drawElementsInstancedBaseVertexBaseInstance,
884 isDrawArrays: false,
885 isMultiDraw: multiDraw,
886 useBaseVertexBuiltin: false,
887 useBaseInstanceBuiltin: false
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();
902 async function runDrawTests(testFn) {
903 function drawArrays(gl) {
904 gl.drawArrays(gl.TRIANGLES, 0, 6);
907 function drawElements(gl) {
908 gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
911 function drawArraysInstanced(gl) {
912 gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 1);
915 function drawElementsInstanced(gl) {
916 gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1);
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';
925 ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, 6, 1, 0);
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';
934 ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1, 0, 0);
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';
942 ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, [0], 0, [6], 0, [1], 0, [0], 0, 1);
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';
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
962 await testFn(drawArrays); // sanity check
963 await testFn(drawElements); // sanity check
964 await testFn(drawArraysInstanced); // sanity check
965 await testFn(drawElementsInstanced); // sanity check
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);
974 if (gl.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')) {
975 await testFn(multiDrawArraysInstancedBaseInstanceWEBGL);
976 await testFn(multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL);
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;
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);
999 return [vs, fs];
1002 await runDrawTests(compositingTestFn);
1005 async function runInvalidAttribTests(gl) {
1006 const invalidAttribTestFn = createInvalidAttribTestFn(gl);
1007 await runDrawTests(invalidAttribTestFn);
1010 async function main() {
1011 runTest();
1012 await runInvalidAttribTests(gl);
1013 await runCompositingTests();
1014 finishTest();
1016 main();
1018 var successfullyParsed = true;
1019 </script>
1020 </body>
1021 </html>