5 <title>WebGL OES_vertex_array_object Conformance Tests
</title>
6 <script src=
"resources/desktop-gl-constants.js" type=
"text/javascript"></script>
7 <script src=
"../../../resources/js-test.js"></script>
8 <script src=
"resources/webgl-test.js"></script>
9 <script src=
"resources/webgl-test-utils.js"></script>
12 <div id=
"description"></div>
13 <canvas id=
"canvas" style=
"width: 50px; height: 50px;"> </canvas>
14 <div id=
"console"></div>
15 <script id=
"vshader",
type=
"x-shader/x-vertex">
16 attribute vec4 a_position;
17 attribute vec4 a_color;
20 gl_Position = a_position;
24 <script id=
"fshader",
type=
"x-shader/x-fragment">
25 precision mediump float;
28 gl_FragColor = v_color;
32 description("This test verifies the functionality of the OES_vertex_array_object extension, if it is available.");
37 window
.internals
.settings
.setWebGLErrorsToConsoleEnabled(false);
39 var wtu
= WebGLTestUtils
;
40 var canvas
= document
.getElementById("canvas");
41 var gl
= create3DContext(canvas
);
46 testFailed("WebGL context does not exist");
48 testPassed("WebGL context exists");
50 // Setup emulated OESVertexArrayObject if it has been included.
51 if (window
.setupVertexArrayObject
) {
52 debug("using emuated OES_vertex_array_object");
53 setupVertexArrayObject(gl
);
56 // Run tests with extension disabled
57 runBindingTestDisabled();
59 // Query the extension and store globally so shouldBe can access it
60 ext
= gl
.getExtension("OES_vertex_array_object");
62 testPassed("No OES_vertex_array_object support -- this is legal");
64 runSupportedTest(false);
66 testPassed("Successfully enabled OES_vertex_array_object extension");
68 runSupportedTest(true);
69 runBindingTestEnabled();
72 runAttributeValueTests();
75 runArrayBufferBindTests();
76 glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
80 function runSupportedTest(extensionEnabled
) {
81 var supported
= gl
.getSupportedExtensions();
82 if (supported
.indexOf("OES_vertex_array_object") >= 0) {
83 if (extensionEnabled
) {
84 testPassed("OES_vertex_array_object listed as supported and getExtension succeeded");
86 testFailed("OES_vertex_array_object listed as supported but getExtension failed");
89 if (extensionEnabled
) {
90 testFailed("OES_vertex_array_object not listed as supported but getExtension succeeded");
92 testPassed("OES_vertex_array_object not listed as supported and getExtension failed -- this is legal");
97 function runBindingTestDisabled() {
98 debug("Testing binding enum with extension disabled");
100 // Use the constant directly as we don't have the extension
101 var VERTEX_ARRAY_BINDING_OES
= 0x85B5;
103 gl
.getParameter(VERTEX_ARRAY_BINDING_OES
);
104 glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "VERTEX_ARRAY_BINDING_OES should not be queryable if extension is disabled");
107 function runBindingTestEnabled() {
108 debug("Testing binding enum with extension enabled");
110 shouldBe("ext.VERTEX_ARRAY_BINDING_OES", "0x85B5");
112 gl
.getParameter(ext
.VERTEX_ARRAY_BINDING_OES
);
113 glErrorShouldBe(gl
, gl
.NO_ERROR
, "VERTEX_ARRAY_BINDING_OES query should succeed if extension is enable");
115 // Default value is null
116 if (gl
.getParameter(ext
.VERTEX_ARRAY_BINDING_OES
) === null) {
117 testPassed("Default value of VERTEX_ARRAY_BINDING_OES is null");
119 testFailed("Default value of VERTEX_ARRAY_BINDING_OES is not null");
122 debug("Testing binding a VAO");
123 var vao0
= ext
.createVertexArrayOES();
124 var vao1
= ext
.createVertexArrayOES();
125 shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
126 ext
.bindVertexArrayOES(vao0
);
127 if (gl
.getParameter(ext
.VERTEX_ARRAY_BINDING_OES
) == vao0
) {
128 testPassed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is expected VAO");
130 testFailed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is not expected VAO")
132 ext
.bindVertexArrayOES(vao1
);
133 if (gl
.getParameter(ext
.VERTEX_ARRAY_BINDING_OES
) == vao1
) {
134 testPassed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is expected VAO");
136 testFailed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is not expected VAO")
138 ext
.deleteVertexArrayOES(vao1
);
139 shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
140 ext
.bindVertexArrayOES(vao1
);
141 glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "binding a deleted vertex array object");
142 ext
.bindVertexArrayOES(null);
143 shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)");
144 ext
.deleteVertexArrayOES(vao1
);
147 function runObjectTest() {
148 debug("Testing object creation");
150 vao
= ext
.createVertexArrayOES();
151 glErrorShouldBe(gl
, gl
.NO_ERROR
, "createVertexArrayOES should not set an error");
152 shouldBeNonNull("vao");
154 // Expect false if never bound
155 shouldBeFalse("ext.isVertexArrayOES(vao)");
156 ext
.bindVertexArrayOES(vao
);
157 shouldBeTrue("ext.isVertexArrayOES(vao)");
158 ext
.bindVertexArrayOES(null);
159 shouldBeTrue("ext.isVertexArrayOES(vao)");
161 shouldBeFalse("ext.isVertexArrayOES()");
162 shouldBeFalse("ext.isVertexArrayOES(null)");
164 ext
.deleteVertexArrayOES(vao
);
168 function runAttributeTests() {
169 debug("Testing attributes work across bindings");
173 var attrCount
= gl
.getParameter(gl
.MAX_VERTEX_ATTRIBS
);
174 for (var n
= 0; n
< attrCount
; n
++) {
175 gl
.bindBuffer(gl
.ARRAY_BUFFER
, null);
176 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, null);
181 var vao
= state
.vao
= ext
.createVertexArrayOES();
182 ext
.bindVertexArrayOES(vao
);
185 gl
.enableVertexAttribArray(n
);
187 gl
.disableVertexAttribArray(n
);
191 var buffer
= state
.buffer
= gl
.createBuffer();
192 gl
.bindBuffer(gl
.ARRAY_BUFFER
, buffer
);
193 gl
.bufferData(gl
.ARRAY_BUFFER
, 1024, gl
.STATIC_DRAW
);
195 gl
.vertexAttribPointer(n
, 1 + n
% 4, gl
.FLOAT
, true, n
* 4, n
* 4);
199 var elbuffer
= state
.elbuffer
= gl
.createBuffer();
200 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elbuffer
);
201 gl
.bufferData(gl
.ELEMENT_ARRAY_BUFFER
, 1024, gl
.STATIC_DRAW
);
204 ext
.bindVertexArrayOES(null);
207 var anyMismatch
= false;
208 for (var n
= 0; n
< attrCount
; n
++) {
209 var state
= states
[n
];
211 ext
.bindVertexArrayOES(state
.vao
);
213 var isEnabled
= gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_ENABLED
);
214 if ((n
% 2 == 1) || isEnabled
) {
217 testFailed("VERTEX_ATTRIB_ARRAY_ENABLED not preserved");
221 var buffer
= gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING
);
223 if (buffer
== state
.buffer
) {
225 if ((gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_SIZE
) == 1 + n
% 4) &&
226 (gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_TYPE
) == gl
.FLOAT
) &&
227 (gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_NORMALIZED
) == true) &&
228 (gl
.getVertexAttrib(n
, gl
.VERTEX_ATTRIB_ARRAY_STRIDE
) == n
* 4) &&
229 (gl
.getVertexAttribOffset(n
, gl
.VERTEX_ATTRIB_ARRAY_POINTER
) == n
* 4)) {
232 testFailed("VERTEX_ATTRIB_ARRAY_* not preserved");
236 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
240 // GL_CURRENT_VERTEX_ATTRIB is not preserved
242 testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved");
247 var elbuffer
= gl
.getParameter(gl
.ELEMENT_ARRAY_BUFFER_BINDING
);
249 if (elbuffer
== state
.elbuffer
) {
252 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
256 if (elbuffer
== null) {
259 testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved");
264 ext
.bindVertexArrayOES(null);
266 testPassed("All attributes preserved across bindings");
269 for (var n
= 0; n
< attrCount
; n
++) {
270 var state
= states
[n
];
271 ext
.deleteVertexArrayOES(state
.vao
);
275 function runAttributeValueTests() {
276 debug("Testing that attribute values are not attached to bindings");
279 var vao0
= ext
.createVertexArrayOES();
280 var anyFailed
= false;
282 ext
.bindVertexArrayOES(null);
283 gl
.vertexAttrib4f(0, 0, 1, 2, 3);
285 v
= gl
.getVertexAttrib(0, gl
.CURRENT_VERTEX_ATTRIB
);
286 if (!(v
[0] == 0 && v
[1] == 1 && v
[2] == 2 && v
[3] == 3)) {
287 testFailed("Vertex attrib value not round-tripped?");
291 ext
.bindVertexArrayOES(vao0
);
293 v
= gl
.getVertexAttrib(0, gl
.CURRENT_VERTEX_ATTRIB
);
294 if (!(v
[0] == 0 && v
[1] == 1 && v
[2] == 2 && v
[3] == 3)) {
295 testFailed("Vertex attrib value reset across bindings");
299 gl
.vertexAttrib4f(0, 4, 5, 6, 7);
300 ext
.bindVertexArrayOES(null);
302 v
= gl
.getVertexAttrib(0, gl
.CURRENT_VERTEX_ATTRIB
);
303 if (!(v
[0] == 4 && v
[1] == 5 && v
[2] == 6 && v
[3] == 7)) {
304 testFailed("Vertex attrib value bound to buffer");
309 testPassed("Vertex attribute values are not attached to bindings")
312 ext
.bindVertexArrayOES(null);
313 ext
.deleteVertexArrayOES(vao0
);
316 function runDrawTests() {
317 debug("Testing draws with various VAO bindings");
319 canvas
.width
= 50; canvas
.height
= 50;
320 gl
.viewport(0, 0, canvas
.width
, canvas
.height
);
322 var vao0
= ext
.createVertexArrayOES();
323 var vao1
= ext
.createVertexArrayOES();
325 var program
= wtu
.setupSimpleTextureProgram(gl
, 0, 1);
327 function setupQuad(s
) {
328 var opt_positionLocation
= 0;
329 var opt_texcoordLocation
= 1;
330 var vertexObject
= gl
.createBuffer();
331 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertexObject
);
332 gl
.bufferData(gl
.ARRAY_BUFFER
, new Float32Array([
333 1.0 * s
, 1.0 * s
, 0.0,
334 -1.0 * s
, 1.0 * s
, 0.0,
335 -1.0 * s
, -1.0 * s
, 0.0,
336 1.0 * s
, 1.0 * s
, 0.0,
337 -1.0 * s
, -1.0 * s
, 0.0,
338 1.0 * s
, -1.0 * s
, 0.0]), gl
.STATIC_DRAW
);
339 gl
.enableVertexAttribArray(opt_positionLocation
);
340 gl
.vertexAttribPointer(opt_positionLocation
, 3, gl
.FLOAT
, false, 0, 0);
342 var vertexObject
= gl
.createBuffer();
343 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertexObject
);
344 gl
.bufferData(gl
.ARRAY_BUFFER
, new Float32Array([
350 1.0 * s
, 0.0 * s
]), gl
.STATIC_DRAW
);
351 gl
.enableVertexAttribArray(opt_texcoordLocation
);
352 gl
.vertexAttribPointer(opt_texcoordLocation
, 2, gl
.FLOAT
, false, 0, 0);
355 function readLocation(x
, y
) {
356 var pixels
= new Uint8Array(1 * 1 * 4);
357 gl
.readPixels(x
, y
, 1, 1, gl
.RGBA
, gl
.UNSIGNED_BYTE
, pixels
);
360 function testPixel(blackList
, whiteList
) {
361 function testList(list
, expected
) {
362 for (var n
= 0; n
< list
.length
; n
++) {
364 var x
= -Math
.floor(l
* canvas
.width
/ 2) + canvas
.width
/ 2;
365 var y
= -Math
.floor(l
* canvas
.height
/ 2) + canvas
.height
/ 2;
366 var source
= readLocation(x
, y
);
367 if (Math
.abs(source
[0] - expected
) > 2) {
373 return testList(blackList
, 0) && testList(whiteList
, 255);
375 function verifyDraw(drawNumber
, s
) {
379 var points
= [0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
380 for (var n
= 0; n
< points
.length
; n
++) {
381 if (points
[n
] <= s
) {
382 blackList
.push(points
[n
]);
384 whiteList
.push(points
[n
]);
387 if (testPixel(blackList
, whiteList
)) {
388 testPassed("Draw " + drawNumber
+ " passed pixel test");
390 testFailed("Draw " + drawNumber
+ " failed pixel test");
394 // Setup all bindings
396 ext
.bindVertexArrayOES(vao0
);
398 ext
.bindVertexArrayOES(vao1
);
402 ext
.bindVertexArrayOES(null);
404 ext
.bindVertexArrayOES(vao0
);
406 ext
.bindVertexArrayOES(vao1
);
409 ext
.bindVertexArrayOES(null);
410 ext
.deleteVertexArrayOES(vao0
);
411 ext
.deleteVertexArrayOES(vao1
);
414 function runDeleteTests() {
415 debug("Testing using deleted buffers referenced by VAOs");
417 var program
= wtu
.setupProgram(gl
, ["vshader", "fshader"], ["a_position", "a_color"]);
418 gl
.useProgram(program
);
420 var positionBuffer
= gl
.createBuffer();
421 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
437 var colorBuffers
= [];
438 var elementBuffers
= [];
440 for (var ii
= 0; ii
< colors
.length
; ++ii
) {
441 var vao
= ext
.createVertexArrayOES();
443 ext
.bindVertexArrayOES(vao
);
444 // Set the position buffer
445 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
446 gl
.enableVertexAttribArray(0);
447 gl
.vertexAttribPointer(0, 2, gl
.FLOAT
, false, 0, 0);
449 var elementBuffer
= gl
.createBuffer();
450 elementBuffers
.push(elementBuffer
);
451 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementBuffer
);
453 gl
.ELEMENT_ARRAY_BUFFER
,
454 new Uint8Array([0, 1, 2, 0, 2, 3]),
457 // Setup the color attrib
458 var color
= colors
[ii
];
460 var colorBuffer
= gl
.createBuffer();
461 colorBuffers
.push(colorBuffer
);
462 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
463 gl
.bufferData(gl
.ARRAY_BUFFER
, new Uint8Array(
464 [ color
[0], color
[1], color
[2], color
[3],
465 color
[0], color
[1], color
[2], color
[3],
466 color
[0], color
[1], color
[2], color
[3],
467 color
[0], color
[1], color
[2], color
[3]
469 gl
.enableVertexAttribArray(1);
470 gl
.vertexAttribPointer(1, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
472 gl
.vertexAttrib4f(1, color
[0] / 255, color
[1] / 255, color
[2] / 255, color
[3] / 255);
476 // delete the color buffers AND the position buffer.
477 ext
.bindVertexArrayOES(null);
478 for (var ii
= 0; ii
< colorBuffers
.length
; ++ii
) {
479 gl
.deleteBuffer(colorBuffers
[ii
]);
480 gl
.deleteBuffer(elementBuffers
[ii
]);
481 // The buffers should still be valid at this point, since it was attached to the VAO
482 if(!gl
.isBuffer(colorBuffers
[ii
])) {
483 testFailed("buffer removed too early");
486 gl
.deleteBuffer(positionBuffer
);
488 // Render with the deleted buffers. As they are referenced by VAOs they
489 // must still be around.
490 for (var ii
= 0; ii
< colors
.length
; ++ii
) {
491 var color
= colors
[ii
];
492 ext
.bindVertexArrayOES(vaos
[ii
]);
493 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
494 wtu
.checkCanvas(gl
, color
, "should be " + color
);
498 for (var ii
= 0; ii
< colorBuffers
.length
; ++ii
) {
499 ext
.deleteVertexArrayOES(vaos
[ii
]);
502 for (var ii
= 0; ii
< colorBuffers
.length
; ++ii
) {
503 // The buffers should no longer be valid now that the VAOs are deleted
504 if(gl
.isBuffer(colorBuffers
[ii
])) {
505 testFailed("buffer not properly cleaned up after VAO deletion");
510 function runArrayBufferBindTests() {
511 debug("Testing that VAOs don't effect ARRAY_BUFFER binding.");
513 ext
.bindVertexArrayOES(null);
515 var program
= wtu
.setupProgram(gl
, ["vshader", "fshader"], ["a_color", "a_position"]);
516 gl
.useProgram(program
);
518 // create shared element buuffer
519 var elementBuffer
= gl
.createBuffer();
521 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementBuffer
);
523 gl
.ELEMENT_ARRAY_BUFFER
,
524 new Uint8Array([0, 1, 2, 0, 2, 3]),
527 // first create the buffers for no vao draw.
528 var nonVAOColorBuffer
= gl
.createBuffer();
529 gl
.bindBuffer(gl
.ARRAY_BUFFER
, nonVAOColorBuffer
);
530 gl
.bufferData(gl
.ARRAY_BUFFER
, new Uint8Array(
537 // shared position buffer.
538 var positionBuffer
= gl
.createBuffer();
539 gl
.bindBuffer(gl
.ARRAY_BUFFER
, positionBuffer
);
549 // attach position buffer to default
550 gl
.enableVertexAttribArray(1);
551 gl
.vertexAttribPointer(1, 2, gl
.FLOAT
, false, 0, 0);
554 var vao
= ext
.createVertexArrayOES();
555 ext
.bindVertexArrayOES(vao
);
557 // attach the position buffer vao
558 gl
.enableVertexAttribArray(1);
559 gl
.vertexAttribPointer(1, 2, gl
.FLOAT
, false, 0, 0);
561 var vaoColorBuffer
= gl
.createBuffer();
562 gl
.enableVertexAttribArray(0);
563 gl
.vertexAttribPointer(0, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
564 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vaoColorBuffer
);
565 gl
.bufferData(gl
.ARRAY_BUFFER
, new Uint8Array(
571 gl
.enableVertexAttribArray(0);
572 gl
.vertexAttribPointer(0, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
574 // now set the buffer back to the nonVAOColorBuffer
575 gl
.bindBuffer(gl
.ARRAY_BUFFER
, nonVAOColorBuffer
);
578 gl
.bindBuffer(gl
.ELEMENT_ARRAY_BUFFER
, elementBuffer
);
579 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
580 wtu
.checkCanvas(gl
, [255, 0, 0, 255], "should be red");
583 ext
.bindVertexArrayOES(null);
585 // At this point the nonVAOColorBuffer should be still be bound.
586 // If the WebGL impl is emulating VAOs it must make sure
587 // it correctly restores this binding.
588 gl
.enableVertexAttribArray(0);
589 gl
.vertexAttribPointer(0, 4, gl
.UNSIGNED_BYTE
, true, 0, 0);
590 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0);
591 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green");
595 successfullyParsed
= true;
596 isSuccessfullyParsed();