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 vertex_array_object Conformance Tests
</title>
12 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
13 <script src=
"../../js/js-test-pre.js"></script>
14 <script src=
"../../js/webgl-test-utils.js"></script>
17 <div id=
"description"></div>
18 <canvas id=
"canvas" style=
"width: 50px; height: 50px;"> </canvas>
19 <div id=
"console"></div>
20 <script id=
"vshader" type=
"x-shader/x-vertex">
21 attribute vec4 a_position;
22 attribute vec4 a_color;
25 gl_Position = a_position;
29 <script id=
"fshader" type=
"x-shader/x-fragment">
30 precision mediump float;
33 gl_FragColor = v_color;
38 description("This test verifies the functionality of the Vertex Array Objects.");
42 var wtu
= WebGLTestUtils
;
43 var canvas
= document
.getElementById("canvas");
44 var gl
= wtu
.create3DContext(canvas
, null, 2);
48 testFailed("WebGL context does not exist");
50 testPassed("WebGL context exists");
55 runAttributeValueTests();
57 runUnboundDeleteTests();
58 runBoundDeleteTests();
59 runArrayBufferBindTests();
60 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
63 function runBindingTest() {
64 debug("Testing binding enum");
66 shouldBe("gl.VERTEX_ARRAY_BINDING", "0x85B5");
68 gl
.getParameter(gl
.VERTEX_ARRAY_BINDING
);
69 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "VERTEX_ARRAY_BINDING query should succeed");
71 // Default value is null
72 if (gl
.getParameter(gl
.VERTEX_ARRAY_BINDING
) === null) {
73 testPassed("Default value of VERTEX_ARRAY_BINDING is null");
75 testFailed("Default value of VERTEX_ARRAY_BINDING is not null");
78 debug("Testing binding a VAO");
79 var vao0
= gl
.createVertexArray();
80 var vao1
= gl
.createVertexArray();
81 shouldBeNull("gl.getParameter(gl.VERTEX_ARRAY_BINDING)");
82 gl
.bindVertexArray(vao0
);
83 if (gl
.getParameter(gl
.VERTEX_ARRAY_BINDING
) == vao0
) {
84 testPassed("gl.getParameter(gl.VERTEX_ARRAY_BINDING) is expected VAO");
86 testFailed("gl.getParameter(gl.VERTEX_ARRAY_BINDING) is not expected VAO")
88 gl
.bindVertexArray(vao1
);
89 if (gl
.getParameter(gl
.VERTEX_ARRAY_BINDING
) == vao1
) {
90 testPassed("gl.getParameter(gl.VERTEX_ARRAY_BINDING) is expected VAO");
92 testFailed("gl.getParameter(gl.VERTEX_ARRAY_BINDING) is not expected VAO")
94 gl
.deleteVertexArray(vao1
);
95 shouldBeNull("gl.getParameter(gl.VERTEX_ARRAY_BINDING)");
96 gl
.bindVertexArray(vao1
);
97 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "binding a deleted vertex array object");
98 gl
.bindVertexArray(null);
99 shouldBeNull("gl.getParameter(gl.VERTEX_ARRAY_BINDING)");
100 gl
.deleteVertexArray(vao1
);
103 function runObjectTest() {
104 debug("Testing object creation");
106 vao
= gl
.createVertexArray();
107 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "createVertexArray should not set an error");
108 shouldBeNonNull("vao");
110 // Expect false if never bound
111 shouldBeFalse("gl.isVertexArray(vao)");
112 gl
.bindVertexArray(vao
);
113 shouldBeTrue("gl.isVertexArray(vao)");
114 gl
.bindVertexArray(null);
115 shouldBeTrue("gl.isVertexArray(vao)");
117 shouldBeFalse("gl.isVertexArray(null)");
119 gl
.deleteVertexArray(vao
);
123 function runAttributeTests() {
124 debug("Testing attributes work across bindings");
128 var attrCount
= gl
.getParameter(gl
.MAX_VERTEX_ATTRIBS
);
129 for (var n
= 0; n
< attrCount
; n
++) {
130 gl
.bindBuffer(gl
.ARRAY_BUFFER
, null);
131 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, null);
136 var vao
= state
.vao
= gl
.createVertexArray();
137 gl
.bindVertexArray(vao
);
139 var enableArray
= (n
% 2 == 0);
141 gl
.enableVertexAttribArray(n
);
143 gl
.disableVertexAttribArray(n
);
147 var buffer
= state
.buffer
= gl
.createBuffer();
148 gl
.bindBuffer(gl
.ARRAY_BUFFER
, buffer
);
149 gl
.bufferData(gl
.ARRAY_BUFFER
, 1024, gl
.STATIC_DRAW
);
151 gl
.vertexAttribPointer(n
, 1 + n
% 4, gl
.FLOAT
, true, n
* 4, n
* 4);
155 var elbuffer
= state
.elbuffer
= gl
.createBuffer();
156 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elbuffer
);
157 gl
.bufferData(gl
.ELEMENT_ARRAY_BUFFER
, 1024, gl
.STATIC_DRAW
);
160 gl
.bindVertexArray(null);
163 var anyMismatch
= false;
164 for (var n
= 0; n
< attrCount
; n
++) {
165 var state
= states
[n
];
167 gl
.bindVertexArray(state
.vao
);
169 var shouldBeEnabled
= (n
% 2 == 0);
170 var isEnabled
= gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_ENABLED
);
171 if (shouldBeEnabled
!= isEnabled
) {
172 testFailed("VERTEX_ATTRIB_ARRAY_ENABLED not preserved");
176 var buffer
= gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
);
177 if (shouldBeEnabled
) {
178 if (buffer
== state
.buffer
) {
180 if ((gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_SIZE
) == 1 + n
% 4) &&
181 (gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_TYPE
) == gl
.FLOAT
) &&
182 (gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_NORMALIZED
) == true) &&
183 (gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_STRIDE
) == n
* 4) &&
184 (gl
.getVertexAttribOffset(n
, gl
.VERTEX_ATTRIB_ARRAY_POINTER
) == n
* 4)) {
187 testFailed("VERTEX_ATTRIB_ARRAY_* not preserved");
191 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
195 // GL_CURRENT_VERTEX_ATTRIB is not preserved
197 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
202 var elbuffer
= gl
.getParameter(gl
.ELEMENT_ARRAY_BUFFER_BINDING
);
203 if (shouldBeEnabled
) {
204 if (elbuffer
== state
.elbuffer
) {
207 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
211 if (elbuffer
== null) {
214 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
219 gl
.bindVertexArray(null);
221 testPassed("All attributes preserved across bindings");
224 for (var n
= 0; n
< attrCount
; n
++) {
225 var state
= states
[n
];
226 gl
.deleteVertexArray(state
.vao
);
230 function runAttributeValueTests() {
231 debug("Testing that attribute values are not attached to bindings");
234 var vao0
= gl
.createVertexArray();
235 var anyFailed
= false;
237 gl
.bindVertexArray(null);
238 gl
.vertexAttrib4f(0, 0, 1, 2, 3);
240 v
= gl
.getVertexAttrib(0, gl
.CURRENT_VERTEX_ATTRIB
);
241 if (!(v
[0] == 0 && v
[1] == 1 && v
[2] == 2 && v
[3] == 3)) {
242 testFailed("Vertex attrib value not round-tripped?");
246 gl
.bindVertexArray(vao0
);
248 v
= gl
.getVertexAttrib(0, gl
.CURRENT_VERTEX_ATTRIB
);
249 if (!(v
[0] == 0 && v
[1] == 1 && v
[2] == 2 && v
[3] == 3)) {
250 testFailed("Vertex attrib value reset across bindings");
254 gl
.vertexAttrib4f(0, 4, 5, 6, 7);
255 gl
.bindVertexArray(null);
257 v
= gl
.getVertexAttrib(0, gl
.CURRENT_VERTEX_ATTRIB
);
258 if (!(v
[0] == 4 && v
[1] == 5 && v
[2] == 6 && v
[3] == 7)) {
259 testFailed("Vertex attrib value bound to buffer");
264 testPassed("Vertex attribute values are not attached to bindings")
267 gl
.bindVertexArray(null);
268 gl
.deleteVertexArray(vao0
);
271 function runDrawTests() {
272 debug("Testing draws with various VAO bindings");
274 canvas
.width
= 50; canvas
.height
= 50;
275 gl
.viewport(0, 0, canvas
.width
, canvas
.height
);
277 var vao0
= gl
.createVertexArray();
278 var vao1
= gl
.createVertexArray();
280 var opt_positionLocation
= 0;
281 var opt_texcoordLocation
= 1;
283 var program
= wtu
.setupSimpleTextureProgram(gl
, opt_positionLocation
, opt_texcoordLocation
);
285 function setupQuad(s
) {
286 var vertexObject
= gl
.createBuffer();
287 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertexObject
);
288 gl
.bufferData(gl
.ARRAY_BUFFER
, new Float32Array([
289 1.0 * s
, 1.0 * s
, 0.0,
290 -1.0 * s
, 1.0 * s
, 0.0,
291 -1.0 * s
, -1.0 * s
, 0.0,
292 1.0 * s
, 1.0 * s
, 0.0,
293 -1.0 * s
, -1.0 * s
, 0.0,
294 1.0 * s
, -1.0 * s
, 0.0]), gl
.STATIC_DRAW
);
295 gl
.enableVertexAttribArray(opt_positionLocation
);
296 gl
.vertexAttribPointer(opt_positionLocation
, 3, gl
.FLOAT
, false, 0, 0);
298 var vertexObject
= gl
.createBuffer();
299 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertexObject
);
300 gl
.bufferData(gl
.ARRAY_BUFFER
, new Float32Array([
306 1.0 * s
, 0.0 * s
]), gl
.STATIC_DRAW
);
307 gl
.enableVertexAttribArray(opt_texcoordLocation
);
308 gl
.vertexAttribPointer(opt_texcoordLocation
, 2, gl
.FLOAT
, false, 0, 0);
311 function readLocation(x
, y
) {
312 var pixels
= new Uint8Array(1 * 1 * 4);
313 gl
.readPixels(x
, y
, 1, 1, gl
.RGBA
, gl
.UNSIGNED_BYTE
, pixels
);
316 function testPixel(blockList
, allowList
) {
317 function testList(list
, expected
) {
318 for (var n
= 0; n
< list
.length
; n
++) {
320 var x
= -Math
.floor(l
* canvas
.width
/ 2) + canvas
.width
/ 2;
321 var y
= -Math
.floor(l
* canvas
.height
/ 2) + canvas
.height
/ 2;
322 var source
= readLocation(x
, y
);
323 if (Math
.abs(source
[0] - expected
) > 2) {
329 return testList(blockList
, 0) && testList(allowList
, 255);
331 function verifyDraw(drawNumber
, s
) {
332 wtu
.clearAndDrawUnitQuad(gl
);
335 var points
= [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
336 for (var n
= 0; n
< points
.length
; n
++) {
337 if (points
[n
] <= s
) {
338 blockList
.push(points
[n
]);
340 allowList
.push(points
[n
]);
343 if (testPixel(blockList
, allowList
)) {
344 testPassed("Draw " + drawNumber
+ " passed pixel test");
346 testFailed("Draw " + drawNumber
+ " failed pixel test");
350 // Setup all bindings
352 gl
.bindVertexArray(vao0
);
354 gl
.bindVertexArray(vao1
);
358 gl
.bindVertexArray(null);
360 gl
.bindVertexArray(vao0
);
362 gl
.bindVertexArray(vao1
);
365 gl
.bindVertexArray(null);
366 gl
.deleteVertexArray(vao0
);
367 gl
.deleteVertexArray(vao1
);
369 // Disable global vertex attrib array
370 gl
.disableVertexAttribArray(opt_positionLocation
);
371 gl
.disableVertexAttribArray(opt_texcoordLocation
);
377 wtu
.setupIndexedQuad(gl
, gridRes
, positionLoc
);
378 // Set the vertex color to red.
379 gl
.vertexAttrib4f(colorLoc
, 1, 0, 0, 1);
381 var vao0
= gl
.createVertexArray();
382 gl
.bindVertexArray(vao0
);
383 var program
= wtu
.setupSimpleVertexColorProgram(gl
, positionLoc
, colorLoc
);
384 wtu
.setupIndexedQuad(gl
, gridRes
, positionLoc
);
385 // Set the vertex color to green.
386 gl
.vertexAttrib4f(colorLoc
, 0, 1, 0, 1);
387 wtu
.clearAndDrawIndexedQuad(gl
, gridRes
);
388 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green")
389 gl
.deleteVertexArray(vao0
);
390 wtu
.clearAndDrawIndexedQuad(gl
, gridRes
);
391 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green")
394 function runUnboundDeleteTests() {
395 debug("Testing using buffers that are deleted when attached to unbound VAOs");
397 var program
= wtu
.setupProgram(gl
, ["vshader", "fshader"], ["a_position", "a_color"]);
398 gl
.useProgram(program
);
400 var positionBuffer
= gl
.createBuffer();
401 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
417 var colorBuffers
= [];
418 var elementBuffers
= [];
420 for (var ii
= 0; ii
< colors
.length
; ++ii
) {
421 var vao
= gl
.createVertexArray();
423 gl
.bindVertexArray(vao
);
424 // Set the position buffer
425 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
426 gl
.enableVertexAttribArray(0);
427 gl
.vertexAttribPointer(0, 2, gl
.FLOAT
, false, 0, 0);
429 var elementBuffer
= gl
.createBuffer();
430 elementBuffers
.push(elementBuffer
);
431 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementBuffer
);
433 gl
.ELEMENT_ARRAY_BUFFER
,
434 new Uint8Array([0, 1, 2, 0, 2, 3]),
437 // Setup the color attrib
438 var color
= colors
[ii
];
440 var colorBuffer
= gl
.createBuffer();
441 colorBuffers
.push(colorBuffer
);
442 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
443 gl
.bufferData(gl
.ARRAY_BUFFER
, new Uint8Array(
444 [ color
[0], color
[1], color
[2], color
[3],
445 color
[0], color
[1], color
[2], color
[3],
446 color
[0], color
[1], color
[2], color
[3],
447 color
[0], color
[1], color
[2], color
[3]
449 gl
.enableVertexAttribArray(1);
450 gl
.vertexAttribPointer(1, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
452 gl
.vertexAttrib4f(1, color
[0] / 255, color
[1] / 255, color
[2] / 255, color
[3] / 255);
456 // delete the color buffers AND the position buffer.
457 gl
.bindVertexArray(null);
458 for (var ii
= 0; ii
< colorBuffers
.length
; ++ii
) {
459 gl
.deleteBuffer(colorBuffers
[ii
]);
460 gl
.deleteBuffer(elementBuffers
[ii
]);
461 gl
.bindVertexArray(vaos
[ii
]);
462 var boundBuffer
= gl
.getVertexAttrib(1, gl
.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
);
463 // The buffers should still be valid at this point, since it was attached to the VAO
464 if(boundBuffer
!= colorBuffers
[ii
]) {
465 testFailed("buffer removed too early");
468 gl
.bindVertexArray(null);
469 gl
.deleteBuffer(positionBuffer
);
471 // Render with the deleted buffers. As they are referenced by VAOs they
472 // must still be around.
473 for (var ii
= 0; ii
< colors
.length
; ++ii
) {
474 var color
= colors
[ii
];
475 gl
.bindVertexArray(vaos
[ii
]);
476 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
477 wtu
.checkCanvas(gl
, color
, "should be " + color
);
481 for (var ii
= 0; ii
< colorBuffers
.length
; ++ii
) {
482 gl
.deleteVertexArray(vaos
[ii
]);
485 for (var ii
= 0; ii
< colorBuffers
.length
; ++ii
) {
486 // The buffers should no longer be valid now that the VAOs are deleted
487 if(gl
.isBuffer(colorBuffers
[ii
])) {
488 testFailed("buffer not properly cleaned up after VAO deletion");
493 function runBoundDeleteTests() {
494 debug("Testing using buffers that are deleted when attached to bound VAOs");
496 var program
= wtu
.setupProgram(gl
, ["vshader", "fshader"], ["a_position", "a_color"]);
497 gl
.useProgram(program
);
499 var positionBuffer
= gl
.createBuffer();
500 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
510 // Setup the color attrib
511 var colorBuffer
= gl
.createBuffer();
512 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
513 gl
.bufferData(gl
.ARRAY_BUFFER
, new Uint8Array(
521 var elementBuffers
= [];
522 for (var ii
= 0; ii
< 4; ++ii
) {
523 var vao
= gl
.createVertexArray();
525 gl
.bindVertexArray(vao
);
527 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
528 gl
.enableVertexAttribArray(0);
529 gl
.vertexAttribPointer(0, 2, gl
.FLOAT
, false, 0, 0);
531 var elementBuffer
= gl
.createBuffer();
532 elementBuffers
.push(elementBuffer
);
533 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementBuffer
);
535 gl
.ELEMENT_ARRAY_BUFFER
,
536 new Uint8Array([0, 1, 2, 0, 2, 3]),
539 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
540 gl
.enableVertexAttribArray(1);
541 gl
.vertexAttribPointer(1, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
544 // delete the color buffers AND the position buffer, that are bound to the current VAO
545 for (var ii
= 0; ii
< vaos
.length
; ++ii
) {
546 gl
.bindVertexArray(vaos
[ii
]);
548 gl
.deleteBuffer(colorBuffer
);
549 gl
.deleteBuffer(positionBuffer
);
551 // After the first iteration, deleteBuffer will be a no-op, and will not unbind its matching
552 // bind points on the now-bound VAO like it did on the first iteration.
553 var expectRetained
= (ii
!= 0);
554 var shouldBeStr
= (expectRetained
? "retained" : "cleared");
556 var boundPositionBuffer
= gl
.getVertexAttrib(0, gl
.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
);
557 if (expectRetained
!= (boundPositionBuffer
== positionBuffer
)) {
558 testFailed("Position attrib stored buffer should be " + shouldBeStr
+ ".");
561 var boundColorBuffer
= gl
.getVertexAttrib(1, gl
.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
);
562 if (expectRetained
!= (boundColorBuffer
== colorBuffer
)) {
563 testFailed("Color attrib stored buffer should be " + shouldBeStr
+ ".");
566 // If retained, everything should still work. If cleared, drawing should now fail.
567 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
568 var expectedError
= (expectRetained
? gl
.NO_ERROR
: gl
.INVALID_OPERATION
);
569 wtu
.glErrorShouldBe(gl
, expectedError
,
570 "Draw call should " + (expectRetained
? "not " : "") + "fail.");
572 if (gl
.isBuffer(positionBuffer
)) {
573 testFailed("References from unbound VAOs don't keep Position buffer alive.");
575 if (gl
.isBuffer(colorBuffer
)) {
576 testFailed("References from unbound VAOs don't keep Color buffer alive");
581 function runArrayBufferBindTests() {
582 debug("Testing that VAOs don't effect ARRAY_BUFFER binding.");
584 gl
.bindVertexArray(null);
586 var program
= wtu
.setupProgram(gl
, ["vshader", "fshader"], ["a_color", "a_position"]);
587 gl
.useProgram(program
);
589 // create shared element buuffer
590 var elementBuffer
= gl
.createBuffer();
592 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementBuffer
);
594 gl
.ELEMENT_ARRAY_BUFFER
,
595 new Uint8Array([0, 1, 2, 0, 2, 3]),
598 // first create the buffers for no vao draw.
599 var nonVAOColorBuffer
= gl
.createBuffer();
600 gl
.bindBuffer(gl
.ARRAY_BUFFER
, nonVAOColorBuffer
);
601 gl
.bufferData(gl
.ARRAY_BUFFER
, new Uint8Array(
608 // shared position buffer.
609 var positionBuffer
= gl
.createBuffer();
610 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
620 // attach position buffer to default
621 gl
.enableVertexAttribArray(1);
622 gl
.vertexAttribPointer(1, 2, gl
.FLOAT
, false, 0, 0);
625 var vao
= gl
.createVertexArray();
626 gl
.bindVertexArray(vao
);
628 // attach the position buffer vao
629 gl
.enableVertexAttribArray(1);
630 gl
.vertexAttribPointer(1, 2, gl
.FLOAT
, false, 0, 0);
632 var vaoColorBuffer
= gl
.createBuffer();
633 gl
.enableVertexAttribArray(0);
634 gl
.vertexAttribPointer(0, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
635 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vaoColorBuffer
);
636 gl
.bufferData(gl
.ARRAY_BUFFER
, new Uint8Array(
642 gl
.enableVertexAttribArray(0);
643 gl
.vertexAttribPointer(0, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
645 // now set the buffer back to the nonVAOColorBuffer
646 gl
.bindBuffer(gl
.ARRAY_BUFFER
, nonVAOColorBuffer
);
649 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementBuffer
);
650 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
651 wtu
.checkCanvas(gl
, [255, 0, 0, 255], "should be red");
654 gl
.bindVertexArray(null);
656 // At this point the nonVAOColorBuffer should be still be bound.
657 // If the WebGL impl is emulating VAOs it must make sure
658 // it correctly restores this binding.
659 gl
.enableVertexAttribArray(0);
660 gl
.vertexAttribPointer(0, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
661 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
662 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green");
666 var successfullyParsed
= true;
668 <script src=
"../../js/js-test-post.js"></script>