4 ** Copyright (c) 2012 The Khronos Group Inc.
6 ** Permission is hereby granted, free of charge, to any person obtaining a
7 ** copy of this software and/or associated documentation files (the
8 ** "Materials"), to deal in the Materials without restriction, including
9 ** without limitation the rights to use, copy, modify, merge, publish,
10 ** distribute, sublicense, and/or sell copies of the Materials, and to
11 ** permit persons to whom the Materials are furnished to do so, subject to
12 ** the following conditions:
14 ** The above copyright notice and this permission notice shall be included
15 ** in all copies or substantial portions of the Materials.
17 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
31 <meta charset="utf-8">
32 <title>WebGL OES_vertex_array_object Conformance Tests</title>
33 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
34 <script src="../../resources/js-test-pre.js"></script>
35 <script src="../resources/webgl-test-utils.js"></script>
36 <!-- comment in the script tag below to test through JS emulation of the extension. -->
38 <script src="../../../demos/google/resources/OESVertexArrayObject.js"></script>
42 <div id="description"></div>
43 <canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
44 <div id="console"></div>
45 <script id="vshader" type="x-shader/x-vertex">
46 attribute vec4 a_position;
47 attribute vec4 a_color;
50 gl_Position = a_position;
54 <script id="fshader" type="x-shader/x-fragment">
55 precision mediump float;
58 gl_FragColor = v_color;
63 description("This test verifies the functionality of the OES_vertex_array_object extension, if it is available.");
67 var wtu = WebGLTestUtils;
68 var canvas = document.getElementById("canvas");
69 var gl = wtu.create3DContext(canvas);
74 testFailed("WebGL context does not exist");
76 testPassed("WebGL context exists");
78 // Setup emulated OESVertexArrayObject if it has been included.
79 if (window.setupVertexArrayObject) {
80 debug("using emulated OES_vertex_array_object");
81 setupVertexArrayObject(gl);
84 // Run tests with extension disabled
85 runBindingTestDisabled();
87 // Query the extension and store globally so shouldBe can access it
88 ext = gl.getExtension("OES_vertex_array_object");
90 testPassed("No OES_vertex_array_object support -- this is legal");
92 runSupportedTest(false);
94 testPassed("Successfully enabled OES_vertex_array_object extension");
96 runSupportedTest(true);
97 runBindingTestEnabled();
100 runAttributeValueTests();
103 runArrayBufferBindTests();
104 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
108 function runSupportedTest(extensionEnabled) {
109 var supported = gl.getSupportedExtensions();
110 if (supported.indexOf("OES_vertex_array_object") >= 0) {
111 if (extensionEnabled) {
112 testPassed("OES_vertex_array_object listed as supported and getExtension succeeded");
114 testFailed("OES_vertex_array_object listed as supported but getExtension failed");
117 if (extensionEnabled) {
118 testFailed("OES_vertex_array_object not listed as supported but getExtension succeeded");
120 testPassed("OES_vertex_array_object not listed as supported and getExtension failed -- this is legal");
125 function runBindingTestDisabled() {
127 debug("Testing binding enum with extension disabled");
129 // Use the constant directly as we don't have the extension
130 var VERTEX_ARRAY_BINDING_OES = 0x85B5;
132 gl.getParameter(VERTEX_ARRAY_BINDING_OES);
133 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "VERTEX_ARRAY_BINDING_OES should not be queryable if extension is disabled");
136 function runBindingTestEnabled() {
138 debug("Testing binding enum with extension enabled");
140 shouldBe("ext.VERTEX_ARRAY_BINDING_OES", "0x85B5");
142 gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES);
143 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "VERTEX_ARRAY_BINDING_OES query should succeed if extension is enabled");
145 // Default value is null
146 if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) === null) {
147 testPassed("Default value of VERTEX_ARRAY_BINDING_OES is null");
149 testFailed("Default value of VERTEX_ARRAY_BINDING_OES is not null");
153 debug("Testing binding a VAO");
154 var vao0 = ext.createVertexArrayOES();
155 var vao1 = ext.createVertexArrayOES();
156 shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
157 ext.bindVertexArrayOES(vao0);
158 if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) == vao0) {
159 testPassed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is expected VAO");
161 testFailed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is not expected VAO")
163 ext.bindVertexArrayOES(vao1);
164 if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) == vao1) {
165 testPassed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is expected VAO");
167 testFailed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is not expected VAO")
169 ext.deleteVertexArrayOES(vao1);
170 shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
171 ext.bindVertexArrayOES(vao1);
172 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "binding a deleted vertex array object");
173 ext.bindVertexArrayOES(null);
174 shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
175 ext.deleteVertexArrayOES(vao1);
178 function runObjectTest() {
180 debug("Testing object creation");
182 vao = ext.createVertexArrayOES();
183 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "createVertexArrayOES should not set an error");
184 shouldBeNonNull("vao");
186 // Expect false if never bound
187 shouldBeFalse("ext.isVertexArrayOES(vao)");
188 ext.bindVertexArrayOES(vao);
189 shouldBeTrue("ext.isVertexArrayOES(vao)");
190 ext.bindVertexArrayOES(null);
191 shouldBeTrue("ext.isVertexArrayOES(vao)");
193 shouldBeFalse("ext.isVertexArrayOES(null)");
195 ext.deleteVertexArrayOES(vao);
199 function runAttributeTests() {
201 debug("Testing attributes work across bindings");
205 var attrCount = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
206 for (var n = 0; n < attrCount; n++) {
207 gl.bindBuffer(gl.ARRAY_BUFFER, null);
208 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
213 var vao = state.vao = ext.createVertexArrayOES();
214 ext.bindVertexArrayOES(vao);
216 var enableArray = (n % 2 == 0);
218 gl.enableVertexAttribArray(n);
220 gl.disableVertexAttribArray(n);
224 var buffer = state.buffer = gl.createBuffer();
225 gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
226 gl.bufferData(gl.ARRAY_BUFFER, 1024, gl.STATIC_DRAW);
228 gl.vertexAttribPointer(n, 1 + n % 4, gl.FLOAT, true, n * 4, n * 4);
232 var elbuffer = state.elbuffer = gl.createBuffer();
233 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elbuffer);
234 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 1024, gl.STATIC_DRAW);
237 ext.bindVertexArrayOES(null);
240 var anyMismatch = false;
241 for (var n = 0; n < attrCount; n++) {
242 var state = states[n];
244 ext.bindVertexArrayOES(state.vao);
246 var shouldBeEnabled = (n % 2 == 0);
247 var isEnabled = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_ENABLED);
248 if (shouldBeEnabled != isEnabled) {
249 testFailed("VERTEX_ATTRIB_ARRAY_ENABLED not preserved");
253 var buffer = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
254 if (shouldBeEnabled) {
255 if (buffer == state.buffer) {
257 if ((gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_SIZE) == 1 + n % 4) &&
258 (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_TYPE) == gl.FLOAT) &&
259 (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) == true) &&
260 (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_STRIDE) == n * 4) &&
261 (gl.getVertexAttribOffset(n, gl.VERTEX_ATTRIB_ARRAY_POINTER) == n * 4)) {
264 testFailed("VERTEX_ATTRIB_ARRAY_* not preserved");
268 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
272 // GL_CURRENT_VERTEX_ATTRIB is not preserved
274 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
279 var elbuffer = gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING);
280 if (shouldBeEnabled) {
281 if (elbuffer == state.elbuffer) {
284 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
288 if (elbuffer == null) {
291 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
296 ext.bindVertexArrayOES(null);
298 testPassed("All attributes preserved across bindings");
301 for (var n = 0; n < attrCount; n++) {
302 var state = states[n];
303 ext.deleteVertexArrayOES(state.vao);
307 function runAttributeValueTests() {
309 debug("Testing that attribute values are not attached to bindings");
312 var vao0 = ext.createVertexArrayOES();
313 var anyFailed = false;
315 ext.bindVertexArrayOES(null);
316 gl.vertexAttrib4f(0, 0, 1, 2, 3);
318 v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
319 if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) {
320 testFailed("Vertex attrib value not round-tripped?");
324 ext.bindVertexArrayOES(vao0);
326 v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
327 if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) {
328 testFailed("Vertex attrib value reset across bindings");
332 gl.vertexAttrib4f(0, 4, 5, 6, 7);
333 ext.bindVertexArrayOES(null);
335 v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB);
336 if (!(v[0] == 4 && v[1] == 5 && v[2] == 6 && v[3] == 7)) {
337 testFailed("Vertex attrib value bound to buffer");
342 testPassed("Vertex attribute values are not attached to bindings")
345 ext.bindVertexArrayOES(null);
346 ext.deleteVertexArrayOES(vao0);
349 function runDrawTests() {
351 debug("Testing draws with various VAO bindings");
353 canvas.width = 50; canvas.height = 50;
354 gl.viewport(0, 0, canvas.width, canvas.height);
356 var vao0 = ext.createVertexArrayOES();
357 var vao1 = ext.createVertexArrayOES();
358 var vao2 = ext.createVertexArrayOES();
360 var positionLocation = 0;
361 var colorLocation = 1;
363 var program = wtu.setupSimpleVertexColorProgram(gl, positionLocation, colorLocation);
365 function setupQuad(s, colorsInArray) {
366 var vertexObject = gl.createBuffer();
367 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
368 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
369 1.0 * s, 1.0 * s, 0.0,
370 -1.0 * s, 1.0 * s, 0.0,
371 -1.0 * s, -1.0 * s, 0.0,
372 1.0 * s, 1.0 * s, 0.0,
373 -1.0 * s, -1.0 * s, 0.0,
374 1.0 * s, -1.0 * s, 0.0]), gl.STATIC_DRAW);
375 gl.enableVertexAttribArray(positionLocation);
376 gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);
378 // Test switching between VAOs that have different number of enabled arrays
380 var vertexObject = gl.createBuffer();
381 gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject);
382 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
388 0.0, 0.0, 0.0, 1.0]), gl.STATIC_DRAW);
389 gl.enableVertexAttribArray(colorLocation);
390 gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0);
392 gl.disableVertexAttribArray(colorLocation);
396 function verifyDiagonalPixels(s, expectedInside, drawDescription) {
397 // Tests pixels along a diagonal running from the center of the canvas to the (0, 0) corner.
398 // Values on the points list indicate relative position along this diagonal.
399 var points = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
400 for (var n = 0; n < points.length; n++) {
401 var expected = points[n] <= s ? expectedInside : 255;
402 var x = Math.round((1 - points[n]) * canvas.width / 2);
403 var y = Math.round((1 - points[n]) * canvas.height / 2);
404 wtu.checkCanvasRect(gl, x, y, 1, 1, [expected, expected, expected, 255],
405 "Drawing " + drawDescription + " should pass", 2);
408 function verifyDraw(drawDescription, s, colorsInArray) {
409 wtu.clearAndDrawUnitQuad(gl);
410 var expectedInside = colorsInArray ? 0 : 128;
411 verifyDiagonalPixels(s, expectedInside, drawDescription);
414 // Setup all bindings
416 ext.bindVertexArrayOES(vao0);
417 setupQuad(0.5, true);
418 ext.bindVertexArrayOES(vao1);
419 setupQuad(0.25, true);
420 ext.bindVertexArrayOES(vao2);
421 setupQuad(0.75, false);
423 gl.vertexAttrib4f(colorLocation, 0.5, 0.5, 0.5, 1);
426 ext.bindVertexArrayOES(null);
427 verifyDraw("with the default VAO", 1, true);
428 ext.bindVertexArrayOES(vao0);
429 verifyDraw("with VAO #0", 0.5, true);
430 ext.bindVertexArrayOES(vao1);
431 verifyDraw("with VAO #1", 0.25, true);
432 ext.bindVertexArrayOES(vao2);
433 verifyDraw("with VAO that has the color array disabled", 0.75, false);
435 // Verify bound VAO after delete
436 ext.bindVertexArrayOES(vao1);
437 ext.deleteVertexArrayOES(vao0);
438 verifyDraw("after deleting another VAO", 0.25, true);
439 ext.deleteVertexArrayOES(vao1);
440 verifyDraw("after deleting the VAO that was bound", 1, true);
442 // Disable global vertex attrib array
443 gl.disableVertexAttribArray(positionLocation);
444 gl.disableVertexAttribArray(colorLocation);
446 // Check that constant values are treated correctly as not being part of VAO state.
450 wtu.setupIndexedQuad(gl, gridRes, positionLoc);
451 // Set the vertex color to red.
452 gl.vertexAttrib4f(colorLoc, 1, 0, 0, 1);
454 var vao0 = ext.createVertexArrayOES();
455 ext.bindVertexArrayOES(vao0);
456 var program = wtu.setupSimpleVertexColorProgram(gl, positionLoc, colorLoc);
457 wtu.setupIndexedQuad(gl, gridRes, positionLoc);
458 // Set the vertex color to green.
459 gl.vertexAttrib4f(colorLoc, 0, 1, 0, 1);
460 wtu.clearAndDrawIndexedQuad(gl, gridRes);
461 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
462 ext.deleteVertexArrayOES(vao0);
463 wtu.clearAndDrawIndexedQuad(gl, gridRes);
464 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
467 function runDeleteTests() {
469 debug("Testing using deleted buffers referenced by VAOs");
471 var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["a_position", "a_color"]);
472 gl.useProgram(program);
474 var positionBuffer = gl.createBuffer();
475 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
491 var colorBuffers = [];
492 var elementBuffers = [];
494 for (var ii = 0; ii < colors.length; ++ii) {
495 var vao = ext.createVertexArrayOES();
497 ext.bindVertexArrayOES(vao);
498 // Set the position buffer
499 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
500 gl.enableVertexAttribArray(0);
501 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
503 var elementBuffer = gl.createBuffer();
504 elementBuffers.push(elementBuffer);
505 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
507 gl.ELEMENT_ARRAY_BUFFER,
508 new Uint8Array([0, 1, 2, 0, 2, 3]),
511 // Setup the color attrib
512 var color = colors[ii];
514 var colorBuffer = gl.createBuffer();
515 colorBuffers.push(colorBuffer);
516 gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
517 gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(
518 [ color[0], color[1], color[2], color[3],
519 color[0], color[1], color[2], color[3],
520 color[0], color[1], color[2], color[3],
521 color[0], color[1], color[2], color[3]
523 gl.enableVertexAttribArray(1);
524 gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0);
526 gl.vertexAttrib4f(1, color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255);
530 // delete the color buffers AND the position buffer.
531 ext.bindVertexArrayOES(null);
532 for (var ii = 0; ii < colorBuffers.length; ++ii) {
533 gl.deleteBuffer(colorBuffers[ii]);
534 gl.deleteBuffer(elementBuffers[ii]);
535 // The buffers should still be valid at this point, since it was attached to the VAO
536 if(!gl.isBuffer(colorBuffers[ii])) {
537 testFailed("buffer removed even though it is still attached to a VAO");
540 gl.deleteBuffer(positionBuffer);
542 // Render with the deleted buffers. As they are referenced by VAOs they
543 // must still be around.
544 for (var ii = 0; ii < colors.length; ++ii) {
545 var color = colors[ii];
546 ext.bindVertexArrayOES(vaos[ii]);
547 gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
548 wtu.checkCanvas(gl, color, "should be " + color);
552 for (var ii = 0; ii < colorBuffers.length; ++ii) {
553 ext.deleteVertexArrayOES(vaos[ii]);
556 for (var ii = 0; ii < colorBuffers.length; ++ii) {
557 // The buffers should no longer be valid now that the VAOs are deleted
558 if(gl.isBuffer(colorBuffers[ii])) {
559 testFailed("buffer not properly cleaned up after VAO deletion");
564 function runArrayBufferBindTests() {
566 debug("Testing that buffer bindings on VAOs don't affect default VAO ARRAY_BUFFER binding.");
568 ext.bindVertexArrayOES(null);
570 var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["a_color", "a_position"]);
571 gl.useProgram(program);
573 // create shared element buffer
574 var elementBuffer = gl.createBuffer();
576 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
578 gl.ELEMENT_ARRAY_BUFFER,
579 new Uint8Array([0, 1, 2, 0, 2, 3]),
582 // first create the buffers for no VAO draw.
583 var nonVAOColorBuffer = gl.createBuffer();
584 gl.bindBuffer(gl.ARRAY_BUFFER, nonVAOColorBuffer);
585 gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(
592 // shared position buffer.
593 var positionBuffer = gl.createBuffer();
594 gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
604 // attach position buffer to default
605 gl.enableVertexAttribArray(1);
606 gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);
609 var vao = ext.createVertexArrayOES();
610 ext.bindVertexArrayOES(vao);
612 // attach the position buffer VAO
613 gl.enableVertexAttribArray(1);
614 gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0);
616 var vaoColorBuffer = gl.createBuffer();
617 gl.bindBuffer(gl.ARRAY_BUFFER, vaoColorBuffer);
618 gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(
624 gl.enableVertexAttribArray(0);
625 gl.vertexAttribPointer(0, 4, gl.UNSIGNED_BYTE, true, 0, 0);
627 // now set the buffer back to the nonVAOColorBuffer
628 gl.bindBuffer(gl.ARRAY_BUFFER, nonVAOColorBuffer);
631 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
632 gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
633 wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red");
636 ext.bindVertexArrayOES(null);
638 // At this point the nonVAOColorBuffer should be still be bound.
639 // If the WebGL impl is emulating VAOs it must make sure
640 // it correctly restores this binding.
641 gl.enableVertexAttribArray(0);
642 gl.vertexAttribPointer(0, 4, gl.UNSIGNED_BYTE, true, 0, 0);
643 gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0);
644 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
648 var successfullyParsed = true;
650 <script src="../../resources/js-test-post.js"></script>