Backed out changeset b462e7b742d8 (bug 1908261) for causing multiple reftest failures...
[gecko.git] / dom / canvas / test / webgl-conf / checkout / conformance / extensions / webgl-multi-draw.html
blobae4bbc1af0e3ef34bd32f0c0b1429f6a8f859b85
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()
19 color = vec4(1.0, 0.0, 0.0, 1.0);
20 gl_Position = vec4(vPosition * 2.0 - 1.0, gl_DrawID, 1);
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()
29 if (gl_DrawID == 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 <!-- 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()
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);
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;
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;
70 gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
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()
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);
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;
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;
104 gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
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()
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;
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;
129 gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1);
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;
138 </script>
139 <div id="description"></div>
140 <canvas id="canvas" width="128" height="128"> </canvas>
141 <div id="console"></div>
143 <script>
144 "use strict";
145 description("This test verifies the functionality of the ANGLE_multi_draw extension, if it is available.");
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 ];
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');
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');
171 return false;
174 function runTest() {
175 if (!gl) {
176 return function() {
177 testFailed('WebGL context does not exist');
181 const extensionName = 'WEBGL_multi_draw';
182 const ext = gl.getExtension(extensionName);
183 if (!runSupportedTest(extensionName, ext)) {
184 return;
187 doTest(ext, false);
188 doTest(ext, true);
191 function doTest(ext, instanced) {
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);
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);
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);
206 const program = wtu.setupProgram(gl, ["vshaderNoDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]);
207 expectTrue(program != null, "can compile simple program");
209 function setupDrawArrays() {
210 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
211 gl.enableVertexAttribArray(0);
212 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
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);
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);
233 function setupDrawArraysInstanced() {
234 setupDrawArrays();
235 setupInstanced();
238 function setupDrawElementsInstanced() {
239 setupDrawElements();
240 setupInstanced();
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() {};
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);
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);
269 gl.useProgram(program);
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");
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");
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");
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");
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");
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");
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");
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);
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.
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.
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) ];
397 function getTileCenter(x, y) {
398 return [ tileSize[0] * (0.5 + x), tileSize[1] * (0.5 + y) ];
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],
411 const indicesData = [];
412 const verticesData = [];
413 const nonIndexedVerticesData = [];
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]);
424 for (let i = 0; i < vs.length; ++i) {
425 for (let v = 0; v < vs[i].length; ++v) verticesData.push(vs[i][v]);
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]);
434 const indices = new Uint16Array(indicesData);
435 const vertices = new Float32Array(verticesData);
436 const nonIndexedVertices = new Float32Array(nonIndexedVerticesData);
438 const indexBuffer = gl.createBuffer();
439 const vertexBuffer = gl.createBuffer();
440 const nonIndexedVertexBuffer = gl.createBuffer();
441 const instanceBuffer = gl.createBuffer();
443 gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer);
444 gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage);
446 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
447 gl.bufferData(gl.ARRAY_BUFFER, vertices, bufferUsage);
449 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
450 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, bufferUsage);
452 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer);
453 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), bufferUsage);
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],
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));
508 wtu.checkCanvasRects(gl, rects);
511 function newIntArray(count) {
512 if (!useSharedArrayBuffer) {
513 return new Int32Array(count);
515 let sab = new SharedArrayBuffer(count * Int32Array.BYTES_PER_ELEMENT);
516 return new Int32Array(sab);
519 const firsts = newIntArray(tri_count);
520 const counts = newIntArray(tri_count);
521 const offsets = newIntArray(tri_count);
522 const instances = newIntArray(tri_count);
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);
529 const firstsOffset = 47;
530 const countsOffset = firstsOffset + firsts.length;
531 const offsetsOffset = countsOffset + counts.length;
532 const instancesOffset = offsetsOffset + instances.length;
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);
540 let drawIDLocation;
542 const multiDrawArrays = function() {
543 ext.multiDrawArraysWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, tri_count);
546 const multiDrawArraysWithNonzeroOffsets = function() {
547 ext.multiDrawArraysWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, tri_count);
550 const multiDrawElements = function() {
551 ext.multiDrawElementsWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, tri_count);
554 const multiDrawElementsWithNonzeroOffsets = function() {
555 ext.multiDrawElementsWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, tri_count);
558 const multiDrawArraysInstanced = function() {
559 ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, instances, 0, tri_count);
562 const multiDrawArraysInstancedWithNonzeroOffsets = function() {
563 ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, buffer, instancesOffset, tri_count);
566 const multiDrawElementsInstanced = function() {
567 ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, instances, 0, tri_count);
570 const multiDrawElementsInstancedWithNonzeroOffsets = function() {
571 ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, buffer, instancesOffset, tri_count);
574 const manyDrawArrays = function() {
575 for (let i = 0; i < tri_count; ++i) {
576 gl.drawArrays(gl.TRIANGLES, firsts[i], counts[i]);
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]);
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]);
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]);
600 function drawArraysInstanced() {
601 if (wtu.getDefault3DContextVersion() < 2) {
602 instancedExt.drawArraysInstancedANGLE.apply(instancedExt, arguments);
603 } else {
604 gl.drawArraysInstanced.apply(gl, arguments);
608 function drawElementsInstanced() {
609 if (wtu.getDefault3DContextVersion() < 2) {
610 instancedExt.drawElementsInstancedANGLE.apply(instancedExt, arguments);
611 } else {
612 gl.drawElementsInstanced.apply(gl, arguments);
616 function vertexAttribDivisor(attrib, divisor) {
617 if (wtu.getDefault3DContextVersion() < 2) {
618 instancedExt.vertexAttribDivisorANGLE(attrib, divisor);
619 } else {
620 gl.vertexAttribDivisor(attrib, divisor);
624 const manyDrawArraysInstanced = function() {
625 for (let i = 0; i < tri_count; ++i) {
626 drawArraysInstanced(gl.TRIANGLES, firsts[i], counts[i], 4);
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);
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);
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);
650 function checkDraw(config) {
651 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
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);
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);
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' : ''
681 gl.disableVertexAttribArray(0);
682 gl.disableVertexAttribArray(1);
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);
690 if (!instanced) {
691 checkDraw({
692 drawFunc: multiDrawArrays,
693 drawID: false,
695 checkDraw({
696 drawFunc: multiDrawArraysWithNonzeroOffsets,
697 drawID: false,
699 checkDraw({
700 drawFunc: multiDrawElements,
701 indexed: true,
702 drawID: false,
704 checkDraw({
705 drawFunc: multiDrawElementsWithNonzeroOffsets,
706 indexed: true,
707 drawID: false,
709 checkDraw({
710 drawFunc: manyDrawArrays,
711 drawID: false,
713 checkDraw({
714 drawFunc: manyDrawElements,
715 indexed: true,
716 drawID: false,
718 } else {
719 checkDraw({
720 drawFunc: multiDrawArraysInstanced,
721 drawID: false,
722 instanced: true,
724 checkDraw({
725 drawFunc: multiDrawArraysInstancedWithNonzeroOffsets,
726 drawID: false,
727 instanced: true,
729 checkDraw({
730 drawFunc: multiDrawElementsInstanced,
731 indexed: true,
732 drawID: false,
733 instanced: true,
735 checkDraw({
736 drawFunc: multiDrawElementsInstancedWithNonzeroOffsets,
737 indexed: true,
738 drawID: false,
739 instanced: true,
741 checkDraw({
742 drawFunc: manyDrawArraysInstanced,
743 drawID: false,
744 instanced: true,
746 checkDraw({
747 drawFunc: manyDrawElementsInstanced,
748 indexed: true,
749 drawID: false,
750 instanced: true,
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);
760 if (!instanced) {
761 checkDraw({
762 drawFunc: multiDrawArrays,
763 drawID: true,
765 checkDraw({
766 drawFunc: multiDrawArraysWithNonzeroOffsets,
767 drawID: true,
769 checkDraw({
770 drawFunc: multiDrawElements,
771 indexed: true,
772 drawID: true,
774 checkDraw({
775 drawFunc: multiDrawElementsWithNonzeroOffsets,
776 indexed: true,
777 drawID: true,
779 } else {
780 checkDraw({
781 drawFunc: multiDrawArraysInstanced,
782 drawID: true,
783 instanced: true,
785 checkDraw({
786 drawFunc: multiDrawArraysInstancedWithNonzeroOffsets,
787 drawID: true,
788 instanced: true,
790 checkDraw({
791 drawFunc: multiDrawElementsInstanced,
792 indexed: true,
793 drawID: true,
794 instanced: true,
796 checkDraw({
797 drawFunc: multiDrawElementsInstancedWithNonzeroOffsets,
798 indexed: true,
799 drawID: true,
800 instanced: true,
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);
811 if (!instanced) {
812 checkDraw({
813 drawFunc: manyDrawArraysEmulateDrawID,
814 drawID: true,
816 checkDraw({
817 drawFunc: manyDrawElementsEmulateDrawID,
818 indexed: true,
819 drawID: true,
821 } else {
822 checkDraw({
823 drawFunc: manyDrawArraysInstancedEmulateDrawID,
824 drawID: true,
825 instanced: true,
827 checkDraw({
828 drawFunc: manyDrawElementsInstancedEmulateDrawID,
829 indexed: true,
830 drawID: true,
831 instanced: true,
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);
845 // Run a subset of the pixel tests with SharedArrayBuffer if supported.
846 if (window.SharedArrayBuffer) {
847 runPixelTests(bufferUsageSet[0], true);
851 function waitForComposite() {
852 debug('wait for composite');
853 return new Promise(resolve => wtu.waitForComposite(resolve));
856 async function testPreserveDrawingBufferFalse(gl, drawFn) {
857 debug('');
858 debug('test preserveDrawingBuffer: false');
860 if (drawFn(gl)) {
861 debug('skipped: extension does not exist');
862 return;
865 wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255],
866 "canvas should be red");
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);
873 await waitForComposite();
875 // scissor was set earlier
876 gl.clearColor(0, 0, 1, 1);
877 gl.clear(gl.COLOR_BUFFER_BIT);
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");
884 gl.disable(gl.SCISSOR_TEST);
885 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
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;
896 wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255],
897 "canvas should be red");
899 await waitForComposite();
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");
906 async function doCompositingTests([glPreserveDrawingBufferFalse, glPreserveDrawingBufferTrue], drawFn) {
907 debug('');
908 debug(drawFn.name);
910 await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn);
911 await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn);
914 async function runDrawTests(testFn) {
915 function drawArrays(gl) {
916 gl.drawArrays(gl.TRIANGLES, 0, 6);
919 function drawElements(gl) {
920 gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
923 function drawArraysInstanced(gl) {
924 gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 1);
927 function drawElementsInstanced(gl) {
928 gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1);
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';
937 ext.multiDrawArraysWEBGL(
938 gl.TRIANGLES,
939 [0], 0, // firsts
940 [6], 0, // counts
941 1, // drawCount
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';
951 ext.multiDrawElementsWEBGL(
952 gl.TRIANGLES,
953 [6], 0, // counts
954 gl.UNSIGNED_BYTE,
955 [0], 0, // offsets
956 1, // drawCount
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';
965 ext.multiDrawArraysInstancedWEBGL(
966 gl.TRIANGLES,
967 [0], 0, // firsts
968 [6], 0, // counts
969 [1], 0, // instances
970 1, // drawCount
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';
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
989 await testFn(drawArrays); // sanity check
990 await testFn(drawElements); // sanity check
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);
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;
1014 const fs = `\
1015 precision mediump float;
1016 void main() {
1017 gl_FragColor = vec4(1, 0, 0, 1);
1020 return [vs, fs];
1023 await runDrawTests(compositingTestFn);
1026 async function runInvalidAttribTests(gl) {
1027 const invalidAttribTestFn = createInvalidAttribTestFn(gl);
1028 await runDrawTests(invalidAttribTestFn);
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;
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");
1052 async function main() {
1053 runTest();
1054 testSideEffects();
1055 await runInvalidAttribTests(gl);
1056 await runCompositingTests();
1057 finishTest();
1059 main();
1061 var successfullyParsed = true;
1062 </script>
1063 </body>
1064 </html>