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>
14 <script id=
"vshaderIllegalDrawID" type=
"x-shader/x-vertex">
15 attribute vec2 vPosition;
19 color = vec4(
1.0,
0.0,
0.0,
1.0);
20 gl_Position = vec4(vPosition *
2.0 -
1.0, gl_DrawID,
1);
23 <script id=
"vshaderDrawIDZero" type=
"x-shader/x-vertex">
24 #extension GL_ANGLE_multi_draw : require
25 attribute vec2 vPosition;
30 color = vec4(
0,
1,
0,
1);
32 color = vec4(
1,
0,
0,
1);
34 gl_Position = vec4(vPosition *
2.0 -
1.0,
0,
1);
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;
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));
49 color = vec4(
1,
0,
0,
1);
50 } else if (color_id <
1.5) {
51 color = vec4(
0,
1,
0,
1);
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);
73 <script id=
"vshaderEmulatedDrawID" type=
"x-shader/x-vertex">
75 attribute vec2 vPosition;
76 attribute float vInstance;
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);
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);
107 <script id=
"vshaderNoDrawID" type=
"x-shader/x-vertex">
108 attribute vec2 vPosition;
109 attribute float vInstance;
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);
132 <script id=
"fshader" type=
"x-shader/x-fragment">
133 precision mediump float;
136 gl_FragColor = color;
139 <div id=
"description"></div>
140 <canvas id=
"canvas" width=
"128" height=
"128"> </canvas>
141 <div id=
"console"></div>
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');
162 testFailed(extensionName
+ ' listed as supported but getExtension failed');
165 if (extensionEnabled
) {
166 testFailed(extensionName
+ ' not listed as supported but getExtension succeeded');
168 testPassed(extensionName
+ ' not listed as supported and getExtension failed -- this is legal');
177 testFailed('WebGL context does not exist');
181 const extensionName
= 'WEBGL_multi_draw';
182 const ext
= gl
.getExtension(extensionName
);
183 if (!runSupportedTest(extensionName
, ext
)) {
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);
229 gl
.vertexAttribDivisor(1, 1);
233 function setupDrawArraysInstanced() {
238 function setupDrawElementsInstanced() {
243 // Wrap a draw call in a function to setup the draw call, execute,
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
) {
249 return function() {};
251 return function(f_args
, expect
, msg
) {
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
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");
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
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");
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
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");
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
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");
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
;
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
);
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
) {
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,
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,
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,
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,
496 msg
+ " (" + x
+ "," + y
+ ")", 0));
498 rects
.push(wtu
.makeCheckRect(
499 center_x
- Math
.floor(pixelCheckSize
[0] / 2),
500 center_y
- Math
.floor(pixelCheckSize
[1] / 2),
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;
526 for (let i
= 0; i
< offsets
.length
; ++i
) offsets
[i
] = i
* 3 * 2;
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
);
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
);
604 gl
.drawArraysInstanced
.apply(gl
, arguments
);
608 function drawElementsInstanced() {
609 if (wtu
.getDefault3DContextVersion() < 2) {
610 instancedExt
.drawElementsInstancedANGLE
.apply(instancedExt
, arguments
);
612 gl
.drawElementsInstanced
.apply(gl
, arguments
);
616 function vertexAttribDivisor(attrib
, divisor
) {
617 if (wtu
.getDefault3DContextVersion() < 2) {
618 instancedExt
.vertexAttribDivisorANGLE(attrib
, divisor
);
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);
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);
672 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
673 checkResult(config
, config
.drawFunc
.name
+ (
674 config
.instanced
? ' instanced' : ''
676 config
.drawID
? ' with gl_DrawID' : ''
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
);
692 drawFunc
: multiDrawArrays
,
696 drawFunc
: multiDrawArraysWithNonzeroOffsets
,
700 drawFunc
: multiDrawElements
,
705 drawFunc
: multiDrawElementsWithNonzeroOffsets
,
710 drawFunc
: manyDrawArrays
,
714 drawFunc
: manyDrawElements
,
720 drawFunc
: multiDrawArraysInstanced
,
725 drawFunc
: multiDrawArraysInstancedWithNonzeroOffsets
,
730 drawFunc
: multiDrawElementsInstanced
,
736 drawFunc
: multiDrawElementsInstancedWithNonzeroOffsets
,
742 drawFunc
: manyDrawArraysInstanced
,
747 drawFunc
: manyDrawElementsInstanced
,
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
);
762 drawFunc
: multiDrawArrays
,
766 drawFunc
: multiDrawArraysWithNonzeroOffsets
,
770 drawFunc
: multiDrawElements
,
775 drawFunc
: multiDrawElementsWithNonzeroOffsets
,
781 drawFunc
: multiDrawArraysInstanced
,
786 drawFunc
: multiDrawArraysInstancedWithNonzeroOffsets
,
791 drawFunc
: multiDrawElementsInstanced
,
797 drawFunc
: multiDrawElementsInstancedWithNonzeroOffsets
,
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
);
813 drawFunc
: manyDrawArraysEmulateDrawID
,
817 drawFunc
: manyDrawElementsEmulateDrawID
,
823 drawFunc
: manyDrawArraysInstancedEmulateDrawID
,
828 drawFunc
: manyDrawElementsInstancedEmulateDrawID
,
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
) {
858 debug('test preserveDrawingBuffer: false');
861 debug('skipped: extension does not exist');
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
) {
890 debug('test preserveDrawingBuffer: true');
892 debug('skipped: extension does not exist');
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
) {
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');
934 throw 'Should not have run this test without WEBGL_multi_draw';
937 ext
.multiDrawArraysWEBGL(
945 function multiDrawElementsWEBGL(gl
) {
946 const ext
= gl
.getExtension('WEBGL_multi_draw');
948 throw 'Should not have run this test without WEBGL_multi_draw';
951 ext
.multiDrawElementsWEBGL(
960 function multiDrawArraysInstancedWEBGL(gl
) {
961 const ext
= gl
.getExtension('WEBGL_multi_draw');
963 throw 'Should not have run this test without WEBGL_multi_draw';
965 ext
.multiDrawArraysInstancedWEBGL(
974 function multiDrawElementsInstancedWEBGL(gl
) {
975 const ext
= gl
.getExtension('WEBGL_multi_draw');
977 throw 'Should not have run this test without WEBGL_multi_draw';
979 ext
.multiDrawElementsInstancedWEBGL(
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({
1008 //#extension GL_ANGLE_multi_draw : enable
1009 attribute vec4 position;
1011 gl_Position = position;
1015 precision mediump float;
1017 gl_FragColor = vec4(1, 0, 0, 1);
1023 await
runDrawTests(compositingTestFn
);
1026 async
function runInvalidAttribTests(gl
) {
1027 const invalidAttribTestFn
= createInvalidAttribTestFn(gl
);
1028 await
runDrawTests(invalidAttribTestFn
);
1031 function testSideEffects() {
1033 debug("Testing that ANGLE_instanced_arrays is implicitly enabled")
1034 const canvas
= document
.createElement("canvas");
1035 const gl
= wtu
.create3DContext(canvas
, undefined, 1);
1037 testFailed('WebGL context creation failed');
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");
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() {
1055 await
runInvalidAttribTests(gl
);
1056 await
runCompositingTests();
1061 var successfullyParsed
= true;