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>
14 <script id=
"vshaderBaseInstanceWithoutExt" type=
"x-shader/x-vertex">#version
300 es
15 layout(location =
0) in vec2 vPosition;
19 color = vec4(
1.0,
0.0,
0.0,
1.0);
20 gl_Position = vec4(vPosition *
2.0 -
1.0, gl_BaseInstance,
1);
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;
29 if (gl_InstanceID ==
0) {
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 <script id=
"vshaderBaseVertexWithoutExt" type=
"x-shader/x-vertex">#version
300 es
38 layout(location =
0) in vec2 vPosition;
42 color = vec4(
1.0,
0.0,
0.0,
1.0);
43 gl_Position = vec4(vPosition *
2.0 -
1.0, gl_BaseVertex,
1);
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;
52 color = vec4(
0,
1,
0,
1);
53 gl_Position = vec4(vPosition *
2.0 -
1.0,
0,
1);
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;
62 if (gl_VertexID
>=
3) {
63 color = vec4(
0,
1,
0,
1);
65 color = vec4(
1,
0,
0,
1);
67 gl_Position = vec4(vPosition *
2.0 -
1.0,
0,
1);
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;
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);
83 color = vec4(
0.0,
0.0,
0.0,
1.0);
86 gl_Position = vec4(vec3(vPosition,
1.0) *
2.0 -
1.0,
1);
89 <script id=
"fshader" type=
"x-shader/x-fragment">#version
300 es
90 precision mediump float;
97 <div id=
"description"></div>
98 <canvas id=
"canvas" width=
"128" height=
"128"> </canvas>
99 <div id=
"console"></div>
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';
113 const gl
= wtu
.create3DContext(canvas
, attribs
, 2);
115 const width
= gl
.canvas
.width
;
116 const height
= gl
.canvas
.height
;
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
);
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
) {
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
) {
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
)
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
;
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');
241 testFailed(extensionName
+ ' listed as supported but getExtension failed');
244 if (extensionEnabled
) {
245 testFailed(extensionName
+ ' not listed as supported but getExtension succeeded');
247 testPassed(extensionName
+ ' not listed as supported and getExtension failed -- this is legal');
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);
269 for (let i
= 0; i
< n
; i
++) {
274 function crossCombine(...args
) {
275 function crossCombine2(listA
, listB
) {
277 for (const a
of listA
) {
278 for (const b
of listB
) {
279 const c
= Object
.assign({}, a
, b
);
287 while (args
.length
) {
288 const next
= args
.shift();
290 res
= crossCombine2(res
, next
);
297 const PASSTHROUGH_FRAG_SRC
= `\
299 precision mediump float;
308 function testGlslBuiltins() {
309 const EXT
= gl
.getExtension('WEBGL_draw_instanced_base_vertex_base_instance');
311 const vertid_prog
= (() => {
315 layout(location = 0) in int a_vertex_id; // Same as gl_VertexID
319 gl_Position = vec4(0,0,0,1);
321 v_color = vec4(float(gl_VertexID), float(a_vertex_id),0,0);
325 const prog
= wtu
.setupProgram(gl
, [vert_src
, PASSTHROUGH_FRAG_SRC
],
326 undefined, undefined, /*logShaders*/ true);
327 expectTrue(!!prog
, `make_vertid_prog failed`);
331 const instid_prog
= (() => {
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)
342 gl_Position = vec4(0,0,0,1);
344 v_color = vec4(float(gl_InstanceID), float(a_instance_div1),
345 float(a_instance_div2), float(a_instance_div3));
349 const prog
= wtu
.setupProgram(gl
, [vert_src
, PASSTHROUGH_FRAG_SRC
],
350 undefined, undefined, /*logShaders*/ true);
351 expectTrue(!!prog
, `make_instid_prog failed`);
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)}`);
393 debug(`${subtest}: Was [${was}] as expected.`);
398 // Common setup complete
402 const DRAW_FUNC_COMBINER
= [{
403 name
: 'drawArraysInstanced',
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
);
412 name
: 'drawElementsInstanced',
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
);
421 name
: 'drawArraysInstancedBaseInstanceWEBGL',
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
,
431 name
: 'drawElementsInstancedBaseVertexBaseInstanceWEBGL',
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
);
443 function make_key_combiner(key
, vals
) {
445 for (const v
of vals
) {
453 const TEST_DESCS
= crossCombine(
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
);
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]);
496 expect_pixel(desc
, 'vertid_prog', [last_gl_vert_id
, last_vert_id
, 0, 0]);
498 gl
.useProgram(instid_prog
);
500 expect_pixel(desc
, 'instid_prog', [last_inst_id
, last_inst_div1
, last_inst_div2
, last_inst_div3
]);
506 function doTest(extensionName
, multiDraw
) {
507 const ext
= gl
.getExtension(extensionName
);
508 if (!runSupportedTest(extensionName
, ext
)) {
512 function getShaderSource(countX
, countY
, config
) {
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;',
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',
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);',
536 const fs
= document
.getElementById('fshader').text
.trim();
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);
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
) {
581 return function() {};
583 return function(f_args
, expect
, msg
) {
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);
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"
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
) {
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);
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
);
749 ext
.drawArraysInstancedBaseInstanceWEBGL(gl
.TRIANGLES
, 0, 6, 1, 5);
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");
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
);
770 ext
.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0, 1, 3, 0);
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
) {
787 const msg
= config
.drawFunc
.name
+ (
788 config
.useBaseVertexBuiltin
? ' gl_BaseVertex' : ''
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),
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,
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);
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);
869 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
875 drawFunc
: multiDraw
? multiDrawArraysInstancedBaseInstance
: drawArraysInstancedBaseInstance
,
877 isMultiDraw
: multiDraw
,
878 useBaseVertexBuiltin
: false,
879 useBaseInstanceBuiltin
: false
883 drawFunc
: multiDraw
? multiDrawElementsInstancedBaseVertexBaseInstance
: drawElementsInstancedBaseVertexBaseInstance
,
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
);
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');
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');
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');
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');
948 throw 'Should not have run this test without WEBGL_multi_draw_instanced_base_vertex_base_instance';
950 ext
.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(
957 [0], 0, // baseInstances
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({
986 layout(location = 0) in vec4 position;
988 gl_Position = position;
993 precision highp float;
996 fragColor = vec4(1, 0, 0, 1);
1002 await
runDrawTests(compositingTestFn
);
1005 async
function runInvalidAttribTests(gl
) {
1006 const invalidAttribTestFn
= createInvalidAttribTestFn(gl
);
1007 await
runDrawTests(invalidAttribTestFn
);
1010 async
function main() {
1012 await
runInvalidAttribTests(gl
);
1013 await
runCompositingTests();
1018 var successfullyParsed
= true;