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 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>
18 <div id=
"description"></div>
19 <canvas id=
"canvas" style=
"width: 50px; height: 50px;"> </canvas>
20 <div id=
"console"></div>
21 <!-- Shaders for testing instanced draws -->
22 <script id=
"outputVertexShader" type=
"x-shader/x-vertex">
23 attribute vec4 aPosition;
24 attribute vec2 aOffset;
25 attribute vec4 aColor;
29 gl_Position = aPosition + vec4(aOffset,
0.0,
0.0);
33 <script id=
"outputFragmentShader" type=
"x-shader/x-fragment">
34 precision mediump float;
37 gl_FragColor = vColor;
43 description("This test verifies the functionality of Instanced Arrays.");
47 var wtu
= WebGLTestUtils
;
48 var canvas
= document
.getElementById("canvas");
49 var gl
= wtu
.create3DContext(canvas
, null, 2);
52 testFailed("WebGL context does not exist");
54 testPassed("WebGL context exists");
58 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
61 function runDivisorTest() {
62 debug("Testing VERTEX_ATTRIB_ARRAY_DIVISOR");
64 shouldBe("gl.VERTEX_ATTRIB_ARRAY_DIVISOR", "0x88FE");
66 var max_vertex_attribs
= gl
.getParameter(gl
.MAX_VERTEX_ATTRIBS
);
68 for (var i
= 0; i
< max_vertex_attribs
; ++i
) {
69 var queried_value
= gl
.getVertexAttrib(i
, gl
.VERTEX_ATTRIB_ARRAY_DIVISOR
);
70 if(queried_value
== 0){
71 testPassed("Vertex attribute " + i
+ " must has a default divisor of 0");
74 testFailed("Default divisor of vertex attribute " + i
+ " should be: 0, returned value was: " + queried_value
);
78 gl
.vertexAttribDivisor(max_vertex_attribs
, 2);
79 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "vertexAttribDivisor index set greater than or equal to MAX_VERTEX_ATTRIBS should be an invalid value");
81 gl
.vertexAttribDivisor(max_vertex_attribs
-1, 2);
82 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "vertexAttribDivisor index set less than MAX_VERTEX_ATTRIBS should succeed");
84 var queried_value
= gl
.getVertexAttrib(max_vertex_attribs
-1, gl
.VERTEX_ATTRIB_ARRAY_DIVISOR
);
85 if(queried_value
== 2){
86 testPassed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR matches expecation");
89 testFailed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR should be: 2, returned value was: " + queried_value
);
93 function runOutputTests() {
94 var e
= 2; // Amount of variance to allow in result pixels - may need to be tweaked higher
95 var instanceCount
= 4;
97 debug("Testing various draws for valid built-in function behavior");
99 canvas
.width
= 50; canvas
.height
= 50;
100 gl
.viewport(0, 0, canvas
.width
, canvas
.height
);
101 gl
.clearColor(0, 0, 0, 0);
106 var program
= wtu
.setupProgram(gl
, ["outputVertexShader", "outputFragmentShader"], ['aPosition', 'aOffset', 'aColor'], [positionLoc
, offsetLoc
, colorLoc
]);
108 var offsets
= new Float32Array([
114 var offsetBuffer
= gl
.createBuffer();
115 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
116 gl
.bufferData(gl
.ARRAY_BUFFER
, offsets
, gl
.STATIC_DRAW
);
117 gl
.enableVertexAttribArray(offsetLoc
);
118 gl
.vertexAttribPointer(offsetLoc
, 2, gl
.FLOAT
, false, 0, 0);
119 gl
.vertexAttribDivisor(offsetLoc
, 1);
121 var colors
= new Float32Array([
122 1.0, 0.0, 0.0, 1.0, // Red
123 0.0, 1.0, 0.0, 1.0, // Green
124 0.0, 0.0, 1.0, 1.0, // Blue
125 1.0, 1.0, 0.0, 1.0, // Yellow
126 // extra data when colorLoc divisor is set back to 0
127 1.0, 1.0, 0.0, 1.0, // Yellow
128 1.0, 1.0, 0.0, 1.0, // Yellow
130 var colorBuffer
= gl
.createBuffer();
131 gl
.bindBuffer(gl
.ARRAY_BUFFER
, colorBuffer
);
132 gl
.bufferData(gl
.ARRAY_BUFFER
, colors
, gl
.STATIC_DRAW
);
133 gl
.enableVertexAttribArray(colorLoc
);
134 gl
.vertexAttribPointer(colorLoc
, 4, gl
.FLOAT
, false, 0, 0);
135 gl
.vertexAttribDivisor(colorLoc
, 1);
137 // Draw 1: Draw Non-indexed instances
138 debug("Testing drawArraysInstanced");
139 gl
.clear(gl
.COLOR_BUFFER_BIT
);
140 wtu
.setupUnitQuad(gl
, 0);
142 // Test drawArraysInstanced error conditions
143 gl
.drawArraysInstanced(gl
.TRIANGLES
, 0, 6, instanceCount
);
144 wtu
.checkCanvasRect(gl
, 0, canvas
.height
/2, canvas.width/2, canvas
.height
/2, [255, 0, 0, 255]);
145 wtu
.checkCanvasRect(gl
, canvas
.width
/2, canvas.height/2, canvas
.width
/2, canvas.height/2, [0, 255, 0, 255]);
146 wtu
.checkCanvasRect(gl
, 0, 0, canvas
.width
/2, canvas.height/2, [0, 0, 255, 255]);
147 wtu
.checkCanvasRect(gl
, canvas
.width
/2, 0, canvas.width/2, canvas
.height
/2, [255, 255, 0, 255]);
149 gl
.drawArraysInstanced(gl
.TRIANGLES
, 0, 6, -1);
150 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawArraysInstanced cannot have a primcount less than 0");
152 gl
.drawArraysInstanced(gl
.TRIANGLES
, 0, -1, instanceCount
);
153 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawArraysInstanced cannot have a count less than 0");
155 gl
.vertexAttribDivisor(positionLoc
, 1);
156 gl
.clear(gl
.COLOR_BUFFER_BIT
);
157 gl
.drawArraysInstanced(gl
.TRIANGLES
, 0, 6, instanceCount
);
158 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "It's allowed for all vertex attributes to have non-zero divisors when calling drawArraysInstanced");
159 gl
.drawArrays(gl
.TRIANGLES
, 0, 6);
160 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "It's allowed for all vertex attributes to have non-zero divisors when calling drawArrays");
161 wtu
.checkCanvas(gl
, [0, 0, 0, 0], "Nothing should be drawn on the framebuffer when all attributes have non-zero divisors (not enough vertices per instance to form a triangle)");
162 gl
.vertexAttribDivisor(positionLoc
, 0);
164 gl
.drawArraysInstanced(gl
.POINTS
, 0, 6, instanceCount
);
165 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstanced with POINTS should succeed");
166 gl
.drawArraysInstanced(gl
.LINES
, 0, 6, instanceCount
);
167 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstanced with LINES should succeed");
168 gl
.drawArraysInstanced(gl
.LINE_LIST
, 0, 6, instanceCount
);
169 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstanced with LINE_LIST should return succeed");
170 gl
.drawArraysInstanced(gl
.TRI_LIST
, 0, 6, instanceCount
);
171 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawArraysInstanced with TRI_LIST should succeed");
173 gl
.drawArraysInstanced(desktopGL
['QUAD_STRIP'], 0, 6, instanceCount
);
174 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawArraysInstanced with QUAD_STRIP should return INVALID_ENUM");
175 gl
.drawArraysInstanced(desktopGL
['QUADS'], 0, 6, instanceCount
);
176 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawArraysInstanced with QUADS should return INVALID_ENUM");
177 gl
.drawArraysInstanced(desktopGL
['POLYGON'], 0, 6, instanceCount
);
178 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawArraysInstanced with POLYGON should return INVALID_ENUM");
180 debug("Testing drawArraysInstanced with param 'first' > 0");
181 gl
.clear(gl
.COLOR_BUFFER_BIT
);
186 var offsetsHalf
= new Float32Array([
192 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
193 gl
.bufferData(gl
.ARRAY_BUFFER
, offsetsHalf
, gl
.STATIC_DRAW
);
195 gl
.drawArraysInstanced(gl
.TRIANGLES
, 3, 3, instanceCount
);
196 var w
= Math
.floor(0.25*canvas
.width
),
197 h
= Math
.floor(0.25*canvas
.height
);
198 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0.5*canvas
.height
, w
, h
, [255, 0, 0, 255]);
199 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0.5*canvas
.height
, w
, h
, [0, 255, 0, 255]);
200 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0, w
, h
, [0, 0, 255, 255]);
201 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0, w
, h
, [255, 255, 0, 255]);
203 debug("Testing drawArraysInstanced with attributes 'divisor' reset to 0");
204 debug("Correct rendering output: 4 yellow triangles");
205 debug("Possible incorrect rendering output: missing triangles, or triangles with different color at each vertex");
206 gl
.vertexAttribDivisor(colorLoc
, 0);
207 gl
.clear(gl
.COLOR_BUFFER_BIT
);
208 gl
.drawArraysInstanced(gl
.TRIANGLES
, 3, 3, instanceCount
);
209 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0.5*canvas
.height
, w
, h
, [255, 255, 0, 255]);
210 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0.5*canvas
.height
, w
, h
, [255, 255, 0, 255]);
211 wtu
.checkCanvasRect(gl
, Math
.ceil(0.25*canvas
.width
), 0, w
, h
, [255, 255, 0, 255]);
212 wtu
.checkCanvasRect(gl
, Math
.ceil(0.75*canvas
.width
), 0, w
, h
, [255, 255, 0, 255]);
213 gl
.vertexAttribDivisor(colorLoc
, 1);
215 wtu
.setupUnitQuad(gl
, 0);
216 gl
.bindBuffer(gl
.ARRAY_BUFFER
, offsetBuffer
);
217 gl
.bufferData(gl
.ARRAY_BUFFER
, offsets
, gl
.STATIC_DRAW
);
219 // Draw 2: Draw indexed instances
220 debug("Testing drawElementsInstanced");
221 gl
.clear(gl
.COLOR_BUFFER_BIT
);
222 wtu
.setupIndexedQuad(gl
, 1, 0);
223 gl
.drawElementsInstanced(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
224 wtu
.checkCanvasRect(gl
, 0, canvas
.height
/2, canvas.width/2, canvas
.height
/2, [255, 0, 0, 255]);
225 wtu
.checkCanvasRect(gl
, canvas
.width
/2, canvas.height/2, canvas
.width
/2, canvas.height/2, [0, 255, 0, 255]);
226 wtu
.checkCanvasRect(gl
, 0, 0, canvas
.width
/2, canvas.height/2, [0, 0, 255, 255]);
227 wtu
.checkCanvasRect(gl
, canvas
.width
/2, 0, canvas.width/2, canvas
.height
/2, [255, 255, 0, 255]);
229 // Test drawElementsInstanced error conditions
230 gl
.drawElementsInstanced(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0, -1);
231 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawElementsInstanced cannot have a primcount less than 0");
233 gl
.drawElementsInstanced(gl
.TRIANGLES
, -1, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
234 wtu
.glErrorShouldBe(gl
, gl
.INVALID_VALUE
, "drawElementsInstanced cannot have a count less than 0");
236 gl
.vertexAttribDivisor(positionLoc
, 1);
237 gl
.clear(gl
.COLOR_BUFFER_BIT
);
238 gl
.drawElementsInstanced(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
239 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "It's allowed for all vertex attributes to have non-zero divisors when calling drawElementsInstanced");
240 gl
.drawElements(gl
.TRIANGLES
, 6, gl
.UNSIGNED_SHORT
, 0);
241 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "It's allowed for all vertex attributes to have non-zero divisors when calling drawElements");
242 wtu
.checkCanvas(gl
, [0, 0, 0, 0], "Nothing should be drawn on the framebuffer when all attributes have non-zero divisors (not enough vertices per instance to form a triangle)");
243 gl
.vertexAttribDivisor(positionLoc
, 0);
245 gl
.drawElementsInstanced(gl
.TRIANGLES
, 6, gl
.UNSIGNED_BYTE
, 0, instanceCount
);
246 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstanced with UNSIGNED_BYTE should succeed");
248 gl
.drawElementsInstanced(gl
.POINTS
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
249 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstanced with POINTS should succeed");
250 gl
.drawElementsInstanced(gl
.LINES
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
251 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstanced with LINES should succeed");
252 gl
.drawElementsInstanced(gl
.LINE_LIST
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
253 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstanced with LINE_LIST should return succeed");
254 gl
.drawElementsInstanced(gl
.TRI_LIST
, 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
255 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "drawElementsInstanced with TRI_LIST should succeed");
257 gl
.drawElementsInstanced(desktopGL
['QUAD_STRIP'], 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
258 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawElementsInstanced with QUAD_STRIP should return INVALID_ENUM");
259 gl
.drawElementsInstanced(desktopGL
['QUADS'], 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
260 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawElementsInstanced with QUADS should return INVALID_ENUM");
261 gl
.drawElementsInstanced(desktopGL
['POLYGON'], 6, gl
.UNSIGNED_SHORT
, 0, instanceCount
);
262 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "drawElementsInstanced with POLYGON should return INVALID_ENUM");
266 var successfullyParsed
= true;
268 <script src=
"../../js/js-test-post.js"></script>