2 Copyright (c) 2019 The Khronos Group Inc.
3 Use of this source code is governed by an MIT-style license that can be
4 found in the LICENSE.txt file.
10 <meta charset=
"utf-8">
11 <title>WebGL ANGLE_instanced_arrays Conformance Tests
</title>
12 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
13 <script src=
"../../js/desktop-gl-constants.js"></script>
14 <script src=
"../../js/js-test-pre.js"></script>
15 <script src=
"../../js/webgl-test-utils.js"></script>
16 <script src=
"../../js/tests/compositing-test.js"></script>
17 <script src=
"../../js/tests/invalid-vertex-attrib-test.js"></script>
20 <div id=
"description"></div>
21 <canvas id=
"canvas" style=
"width: 50px; height: 50px;"> </canvas>
22 <div id=
"console"></div>
23 <!-- Shaders for testing instanced draws -->
24 <script id=
"outputVertexShader" type=
"x-shader/x-vertex">
25 attribute vec4 aPosition;
26 attribute vec2 aOffset;
27 attribute vec4 aColor;
31 gl_Position = aPosition + vec4(aOffset,
0.0,
0.0);
35 <script id=
"outputFragmentShader" type=
"x-shader/x-fragment">
36 precision mediump float;
39 gl_FragColor = vColor;
43 <script id=
"drawArraysTestVertexShader" type=
"x-shader/x-vertex">
44 attribute vec3 aPosition;
45 attribute vec3 aInstancePos;
48 gl_Position = vec4(aPosition.xyz + aInstancePos.xyz + uOffset,
1.0);
52 <script id=
"drawArraysTestFragmentShader" type=
"x-shader/x-fragment">
54 gl_FragColor = vec4(
1.0,
0,
0,
1.0);
60 description("This test verifies the functionality of the ANGLE_instanced_arrays extension, if it is available.");
64 const wait
= ms
=> new Promise(resolve
=> setTimeout(resolve
, ms
));
66 var wtu
= WebGLTestUtils
;
67 var canvas
= document
.getElementById("canvas");
68 var gl
= wtu
.create3DContext(canvas
);
78 testFailed("WebGL context does not exist");
81 testPassed("WebGL context exists");
83 runDivisorTestDisabled();
85 // Query the extension and store globally so shouldBe can access it
86 ext
= wtu
.getExtensionWithKnownPrefixes(gl
, "ANGLE_instanced_arrays");
88 testPassed("No ANGLE_instanced_arrays support -- this is legal");
90 runSupportedTest(false);
93 testPassed("Successfully enabled ANGLE_instanced_arrays extension");
96 runSupportedTest(true);
98 runDivisorTestEnabled();
99 runUniqueObjectTest();
103 runDrawArraysWithOffsetTest();
104 runVAOInstancingInteractionTest();
105 await
runANGLECorruptionTest();
106 await
runInvalidAttribTests(gl
);
107 await
runCompositingTests();
113 function runSupportedTest(extensionEnabled
) {
114 var supported
= gl
.getSupportedExtensions();
115 if (supported
.indexOf("ANGLE_instanced_arrays") >= 0) {
116 if (extensionEnabled
) {
117 testPassed("ANGLE_instanced_arrays listed as supported and getExtension succeeded");
119 testFailed("ANGLE_instanced_arrays listed as supported but getExtension failed");
122 if (extensionEnabled
) {
123 testFailed("ANGLE_instanced_arrays not listed as supported but getExtension succeeded");
125 testPassed("ANGLE_instanced_arrays not listed as supported and getExtension failed -- this is legal");
130 function runDivisorTestDisabled() {
131 debug("Testing VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE with extension disabled");
133 var VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE
= 0x88FE;
135 gl
.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE
);
136 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE should not be queryable if extension is disabled");
139 function runDivisorTestEnabled() {
140 debug("Testing VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE with extension enabled");
142 shouldBe("ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE", "0x88FE");
144 var max_vertex_attribs
= gl
.getParameter(gl
.MAX_VERTEX_ATTRIBS
);
146 for (var i
= 0; i
< max_vertex_attribs
; ++i
) {
147 var queried_value
= gl
.getVertexAttrib(i
, ext
.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE
);
148 if(queried_value
== 0){
149 testPassed("Vertex attribute " + i
+ " must has a default divisor of 0");
152 testFailed("Default divisor of vertex attribute " + i
+ " should be: 0, returned value was: " + queried_value
);
156 ext
.vertexAttribDivisorANGLE(max_vertex_attribs
, 2);
157 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "vertexAttribDivisorANGLE index set greater than or equal to MAX_VERTEX_ATTRIBS should be an invalid value");
159 ext
.vertexAttribDivisorANGLE(max_vertex_attribs
-1, 2);
160 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "vertexAttribDivisorANGLE index set less than MAX_VERTEX_ATTRIBS should succeed");
162 var queried_value
= gl
.getVertexAttrib(max_vertex_attribs
-1, ext
.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE
);
163 if(queried_value
== 2){
164 testPassed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE matches expecation");
167 testFailed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE should be: 2, returned value was: " + queried_value
);
170 // Reset vertex attrib divisors so they cannot affect following subtests.
171 ext
.vertexAttribDivisorANGLE(max_vertex_attribs
-1, 0);
174 function setupCanvas() {
175 canvas
.width
= 50; canvas
.height
= 50;
176 gl
.viewport(0, 0, canvas
.width
, canvas
.height
);
177 gl
.clearColor(0, 0, 0, 0);
179 program
= wtu
.setupProgram(gl
, ["outputVertexShader", "outputFragmentShader"], ['aPosition', 'aOffset', 'aColor'], [positionLoc
, offsetLoc
, colorLoc
]);
180 ext
= gl
.getExtension("ANGLE_instanced_arrays");
183 function runOutputTests() {
184 var instanceCount
= 4;
186 debug("Testing various draws for valid built-in function behavior");
188 var offsets
= new Float32Array([
194 var offsetBuffer
= gl
.createBuffer();
195 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
196 gl
.bufferData(gl
.ARRAY_BUFFER
, offsets
, gl
.STATIC_DRAW
);
197 gl
.enableVertexAttribArray(offsetLoc
);
198 gl
.vertexAttribPointer(offsetLoc
, 2, gl
.FLOAT
, false, 0, 0);
199 ext
.vertexAttribDivisorANGLE(offsetLoc
, 1);
201 var colors
= new Float32Array([
202 1.0, 0.0, 0.0, 1.0, // Red
203 0.0, 1.0, 0.0, 1.0, // Green
204 0.0, 0.0, 1.0, 1.0, // Blue
205 1.0, 1.0, 0.0, 1.0, // Yellow
206 // extra data when colorLoc divisor is set back to 0
207 1.0, 1.0, 0.0, 1.0, // Yellow
208 1.0, 1.0, 0.0, 1.0, // Yellow
210 var colorBuffer
= gl
.createBuffer();
211 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
212 gl
.bufferData(gl
.ARRAY_BUFFER
, colors
, gl
.STATIC_DRAW
);
213 gl
.enableVertexAttribArray(colorLoc
);
214 gl
.vertexAttribPointer(colorLoc
, 4, gl
.FLOAT
, false, 0, 0);
215 ext
.vertexAttribDivisorANGLE(colorLoc
, 1);
217 wtu
.setupUnitQuad(gl
, 0);
219 // Draw 1: Regular drawArrays
221 debug("Testing drawArrays with non-zero divisor");
222 gl
.clear(gl
.COLOR_BUFFER_BIT
);
223 gl
.drawArrays(gl
.TRIANGLES
, 0, 6);
224 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "vertex attrib divisor should affect regular drawArrays when the extension is enabled");
225 wtu
.checkCanvasRect(gl
, 0, canvas
.height
/2, canvas.width/2, canvas
.height
/2, [255, 0, 0, 255]);
227 // Draw 2: Draw Non-indexed instances
229 debug("Testing drawArraysInstancedANGLE");
230 gl
.clear(gl
.COLOR_BUFFER_BIT
);
232 // Test drawArraysInstancedANGLE error conditions
233 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 0, 6, instanceCount
);
234 wtu
.checkCanvasRect(gl
, 0, canvas
.height
/2, canvas.width/2, canvas
.height
/2, [255, 0, 0, 255]);
235 wtu
.checkCanvasRect(gl
, canvas
.width
/2, canvas.height/2, canvas
.width
/2, canvas.height/2, [0, 255, 0, 255]);
236 wtu
.checkCanvasRect(gl
, 0, 0, canvas
.width
/2, canvas.height/2, [0, 0, 255, 255]);
237 wtu
.checkCanvasRect(gl
, canvas
.width
/2, 0, canvas.width/2, canvas
.height
/2, [255, 255, 0, 255]);
239 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 0, 6, -1);
240 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawArraysInstancedANGLE cannot have a primcount less than 0");
242 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 0, -1, instanceCount
);
243 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawArraysInstancedANGLE cannot have a count less than 0");
245 ext
.vertexAttribDivisorANGLE(positionLoc
, 1);
246 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 0, 6, instanceCount
);
247 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "There must be at least one vertex attribute with a divisor of zero when calling drawArraysInstancedANGLE");
248 ext
.vertexAttribDivisorANGLE(positionLoc
, 0);
250 ext
.drawArraysInstancedANGLE(gl
.POINTS
, 0, 6, instanceCount
);
251 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstancedANGLE with POINTS should succeed");
252 ext
.drawArraysInstancedANGLE(gl
.LINES
, 0, 6, instanceCount
);
253 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstancedANGLE with LINES should succeed");
254 ext
.drawArraysInstancedANGLE(gl
.LINE_LIST
, 0, 6, instanceCount
);
255 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstancedANGLE with LINE_LIST should return succeed");
256 ext
.drawArraysInstancedANGLE(gl
.TRIANGLE_LIST
, 0, 6, instanceCount
);
257 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstancedANGLE with TRIANGLE_LIST should succeed");
259 ext
.drawArraysInstancedANGLE(desktopGL
['QUAD_STRIP'], 0, 6, instanceCount
);
260 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawArraysInstancedANGLE with QUAD_STRIP should return INVALID_ENUM");
261 ext
.drawArraysInstancedANGLE(desktopGL
['QUADS'], 0, 6, instanceCount
);
262 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawArraysInstancedANGLE with QUADS should return INVALID_ENUM");
263 ext
.drawArraysInstancedANGLE(desktopGL
['POLYGON'], 0, 6, instanceCount
);
264 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawArraysInstancedANGLE with POLYGON should return INVALID_ENUM");
267 debug("Testing drawArraysInstancedANGLE with param 'first' > 0");
268 gl
.clear(gl
.COLOR_BUFFER_BIT
);
273 var offsetsHalf
= new Float32Array([
279 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
280 gl
.bufferData(gl
.ARRAY_BUFFER
, offsetsHalf
, gl
.STATIC_DRAW
);
282 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 3, 3, instanceCount
);
283 var w
= Math
.floor(0.25*canvas
.width
),
284 h
= Math
.floor(0.25*canvas
.height
);
285 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0.5*canvas
.height
, w
, h
, [255, 0, 0, 255]);
286 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0.5*canvas
.height
, w
, h
, [0, 255, 0, 255]);
287 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0, w
, h
, [0, 0, 255, 255]);
288 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0, w
, h
, [255, 255, 0, 255]);
291 debug("Testing drawArraysInstancedANGLE with attributes 'divisor' reset to 0");
292 debug("Correct rendering output: 4 yellow triangles");
293 debug("Possible incorrect rendering output: missing triangles, or triangles with different color at each vertex");
294 ext
.vertexAttribDivisorANGLE(colorLoc
, 0);
295 gl
.clear(gl
.COLOR_BUFFER_BIT
);
296 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 3, 3, instanceCount
);
297 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0.5*canvas
.height
, w
, h
, [255, 255, 0, 255]);
298 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0.5*canvas
.height
, w
, h
, [255, 255, 0, 255]);
299 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0, w
, h
, [255, 255, 0, 255]);
300 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0, w
, h
, [255, 255, 0, 255]);
301 ext
.vertexAttribDivisorANGLE(colorLoc
, 1);
303 wtu
.setupUnitQuad(gl
, 0);
304 wtu
.setupIndexedQuad(gl
, 1, 0);
305 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
306 gl
.bufferData(gl
.ARRAY_BUFFER
, offsets
, gl
.STATIC_DRAW
);
308 // Draw 3: Regular drawElements
310 debug("Testing drawElements with non-zero divisor");
311 gl
.clear(gl
.COLOR_BUFFER_BIT
);
312 // Point to another location in the buffer so that the draw would overflow without the divisor
313 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
314 gl
.vertexAttribPointer(colorLoc
, 4, gl
.FLOAT
, false, 0, 48);
315 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0);
316 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "vertex attrib divisor should affect regular drawElements when the extension is enabled");
317 wtu
.checkCanvasRect(gl
, 0, canvas
.height
/2, canvas.width/2, canvas
.height
/2, [255, 255, 0, 255]);
318 // Restore the vertex attrib pointer
319 gl
.vertexAttribPointer(colorLoc
, 4, gl
.FLOAT
, false, 0, 0);
321 // Draw 4: Draw indexed instances
323 debug("Testing drawElementsInstancedANGLE");
324 gl
.clear(gl
.COLOR_BUFFER_BIT
);
325 ext
.drawElementsInstancedANGLE(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
326 wtu
.checkCanvasRect(gl
, 0, canvas
.height
/2, canvas.width/2, canvas
.height
/2, [255, 0, 0, 255]);
327 wtu
.checkCanvasRect(gl
, canvas
.width
/2, canvas.height/2, canvas
.width
/2, canvas.height/2, [0, 255, 0, 255]);
328 wtu
.checkCanvasRect(gl
, 0, 0, canvas
.width
/2, canvas.height/2, [0, 0, 255, 255]);
329 wtu
.checkCanvasRect(gl
, canvas
.width
/2, 0, canvas.width/2, canvas
.height
/2, [255, 255, 0, 255]);
331 // Test drawElementsInstancedANGLE error conditions
332 ext
.drawElementsInstancedANGLE(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0, -1);
333 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawElementsInstancedANGLE cannot have a primcount less than 0");
335 ext
.drawElementsInstancedANGLE(gl
.TRIANGLES
, -1, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
336 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawElementsInstancedANGLE cannot have a count less than 0");
338 ext
.vertexAttribDivisorANGLE(positionLoc
, 1);
339 ext
.drawElementsInstancedANGLE(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
340 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "There must be at least one vertex attribute with a divisor of zero when calling drawElementsInstancedANGLE");
341 ext
.vertexAttribDivisorANGLE(positionLoc
, 0);
343 ext
.drawElementsInstancedANGLE(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0, instanceCount
);
344 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstancedANGLE with UNSIGNED_BYTE should succeed");
346 ext
.drawElementsInstancedANGLE(gl
.POINTS
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
347 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstancedANGLE with POINTS should succeed");
348 ext
.drawElementsInstancedANGLE(gl
.LINES
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
349 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstancedANGLE with LINES should succeed");
350 ext
.drawElementsInstancedANGLE(gl
.LINE_LIST
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
351 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstancedANGLE with LINE_LIST should return succeed");
352 ext
.drawElementsInstancedANGLE(gl
.TRIANGLE_LIST
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
353 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstancedANGLE with TRIANGLE_LIST should succeed");
355 ext
.drawElementsInstancedANGLE(desktopGL
['QUAD_STRIP'], 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
356 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawElementsInstancedANGLE with QUAD_STRIP should return INVALID_ENUM");
357 ext
.drawElementsInstancedANGLE(desktopGL
['QUADS'], 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
358 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawElementsInstancedANGLE with QUADS should return INVALID_ENUM");
359 ext
.drawElementsInstancedANGLE(desktopGL
['POLYGON'], 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
360 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawElementsInstancedANGLE with POLYGON should return INVALID_ENUM");
362 // Reset vertex attrib divisors so they cannot affect following subtests.
363 ext
.vertexAttribDivisorANGLE(colorLoc
, 0);
364 ext
.vertexAttribDivisorANGLE(offsetLoc
, 0);
367 function runDrawArraysTest(program
, first
, count
, instanceCount
, offset
)
369 // Get the attribute and uniform locations
370 var positionLoc
= gl
.getAttribLocation(program
, "aPosition");
371 var instancePosLoc
= gl
.getAttribLocation(program
, "aInstancePos");
372 var uniformLoc
= gl
.getUniformLocation(program
, "uOffset");
374 // Load the vertex positions
375 var positions
= new Float32Array([
392 var positionBuffer
= gl
.createBuffer();
393 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
394 gl
.bufferData(gl
.ARRAY_BUFFER
, positions
, gl
.STATIC_DRAW
);
395 gl
.enableVertexAttribArray(positionLoc
);
396 gl
.vertexAttribPointer(positionLoc
, 2, gl
.FLOAT
, false, 0, 0);
398 // Load the instance positions
399 var instancePositions
= new Float32Array([
403 var instancePositionBuffer
= gl
.createBuffer();
404 gl
.bindBuffer(gl
.ARRAY_BUFFER
, instancePositionBuffer
);
405 gl
.bufferData(gl
.ARRAY_BUFFER
, instancePositions
, gl
.STATIC_DRAW
);
406 gl
.enableVertexAttribArray(instancePosLoc
);
407 gl
.vertexAttribPointer(instancePosLoc
, 2, gl
.FLOAT
, false, 0, 0);
410 ext
.vertexAttribDivisorANGLE(instancePosLoc
, 1);
413 gl
.uniform3fv(uniformLoc
, offset
);
415 // Do the instanced draw
416 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, first
, count
, instanceCount
);
417 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstancedANGLE should succeed");
419 // Reset vertex attrib divisors so they cannot affect following subtests.
420 ext
.vertexAttribDivisorANGLE(instancePosLoc
, 0);
423 function runDrawArraysWithOffsetTest()
426 debug("Testing that the 'first' parameter to drawArraysInstancedANGLE is only an offset into the non-instanced vertex attributes.");
427 // See: http://crbug.com/457269 and http://crbug.com/447140
429 var drawArraysProgram
= wtu
.setupProgram(gl
, ["drawArraysTestVertexShader", "drawArraysTestFragmentShader"]);
431 gl
.clear(gl
.COLOR_BUFFER_BIT
);
433 runDrawArraysTest(drawArraysProgram
, 0, 6, 2, [0, 0, 0]);
435 runDrawArraysTest(drawArraysProgram
, 6, 6, 2, [-1, 1, 0]);
437 wtu
.checkCanvasRect(gl
, 0, 0, canvas
.width
, canvas
.height
, [255, 0, 0, 255]);
440 function runUniqueObjectTest()
443 debug("Testing that getExtension() returns the same object each time");
445 gl
.getExtension("ANGLE_instanced_arrays").myProperty
= 2;
446 webglHarnessCollectGarbage();
447 shouldBe('gl.getExtension("ANGLE_instanced_arrays").myProperty', '2');
450 function runVAOInstancingInteractionTest()
453 debug("Testing that ANGLE_instanced_arrays interacts correctly with OES_vertex_array_object if present");
454 // See: https://github.com/KhronosGroup/WebGL/issues/1228
456 // Query the extension and store globally so shouldBe can access it
457 vaoext
= gl
.getExtension("OES_vertex_array_object");
459 testPassed("No OES_vertex_array_object support -- this is legal");
463 testPassed("Successfully enabled OES_vertex_array_object extension");
465 gl
.useProgram(program
);
467 var positions
= new Float32Array([
468 0.0, 1.0, // Left quad
475 1.0, 1.0, // Right quad
482 var positionBuffer
= gl
.createBuffer();
483 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
484 gl
.bufferData(gl
.ARRAY_BUFFER
, positions
, gl
.STATIC_DRAW
);
486 var colors
= new Float32Array([
487 1.0, 0.0, 0.0, 1.0, // Red
494 0.0, 0.0, 1.0, 1.0, // Blue
501 var colorBuffer
= gl
.createBuffer();
502 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
503 gl
.bufferData(gl
.ARRAY_BUFFER
, colors
, gl
.STATIC_DRAW
);
505 // Reset the divisor of the default VAO to 0
506 ext
.vertexAttribDivisorANGLE(colorLoc
, 0);
508 // Set up VAO with an attrib divisor
509 var vao1
= vaoext
.createVertexArrayOES();
510 vaoext
.bindVertexArrayOES(vao1
);
512 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
513 gl
.enableVertexAttribArray(positionLoc
);
514 gl
.vertexAttribPointer(positionLoc
, 2, gl
.FLOAT
, false, 0, 0);
516 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
517 gl
.enableVertexAttribArray(colorLoc
);
518 gl
.vertexAttribPointer(colorLoc
, 4, gl
.FLOAT
, false, 0, 0);
519 ext
.vertexAttribDivisorANGLE(colorLoc
, 1);
521 gl
.vertexAttrib2fv(offsetLoc
, [0.0, 0.0]);
523 vaoext
.bindVertexArrayOES(null);
525 // Set up VAO with no attrib divisor
526 var vao2
= vaoext
.createVertexArrayOES();
527 vaoext
.bindVertexArrayOES(vao2
);
529 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
530 gl
.enableVertexAttribArray(positionLoc
);
531 gl
.vertexAttribPointer(positionLoc
, 2, gl
.FLOAT
, false, 0, 0);
533 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
534 gl
.enableVertexAttribArray(colorLoc
);
535 gl
.vertexAttribPointer(colorLoc
, 4, gl
.FLOAT
, false, 0, 0);
536 // Note that no divisor is set here, which implies that it's 0
538 gl
.vertexAttrib2fv(offsetLoc
, [0.0, 0.0]);
540 vaoext
.bindVertexArrayOES(null);
543 debug("Ensure that Vertex Array Objects retain attrib divisors");
545 vaoext
.bindVertexArrayOES(vao1
);
546 gl
.clear(gl
.COLOR_BUFFER_BIT
);
547 gl
.drawArrays(gl
.TRIANGLES
, 0, 12);
548 // If the divisor is properly managed by the VAO a single red quad will be drawn
549 wtu
.checkCanvas(gl
, [255, 0, 0, 255], "entire canvas should be red");
551 vaoext
.bindVertexArrayOES(vao2
);
552 gl
.clear(gl
.COLOR_BUFFER_BIT
);
553 gl
.drawArrays(gl
.TRIANGLES
, 0, 12);
554 // If the divisor is properly managed by the VAO a red and blue quad will be drawn.
555 wtu
.checkCanvasRects(gl
, [
556 wtu
.makeCheckRect(0, 0, canvas
.width
* 0.5, canvas
.height
, [255, 0, 0, 255], "left half of canvas should be red", 1),
557 wtu
.makeCheckRect(canvas
.width
* 0.5, 0, canvas
.width
* 0.5, canvas
.height
, [0, 0, 255, 255], "right half of canvas should be blue", 1)
560 vaoext
.bindVertexArrayOES(null);
563 async
function runANGLECorruptionTest()
566 debug("Testing to ensure that rendering isn't corrupt due to an ANGLE bug");
567 // See: https://code.google.com/p/angleproject/issues/detail?id=467
571 var tolerance
= 2; // Amount of variance to allow in result pixels - may need to be tweaked higher
572 var instanceCount
= 10; // Must be higher than 6
574 var offsets
= new Float32Array([
586 var offsetBuffer
= gl
.createBuffer();
587 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
588 gl
.bufferData(gl
.ARRAY_BUFFER
, offsets
.byteLength
* 2, gl
.STATIC_DRAW
);
589 gl
.bufferSubData(gl
.ARRAY_BUFFER
, 0, offsets
);
590 gl
.enableVertexAttribArray(offsetLoc
);
591 gl
.vertexAttribPointer(offsetLoc
, 2, gl
.FLOAT
, false, 0, 0);
592 ext
.vertexAttribDivisorANGLE(offsetLoc
, 1);
594 var colors
= new Float32Array([
606 var colorBuffer
= gl
.createBuffer();
607 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
608 gl
.bufferData(gl
.ARRAY_BUFFER
, colors
.byteLength
* 2, gl
.STATIC_DRAW
);
609 gl
.bufferSubData(gl
.ARRAY_BUFFER
, 0, colors
);
610 gl
.enableVertexAttribArray(colorLoc
);
611 gl
.vertexAttribPointer(colorLoc
, 4, gl
.FLOAT
, false, 0, 0);
612 ext
.vertexAttribDivisorANGLE(colorLoc
, 1);
614 gl
.clear(gl
.COLOR_BUFFER_BIT
);
615 wtu
.setupUnitQuad(gl
, 0);
617 const totalIterations
= 10;
618 for (let iteration
= 0; iteration
< totalIterations
; ++iteration
)
620 // Update the instanced data buffers outside the accessed range.
621 // This, plus rendering more instances than vertices, triggers the bug.
622 var nullData
= new Float32Array(offsets
.length
);
623 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
624 gl
.bufferSubData(gl
.ARRAY_BUFFER
, offsets
.byteLength
, nullData
);
626 nullData
= new Float32Array(colors
.length
);
627 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
628 gl
.bufferSubData(gl
.ARRAY_BUFFER
, colors
.byteLength
, nullData
);
630 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 0, 6, instanceCount
);
632 // Make sure each color was drawn correctly
635 for (i
= 0; i
< instanceCount
; ++i
) {
636 var w
= canvas
.width
/ instanceCount
;
638 var color
= [colors
[(i
*4)] * 255, colors
[(i
*4)+1] * 255, colors
[(i
*4)+2] * 255, 255]
640 wtu
.checkCanvasRectColor(
641 gl
, x
, 0, w
, canvas
.height
, color
, tolerance
,
649 testPassed("Passed test " + iteration
+ " of " + totalIterations
);
651 testFailed("Failed test " + iteration
+ " of " + totalIterations
);
656 ext
.vertexAttribDivisorANGLE(offsetLoc
, 0);
657 ext
.vertexAttribDivisorANGLE(colorLoc
, 0);
660 async
function runDrawTests(testFn
) {
661 function drawArrays(gl
) {
662 gl
.drawArrays(gl
.TRIANGLES
, 0, 6);
665 function drawElements(gl
) {
666 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
669 function drawArraysInstancedANGLE(gl
) {
670 const ext
= gl
.getExtension('ANGLE_instanced_arrays');
675 ext
.drawArraysInstancedANGLE(gl
.TRIANGLES
, 0, 6, 1);
678 function drawElementsInstancedANGLE(gl
) {
679 const ext
= gl
.getExtension('ANGLE_instanced_arrays');
684 ext
.drawElementsInstancedANGLE(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0, 1);
687 await
testFn(drawArrays
); // sanity check
688 await
testFn(drawElements
); // sanity check
690 await
testFn(drawArraysInstancedANGLE
);
691 await
testFn(drawElementsInstancedANGLE
);
694 async
function runCompositingTests() {
695 const compositingTestFn
= createCompositingTestFn({
699 attribute vec4 position;
701 gl_Position = position;
705 precision mediump float;
707 gl_FragColor = vec4(1, 0, 0, 1);
713 await
runDrawTests(compositingTestFn
);
716 async
function runInvalidAttribTests(gl
) {
717 const invalidAttribTestFn
= createInvalidAttribTestFn(gl
);
718 await
runDrawTests(invalidAttribTestFn
);