instanced-arrays.html (12717B)
1 <!-- 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. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 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> 16 </head> 17 <body> 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; 26 varying vec4 vColor; 27 void main() { 28 vColor = aColor; 29 gl_Position = aPosition + vec4(aOffset, 0.0, 0.0); 30 } 31 </script> 32 33 <script id="outputFragmentShader" type="x-shader/x-fragment"> 34 precision mediump float; 35 varying vec4 vColor; 36 void main() { 37 gl_FragColor = vColor; 38 } 39 </script> 40 41 <script> 42 "use strict"; 43 description("This test verifies the functionality of Instanced Arrays."); 44 45 debug(""); 46 47 var wtu = WebGLTestUtils; 48 var canvas = document.getElementById("canvas"); 49 var gl = wtu.create3DContext(canvas, null, 2); 50 51 if (!gl) { 52 testFailed("WebGL context does not exist"); 53 } else { 54 testPassed("WebGL context exists"); 55 56 runDivisorTest(); 57 runOutputTests(); 58 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); 59 } 60 61 function runDivisorTest() { 62 debug("Testing VERTEX_ATTRIB_ARRAY_DIVISOR"); 63 64 shouldBe("gl.VERTEX_ATTRIB_ARRAY_DIVISOR", "0x88FE"); 65 66 var max_vertex_attribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); 67 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"); 72 } 73 else{ 74 testFailed("Default divisor of vertex attribute " + i + " should be: 0, returned value was: " + queried_value); 75 } 76 } 77 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"); 80 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"); 83 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"); 87 } 88 else{ 89 testFailed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR should be: 2, returned value was: " + queried_value); 90 } 91 } 92 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; 96 97 debug("Testing various draws for valid built-in function behavior"); 98 99 canvas.width = 50; canvas.height = 50; 100 gl.viewport(0, 0, canvas.width, canvas.height); 101 gl.clearColor(0, 0, 0, 0); 102 103 var positionLoc = 0; 104 var offsetLoc = 2; 105 var colorLoc = 3; 106 var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['aPosition', 'aOffset', 'aColor'], [positionLoc, offsetLoc, colorLoc]); 107 108 var offsets = new Float32Array([ 109 -1.0, 1.0, 110 1.0, 1.0, 111 -1.0, -1.0, 112 1.0, -1.0, 113 ]); 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); 120 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 129 ]); 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); 136 137 // Draw 1: Draw Non-indexed instances 138 debug("Testing drawArraysInstanced"); 139 gl.clear(gl.COLOR_BUFFER_BIT); 140 wtu.setupUnitQuad(gl, 0); 141 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]); 148 149 gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, -1); 150 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawArraysInstanced cannot have a primcount less than 0"); 151 152 gl.drawArraysInstanced(gl.TRIANGLES, 0, -1, instanceCount); 153 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawArraysInstanced cannot have a count less than 0"); 154 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); 163 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"); 172 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"); 179 180 debug("Testing drawArraysInstanced with param 'first' > 0"); 181 gl.clear(gl.COLOR_BUFFER_BIT); 182 wtu.setupQuad(gl, { 183 positionLocation: 0, 184 scale: 0.5 185 }); 186 var offsetsHalf = new Float32Array([ 187 -0.5, 0.5, 188 0.5, 0.5, 189 -0.5, -0.5, 190 0.5, -0.5 191 ]); 192 gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); 193 gl.bufferData(gl.ARRAY_BUFFER, offsetsHalf, gl.STATIC_DRAW); 194 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]); 202 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); 214 215 wtu.setupUnitQuad(gl, 0); 216 gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); 217 gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW); 218 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]); 228 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"); 232 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"); 235 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); 244 245 gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, instanceCount); 246 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstanced with UNSIGNED_BYTE should succeed"); 247 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"); 256 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"); 263 } 264 265 debug(""); 266 var successfullyParsed = true; 267 </script> 268 <script src="../../js/js-test-post.js"></script> 269 270 </body> 271 </html>