webgl-multi-draw.html (35060B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>WebGL ANGLE_multi_draw Conformance Tests</title> 6 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 7 <script src="../../js/desktop-gl-constants.js"></script> 8 <script src="../../js/js-test-pre.js"></script> 9 <script src="../../js/webgl-test-utils.js"></script> 10 <script src="../../js/tests/compositing-test.js"></script> 11 <script src="../../js/tests/invalid-vertex-attrib-test.js"></script> 12 </head> 13 <body> 14 <script id="vshaderIllegalDrawID" type="x-shader/x-vertex"> 15 attribute vec2 vPosition; 16 varying vec4 color; 17 void main() 18 { 19 color = vec4(1.0, 0.0, 0.0, 1.0); 20 gl_Position = vec4(vPosition * 2.0 - 1.0, gl_DrawID, 1); 21 } 22 </script> 23 <script id="vshaderDrawIDZero" type="x-shader/x-vertex"> 24 #extension GL_ANGLE_multi_draw : require 25 attribute vec2 vPosition; 26 varying vec4 color; 27 void main() 28 { 29 if (gl_DrawID == 0) { 30 color = vec4(0, 1, 0, 1); 31 } else { 32 color = vec4(1, 0, 0, 1); 33 } 34 gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1); 35 } 36 </script> 37 <!-- The behavior of the shaders below is described in runPixelTests() --> 38 <script id="vshaderWithDrawID" type="x-shader/x-vertex"> 39 #extension GL_ANGLE_multi_draw : require 40 attribute vec2 vPosition; 41 attribute float vInstance; 42 varying vec4 color; 43 void main() 44 { 45 // color_id = (gl_DrawID / 2) % 3 46 float quad_id = float(gl_DrawID / 2); 47 float color_id = quad_id - (3.0 * floor(quad_id / 3.0)); 48 if (color_id < 0.5) { 49 color = vec4(1, 0, 0, 1); 50 } else if (color_id < 1.5) { 51 color = vec4(0, 1, 0, 1); 52 } else { 53 color = vec4(0, 0, 1, 1); 54 } 55 mat3 transform = mat3(1.0); 56 // vInstance starts at 1.0 on instanced calls 57 if (vInstance >= 1.0) { 58 transform[0][0] = 0.5; 59 transform[1][1] = 0.5; 60 } 61 if (vInstance == 1.0) { 62 } else if (vInstance == 2.0) { 63 transform[2][0] = 0.5; 64 } else if (vInstance == 3.0) { 65 transform[2][1] = 0.5; 66 } else if (vInstance == 4.0) { 67 transform[2][0] = 0.5; 68 transform[2][1] = 0.5; 69 } 70 gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1); 71 } 72 </script> 73 <script id="vshaderEmulatedDrawID" type="x-shader/x-vertex"> 74 uniform int drawID; 75 attribute vec2 vPosition; 76 attribute float vInstance; 77 varying vec4 color; 78 void main() 79 { 80 float quad_id = float(drawID / 2); 81 float color_id = quad_id - (3.0 * floor(quad_id / 3.0)); 82 if (color_id == 0.0) { 83 color = vec4(1, 0, 0, 1); 84 } else if (color_id == 1.0) { 85 color = vec4(0, 1, 0, 1); 86 } else { 87 color = vec4(0, 0, 1, 1); 88 } 89 mat3 transform = mat3(1.0); 90 // vInstance starts at 1.0 on instanced calls 91 if (vInstance >= 1.0) { 92 transform[0][0] = 0.5; 93 transform[1][1] = 0.5; 94 } 95 if (vInstance == 1.0) { 96 } else if (vInstance == 2.0) { 97 transform[2][0] = 0.5; 98 } else if (vInstance == 3.0) { 99 transform[2][1] = 0.5; 100 } else if (vInstance == 4.0) { 101 transform[2][0] = 0.5; 102 transform[2][1] = 0.5; 103 } 104 gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1); 105 } 106 </script> 107 <script id="vshaderNoDrawID" type="x-shader/x-vertex"> 108 attribute vec2 vPosition; 109 attribute float vInstance; 110 varying vec4 color; 111 void main() 112 { 113 color = vec4(1.0, 0.0, 0.0, 1.0); 114 mat3 transform = mat3(1.0); 115 // vInstance starts at 1.0 on instanced calls 116 if (vInstance >= 1.0) { 117 transform[0][0] = 0.5; 118 transform[1][1] = 0.5; 119 } 120 if (vInstance == 1.0) { 121 } else if (vInstance == 2.0) { 122 transform[2][0] = 0.5; 123 } else if (vInstance == 3.0) { 124 transform[2][1] = 0.5; 125 } else if (vInstance == 4.0) { 126 transform[2][0] = 0.5; 127 transform[2][1] = 0.5; 128 } 129 gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1); 130 } 131 </script> 132 <script id="fshader" type="x-shader/x-fragment"> 133 precision mediump float; 134 varying vec4 color; 135 void main() { 136 gl_FragColor = color; 137 } 138 </script> 139 <div id="description"></div> 140 <canvas id="canvas" width="128" height="128"> </canvas> 141 <div id="console"></div> 142 143 <script> 144 "use strict"; 145 description("This test verifies the functionality of the ANGLE_multi_draw extension, if it is available."); 146 147 const wtu = WebGLTestUtils; 148 const canvas = document.getElementById("canvas"); 149 const gl = wtu.create3DContext(canvas); 150 const instancedExt = gl && gl.getExtension('ANGLE_instanced_arrays'); 151 const bufferUsageSet = [ gl.STATIC_DRAW, gl.DYNAMIC_DRAW ]; 152 153 // Check if the extension is either both enabled and supported or 154 // not enabled and not supported. 155 function runSupportedTest(extensionName, extensionEnabled) { 156 const supported = gl.getSupportedExtensions(); 157 if (supported.indexOf(extensionName) >= 0) { 158 if (extensionEnabled) { 159 testPassed(extensionName + ' listed as supported and getExtension succeeded'); 160 return true; 161 } else { 162 testFailed(extensionName + ' listed as supported but getExtension failed'); 163 } 164 } else { 165 if (extensionEnabled) { 166 testFailed(extensionName + ' not listed as supported but getExtension succeeded'); 167 } else { 168 testPassed(extensionName + ' not listed as supported and getExtension failed -- this is legal'); 169 } 170 } 171 return false; 172 } 173 174 function runTest() { 175 if (!gl) { 176 return function() { 177 testFailed('WebGL context does not exist'); 178 } 179 } 180 181 const extensionName = 'WEBGL_multi_draw'; 182 const ext = gl.getExtension(extensionName); 183 if (!runSupportedTest(extensionName, ext)) { 184 return; 185 } 186 187 doTest(ext, false); 188 doTest(ext, true); 189 } 190 191 function doTest(ext, instanced) { 192 193 function runValidationTests(bufferUsage) { 194 const vertexBuffer = gl.createBuffer(); 195 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 196 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.2,0.2, 0.8,0.2, 0.5,0.8 ]), bufferUsage); 197 198 const indexBuffer = gl.createBuffer(); 199 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 200 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([ 0, 1, 2, 0]), bufferUsage); 201 202 const instanceBuffer = gl.createBuffer(); 203 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); 204 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 2, 3 ]), bufferUsage); 205 206 const program = wtu.setupProgram(gl, ["vshaderNoDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); 207 expectTrue(program != null, "can compile simple program"); 208 209 function setupDrawArrays() { 210 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 211 gl.enableVertexAttribArray(0); 212 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 213 } 214 215 function setupDrawElements() { 216 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 217 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 218 gl.enableVertexAttribArray(0); 219 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 220 } 221 222 function setupInstanced() { 223 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); 224 gl.enableVertexAttribArray(1); 225 gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); 226 if (wtu.getDefault3DContextVersion() < 2) { 227 instancedExt.vertexAttribDivisorANGLE(1, 1); 228 } else { 229 gl.vertexAttribDivisor(1, 1); 230 } 231 } 232 233 function setupDrawArraysInstanced() { 234 setupDrawArrays(); 235 setupInstanced(); 236 } 237 238 function setupDrawElementsInstanced() { 239 setupDrawElements(); 240 setupInstanced(); 241 } 242 243 // Wrap a draw call in a function to setup the draw call, execute, 244 // and check errors. 245 // The `drawFunc` is one of the extension entrypoints being tested. It may 246 // be undefined if that entrypoint is not supported on the context 247 function makeDrawCheck(drawFunc, setup) { 248 if (!drawFunc) { 249 return function() {}; 250 } 251 return function(f_args, expect, msg) { 252 setup(); 253 drawFunc.apply(ext, f_args); 254 wtu.glErrorShouldBe(gl, expect, drawFunc.name + " " + msg); 255 gl.disableVertexAttribArray(0); 256 gl.disableVertexAttribArray(1); 257 } 258 } 259 260 const checkMultiDrawArrays = makeDrawCheck( 261 ext.multiDrawArraysWEBGL, setupDrawArrays); 262 const checkMultiDrawElements = makeDrawCheck( 263 ext.multiDrawElementsWEBGL, setupDrawElements); 264 const checkMultiDrawArraysInstanced = makeDrawCheck( 265 ext.multiDrawArraysInstancedWEBGL, setupDrawArraysInstanced); 266 const checkMultiDrawElementsInstanced = makeDrawCheck( 267 ext.multiDrawElementsInstancedWEBGL, setupDrawElementsInstanced); 268 269 gl.useProgram(program); 270 271 // Check that drawing a single triangle works 272 if (!instanced) { 273 checkMultiDrawArrays( 274 [gl.TRIANGLES, [0], 0, [3], 0, 1], 275 gl.NO_ERROR, "with gl.TRIANGLES"); 276 checkMultiDrawElements( 277 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, 1], 278 gl.NO_ERROR, "with gl.TRIANGLES"); 279 } else { 280 checkMultiDrawElementsInstanced( 281 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 1], 282 gl.NO_ERROR, "with gl.TRIANGLES"); 283 checkMultiDrawArraysInstanced( 284 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, 1], 285 gl.NO_ERROR, "with gl.TRIANGLES"); 286 } 287 288 // Zero drawcount permitted 289 if (!instanced) { 290 checkMultiDrawArrays( 291 [gl.TRIANGLES, [0], 0, [3], 0, 0], 292 gl.NO_ERROR, "with drawcount == 0"); 293 checkMultiDrawElements( 294 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, 0], 295 gl.NO_ERROR, "with drawcount == 0"); 296 } else { 297 checkMultiDrawElementsInstanced( 298 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 0], 299 gl.NO_ERROR, "with drawcount == 0"); 300 checkMultiDrawArraysInstanced( 301 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, 0], 302 gl.NO_ERROR, "with drawcount == 0"); 303 } 304 305 // Check negative drawcount 306 if (!instanced) { 307 checkMultiDrawArrays( 308 [gl.TRIANGLES, [0], 0, [3], 0, -1], 309 gl.INVALID_VALUE, "with drawcount < 0"); 310 checkMultiDrawElements( 311 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, -1], 312 gl.INVALID_VALUE, "with drawcount < 0"); 313 } else { 314 checkMultiDrawElementsInstanced( 315 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, -1], 316 gl.INVALID_VALUE, "with drawcount < 0"); 317 checkMultiDrawArraysInstanced( 318 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, -1], 319 gl.INVALID_VALUE, "with drawcount < 0"); 320 } 321 322 // Check offsets greater than array length 323 if (!instanced) { 324 checkMultiDrawArrays( 325 [gl.TRIANGLES, [0], 1, [3], 0, 1], 326 gl.INVALID_OPERATION, "with firstsStart >= firstsList.length"); 327 checkMultiDrawArrays( 328 [gl.TRIANGLES, [0], 0, [3], 1, 1], 329 gl.INVALID_OPERATION, "with countsStart >= countsList.length"); 330 331 checkMultiDrawElements( 332 [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, 1], 333 gl.INVALID_OPERATION, "with countsStart >= countsList.length"); 334 checkMultiDrawElements( 335 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, 1], 336 gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length"); 337 } else { 338 checkMultiDrawArraysInstanced( 339 [gl.TRIANGLES, [0], 1, [3], 0, [1], 0, 1], 340 gl.INVALID_OPERATION, "with firstsStart >= firstsList.length"); 341 checkMultiDrawArraysInstanced( 342 [gl.TRIANGLES, [0], 0, [3], 1, [1], 0, 1], 343 gl.INVALID_OPERATION, "with countsStart >= countsList.length"); 344 checkMultiDrawArraysInstanced( 345 [gl.TRIANGLES, [0], 0, [3], 0, [1], 1, 1], 346 gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length"); 347 348 checkMultiDrawElementsInstanced( 349 [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 1], 350 gl.INVALID_OPERATION, "with countsStart >= countsList.length"); 351 checkMultiDrawElementsInstanced( 352 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, [1], 0, 1], 353 gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length"); 354 checkMultiDrawElementsInstanced( 355 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 1, 1], 356 gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length"); 357 } 358 } 359 360 function runShaderTests(bufferUsage) { 361 const illegalProgram = wtu.setupProgram(gl, ["vshaderIllegalDrawID", "fshader"], ["vPosition"], [0]); 362 expectTrue(illegalProgram == null, "cannot compile program with gl_DrawID but no extension directive"); 363 364 const drawIDProgram = wtu.setupProgram(gl, ["vshaderDrawIDZero", "fshader"], ["vPosition"], [0]); 365 wtu.setupProgram(gl, ["vshaderDrawIDZero", "fshader"], ["vPosition"], [0]); 366 expectTrue(drawIDProgram !== null, "can compile program with gl_DrawID"); 367 gl.useProgram(drawIDProgram); 368 gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 369 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0,1, 0,1, 1,0, 1,1 ]), bufferUsage); 370 gl.enableVertexAttribArray(0); 371 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 372 gl.drawArrays(gl.TRIANGLES, 0, 6); 373 wtu.checkCanvas(gl, [0, 255, 0, 255], "gl_DrawID is 0 for non-Multi* draw calls", 0); 374 } 375 376 function runPixelTests(bufferUsage, useSharedArrayBuffer) { 377 // An array of quads is tiled across the screen. 378 // gl_DrawID is checked by using it to select the color of the draw. 379 // Instanced entrypoints are tested here scaling and then instancing the 380 // array of quads over four quadrants on the screen. 381 382 // These tests also include "manyDraw" tests which emulate a multiDraw with 383 // a Javascript for-loop and gl_DrawID with a uniform constiable. They are 384 // included to ensure the test is written correctly. 385 386 const width = gl.canvas.width; 387 const height = gl.canvas.height; 388 const x_count = 8; 389 const y_count = 8; 390 const quad_count = x_count * y_count; 391 const tri_count = quad_count * 2; 392 const tileSize = [ 1/x_count, 1/y_count ]; 393 const tilePixelSize = [ Math.floor(width / x_count), Math.floor(height / y_count) ]; 394 const quadRadius = [ 0.25 * tileSize[0], 0.25 * tileSize[1] ]; 395 const pixelCheckSize = [ Math.floor(quadRadius[0] * width), Math.floor(quadRadius[1] * height) ]; 396 397 function getTileCenter(x, y) { 398 return [ tileSize[0] * (0.5 + x), tileSize[1] * (0.5 + y) ]; 399 } 400 401 function getQuadVertices(x, y) { 402 const center = getTileCenter(x, y); 403 return [ 404 [center[0] - quadRadius[0], center[1] - quadRadius[1], 0], 405 [center[0] + quadRadius[0], center[1] - quadRadius[1], 0], 406 [center[0] + quadRadius[0], center[1] + quadRadius[1], 0], 407 [center[0] - quadRadius[0], center[1] + quadRadius[1], 0], 408 ] 409 } 410 411 const indicesData = []; 412 const verticesData = []; 413 const nonIndexedVerticesData = []; 414 { 415 const is = new Uint16Array([0, 1, 2, 0, 2, 3]); 416 for (let y = 0; y < y_count; ++y) { 417 for (let x = 0; x < x_count; ++x) { 418 const quadIndex = y * x_count + x; 419 const starting_index = 4 * quadIndex; 420 const vs = getQuadVertices(x, y); 421 for (let i = 0; i < is.length; ++i) { 422 indicesData.push(starting_index + is[i]); 423 } 424 for (let i = 0; i < vs.length; ++i) { 425 for (let v = 0; v < vs[i].length; ++v) verticesData.push(vs[i][v]); 426 } 427 for (let i = 0; i < is.length; ++i) { 428 for (let v = 0; v < vs[is[i]].length; ++v) nonIndexedVerticesData.push(vs[is[i]][v]); 429 } 430 } 431 } 432 } 433 434 const indices = new Uint16Array(indicesData); 435 const vertices = new Float32Array(verticesData); 436 const nonIndexedVertices = new Float32Array(nonIndexedVerticesData); 437 438 const indexBuffer = gl.createBuffer(); 439 const vertexBuffer = gl.createBuffer(); 440 const nonIndexedVertexBuffer = gl.createBuffer(); 441 const instanceBuffer = gl.createBuffer(); 442 443 gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer); 444 gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage); 445 446 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 447 gl.bufferData(gl.ARRAY_BUFFER, vertices, bufferUsage); 448 449 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 450 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, bufferUsage); 451 452 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); 453 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), bufferUsage); 454 455 function checkResult(config, msg) { 456 const rects = []; 457 const expected = [ 458 [255, 0, 0, 255], 459 [0, 255, 0, 255], 460 [0, 0, 255, 255], 461 ]; 462 for (let y = 0; y < y_count; ++y) { 463 for (let x = 0; x < x_count; ++x) { 464 const center_x = x * tilePixelSize[0] + Math.floor(tilePixelSize[0] / 2); 465 const center_y = y * tilePixelSize[1] + Math.floor(tilePixelSize[1] / 2); 466 const quadID = y * x_count + x; 467 const colorID = config.drawID ? quadID % 3 : 0; 468 if (config.instanced) { 469 rects.push(wtu.makeCheckRect( 470 center_x / 2 - Math.floor(pixelCheckSize[0] / 4), 471 center_y / 2 - Math.floor(pixelCheckSize[1] / 4), 472 pixelCheckSize[0] / 2, 473 pixelCheckSize[1] / 2, 474 expected[colorID], 475 msg + " (" + x + "," + y + ")", 0)); 476 rects.push(wtu.makeCheckRect( 477 center_x / 2 - Math.floor(pixelCheckSize[0] / 4) + width / 2, 478 center_y / 2 - Math.floor(pixelCheckSize[1] / 4), 479 pixelCheckSize[0] / 2, 480 pixelCheckSize[1] / 2, 481 expected[colorID], 482 msg + " (" + x + "," + y + ")", 0)); 483 rects.push(wtu.makeCheckRect( 484 center_x / 2 - Math.floor(pixelCheckSize[0] / 4), 485 center_y / 2 - Math.floor(pixelCheckSize[1] / 4) + height / 2, 486 pixelCheckSize[0] / 2, 487 pixelCheckSize[1] / 2, 488 expected[colorID], 489 msg + " (" + x + "," + y + ")", 0)); 490 rects.push(wtu.makeCheckRect( 491 center_x / 2 - Math.floor(pixelCheckSize[0] / 4) + width / 2, 492 center_y / 2 - Math.floor(pixelCheckSize[1] / 4) + height / 2, 493 pixelCheckSize[0] / 2, 494 pixelCheckSize[1] / 2, 495 expected[colorID], 496 msg + " (" + x + "," + y + ")", 0)); 497 } else { 498 rects.push(wtu.makeCheckRect( 499 center_x - Math.floor(pixelCheckSize[0] / 2), 500 center_y - Math.floor(pixelCheckSize[1] / 2), 501 pixelCheckSize[0], 502 pixelCheckSize[1], 503 expected[colorID], 504 msg + " (" + x + "," + y + ")", 0)); 505 } 506 } 507 } 508 wtu.checkCanvasRects(gl, rects); 509 } 510 511 function newIntArray(count) { 512 if (!useSharedArrayBuffer) { 513 return new Int32Array(count); 514 } 515 let sab = new SharedArrayBuffer(count * Int32Array.BYTES_PER_ELEMENT); 516 return new Int32Array(sab); 517 } 518 519 const firsts = newIntArray(tri_count); 520 const counts = newIntArray(tri_count); 521 const offsets = newIntArray(tri_count); 522 const instances = newIntArray(tri_count); 523 524 for (let i = 0; i < firsts.length; ++i) firsts[i] = i * 3; 525 counts.fill(3); 526 for (let i = 0; i < offsets.length; ++i) offsets[i] = i * 3 * 2; 527 instances.fill(4); 528 529 const firstsOffset = 47; 530 const countsOffset = firstsOffset + firsts.length; 531 const offsetsOffset = countsOffset + counts.length; 532 const instancesOffset = offsetsOffset + instances.length; 533 534 const buffer = newIntArray(firstsOffset + firsts.length + counts.length + offsets.length + instances.length); 535 buffer.set(firsts, firstsOffset); 536 buffer.set(counts, countsOffset); 537 buffer.set(offsets, offsetsOffset); 538 buffer.set(instances, instancesOffset); 539 540 let drawIDLocation; 541 542 const multiDrawArrays = function() { 543 ext.multiDrawArraysWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, tri_count); 544 } 545 546 const multiDrawArraysWithNonzeroOffsets = function() { 547 ext.multiDrawArraysWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, tri_count); 548 } 549 550 const multiDrawElements = function() { 551 ext.multiDrawElementsWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, tri_count); 552 } 553 554 const multiDrawElementsWithNonzeroOffsets = function() { 555 ext.multiDrawElementsWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, tri_count); 556 } 557 558 const multiDrawArraysInstanced = function() { 559 ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, instances, 0, tri_count); 560 } 561 562 const multiDrawArraysInstancedWithNonzeroOffsets = function() { 563 ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, buffer, instancesOffset, tri_count); 564 } 565 566 const multiDrawElementsInstanced = function() { 567 ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, instances, 0, tri_count); 568 } 569 570 const multiDrawElementsInstancedWithNonzeroOffsets = function() { 571 ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, buffer, instancesOffset, tri_count); 572 } 573 574 const manyDrawArrays = function() { 575 for (let i = 0; i < tri_count; ++i) { 576 gl.drawArrays(gl.TRIANGLES, firsts[i], counts[i]); 577 } 578 } 579 580 const manyDrawElements = function() { 581 for (let i = 0; i < tri_count; ++i) { 582 gl.drawElements(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i]); 583 } 584 } 585 586 const manyDrawArraysEmulateDrawID = function() { 587 for (let i = 0; i < tri_count; ++i) { 588 gl.uniform1i(drawIDLocation, i); 589 gl.drawArrays(gl.TRIANGLES, firsts[i], counts[i]); 590 } 591 } 592 593 const manyDrawElementsEmulateDrawID = function() { 594 for (let i = 0; i < tri_count; ++i) { 595 gl.uniform1i(drawIDLocation, i); 596 gl.drawElements(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i]); 597 } 598 } 599 600 function drawArraysInstanced() { 601 if (wtu.getDefault3DContextVersion() < 2) { 602 instancedExt.drawArraysInstancedANGLE.apply(instancedExt, arguments); 603 } else { 604 gl.drawArraysInstanced.apply(gl, arguments); 605 } 606 } 607 608 function drawElementsInstanced() { 609 if (wtu.getDefault3DContextVersion() < 2) { 610 instancedExt.drawElementsInstancedANGLE.apply(instancedExt, arguments); 611 } else { 612 gl.drawElementsInstanced.apply(gl, arguments); 613 } 614 } 615 616 function vertexAttribDivisor(attrib, divisor) { 617 if (wtu.getDefault3DContextVersion() < 2) { 618 instancedExt.vertexAttribDivisorANGLE(attrib, divisor); 619 } else { 620 gl.vertexAttribDivisor(attrib, divisor); 621 } 622 } 623 624 const manyDrawArraysInstanced = function() { 625 for (let i = 0; i < tri_count; ++i) { 626 drawArraysInstanced(gl.TRIANGLES, firsts[i], counts[i], 4); 627 } 628 } 629 630 const manyDrawElementsInstanced = function() { 631 for (let i = 0; i < tri_count; ++i) { 632 drawElementsInstanced(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i], 4); 633 } 634 } 635 636 const manyDrawArraysInstancedEmulateDrawID = function() { 637 for (let i = 0; i < tri_count; ++i) { 638 gl.uniform1i(drawIDLocation, i); 639 drawArraysInstanced(gl.TRIANGLES, firsts[i], counts[i], 4); 640 } 641 } 642 643 const manyDrawElementsInstancedEmulateDrawID = function() { 644 for (let i = 0; i < tri_count; ++i) { 645 gl.uniform1i(drawIDLocation, i); 646 drawElementsInstanced(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i], 4); 647 } 648 } 649 650 function checkDraw(config) { 651 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 652 653 if (config.indexed) { 654 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 655 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 656 gl.enableVertexAttribArray(0); 657 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); 658 } else { 659 gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer); 660 gl.enableVertexAttribArray(0); 661 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); 662 } 663 664 if (config.instanced) { 665 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); 666 gl.enableVertexAttribArray(1); 667 gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); 668 vertexAttribDivisor(1, 1); 669 } 670 671 config.drawFunc(); 672 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); 673 checkResult(config, config.drawFunc.name + ( 674 config.instanced ? ' instanced' : '' 675 ) + ( 676 config.drawID ? ' with gl_DrawID' : '' 677 ) + ( 678 useSharedArrayBuffer ? ' and SharedArrayBuffer' : '' 679 )); 680 681 gl.disableVertexAttribArray(0); 682 gl.disableVertexAttribArray(1); 683 } 684 685 const noDrawIDProgram = wtu.setupProgram(gl, ["vshaderNoDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); 686 expectTrue(noDrawIDProgram != null, "can compile simple program"); 687 if (noDrawIDProgram) { 688 gl.useProgram(noDrawIDProgram); 689 690 if (!instanced) { 691 checkDraw({ 692 drawFunc: multiDrawArrays, 693 drawID: false, 694 }); 695 checkDraw({ 696 drawFunc: multiDrawArraysWithNonzeroOffsets, 697 drawID: false, 698 }); 699 checkDraw({ 700 drawFunc: multiDrawElements, 701 indexed: true, 702 drawID: false, 703 }); 704 checkDraw({ 705 drawFunc: multiDrawElementsWithNonzeroOffsets, 706 indexed: true, 707 drawID: false, 708 }); 709 checkDraw({ 710 drawFunc: manyDrawArrays, 711 drawID: false, 712 }); 713 checkDraw({ 714 drawFunc: manyDrawElements, 715 indexed: true, 716 drawID: false, 717 }); 718 } else { 719 checkDraw({ 720 drawFunc: multiDrawArraysInstanced, 721 drawID: false, 722 instanced: true, 723 }); 724 checkDraw({ 725 drawFunc: multiDrawArraysInstancedWithNonzeroOffsets, 726 drawID: false, 727 instanced: true, 728 }); 729 checkDraw({ 730 drawFunc: multiDrawElementsInstanced, 731 indexed: true, 732 drawID: false, 733 instanced: true, 734 }); 735 checkDraw({ 736 drawFunc: multiDrawElementsInstancedWithNonzeroOffsets, 737 indexed: true, 738 drawID: false, 739 instanced: true, 740 }); 741 checkDraw({ 742 drawFunc: manyDrawArraysInstanced, 743 drawID: false, 744 instanced: true, 745 }); 746 checkDraw({ 747 drawFunc: manyDrawElementsInstanced, 748 indexed: true, 749 drawID: false, 750 instanced: true, 751 }); 752 } 753 } 754 755 const withDrawIDProgram = wtu.setupProgram(gl, ["vshaderWithDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); 756 expectTrue(withDrawIDProgram != null, "can compile program with ANGLE_multi_draw"); 757 if (withDrawIDProgram) { 758 gl.useProgram(withDrawIDProgram); 759 760 if (!instanced) { 761 checkDraw({ 762 drawFunc: multiDrawArrays, 763 drawID: true, 764 }); 765 checkDraw({ 766 drawFunc: multiDrawArraysWithNonzeroOffsets, 767 drawID: true, 768 }); 769 checkDraw({ 770 drawFunc: multiDrawElements, 771 indexed: true, 772 drawID: true, 773 }); 774 checkDraw({ 775 drawFunc: multiDrawElementsWithNonzeroOffsets, 776 indexed: true, 777 drawID: true, 778 }); 779 } else { 780 checkDraw({ 781 drawFunc: multiDrawArraysInstanced, 782 drawID: true, 783 instanced: true, 784 }); 785 checkDraw({ 786 drawFunc: multiDrawArraysInstancedWithNonzeroOffsets, 787 drawID: true, 788 instanced: true, 789 }); 790 checkDraw({ 791 drawFunc: multiDrawElementsInstanced, 792 indexed: true, 793 drawID: true, 794 instanced: true, 795 }); 796 checkDraw({ 797 drawFunc: multiDrawElementsInstancedWithNonzeroOffsets, 798 indexed: true, 799 drawID: true, 800 instanced: true, 801 }); 802 } 803 } 804 805 const emulatedDrawIDProgram = wtu.setupProgram(gl, ["vshaderEmulatedDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); 806 expectTrue(emulatedDrawIDProgram != null, "can compile program to emulate gl_DrawID"); 807 drawIDLocation = gl.getUniformLocation(emulatedDrawIDProgram, "drawID"); 808 if (emulatedDrawIDProgram) { 809 gl.useProgram(emulatedDrawIDProgram); 810 811 if (!instanced) { 812 checkDraw({ 813 drawFunc: manyDrawArraysEmulateDrawID, 814 drawID: true, 815 }); 816 checkDraw({ 817 drawFunc: manyDrawElementsEmulateDrawID, 818 indexed: true, 819 drawID: true, 820 }); 821 } else { 822 checkDraw({ 823 drawFunc: manyDrawArraysInstancedEmulateDrawID, 824 drawID: true, 825 instanced: true, 826 }); 827 checkDraw({ 828 drawFunc: manyDrawElementsInstancedEmulateDrawID, 829 indexed: true, 830 drawID: true, 831 instanced: true, 832 }); 833 } 834 } 835 } 836 837 for (let i = 0; i < bufferUsageSet.length; i++) { 838 let bufferUsage = bufferUsageSet[i]; 839 debug("Testing with BufferUsage = " + bufferUsage); 840 runValidationTests(bufferUsage); 841 runShaderTests(bufferUsage); 842 runPixelTests(bufferUsage, false); 843 } 844 845 // Run a subset of the pixel tests with SharedArrayBuffer if supported. 846 if (window.SharedArrayBuffer) { 847 runPixelTests(bufferUsageSet[0], true); 848 } 849 } 850 851 function waitForComposite() { 852 debug('wait for composite'); 853 return new Promise(resolve => wtu.waitForComposite(resolve)); 854 } 855 856 async function testPreserveDrawingBufferFalse(gl, drawFn) { 857 debug(''); 858 debug('test preserveDrawingBuffer: false'); 859 860 if (drawFn(gl)) { 861 debug('skipped: extension does not exist'); 862 return; 863 } 864 865 wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255], 866 "canvas should be red"); 867 868 // enable scissor here, before compositing, to make sure it's correctly 869 // ignored and restored 870 gl.scissor(0, 10, 10, 10); 871 gl.enable(gl.SCISSOR_TEST); 872 873 await waitForComposite(); 874 875 // scissor was set earlier 876 gl.clearColor(0, 0, 1, 1); 877 gl.clear(gl.COLOR_BUFFER_BIT); 878 879 wtu.checkCanvasRect(gl, 0, 10, 10, 10, [0, 0, 255, 255], 880 "cleared corner should be blue, stencil should be preserved"); 881 wtu.checkCanvasRect(gl, 0, 0, 10, 10, [0, 0, 0, 0], 882 "remainder of buffer should be cleared"); 883 884 gl.disable(gl.SCISSOR_TEST); 885 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); 886 } 887 888 async function testPreserveDrawingBufferTrue(gl, drawFn) { 889 debug(''); 890 debug('test preserveDrawingBuffer: true'); 891 if (drawFn(gl)) { 892 debug('skipped: extension does not exist'); 893 return; 894 } 895 896 wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255], 897 "canvas should be red"); 898 899 await waitForComposite(); 900 901 wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255], 902 "canvas should be red"); 903 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); 904 } 905 906 async function doCompositingTests([glPreserveDrawingBufferFalse, glPreserveDrawingBufferTrue], drawFn) { 907 debug(''); 908 debug(drawFn.name); 909 910 await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn); 911 await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn); 912 } 913 914 async function runDrawTests(testFn) { 915 function drawArrays(gl) { 916 gl.drawArrays(gl.TRIANGLES, 0, 6); 917 } 918 919 function drawElements(gl) { 920 gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); 921 } 922 923 function drawArraysInstanced(gl) { 924 gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 1); 925 } 926 927 function drawElementsInstanced(gl) { 928 gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1); 929 } 930 931 function multiDrawArraysWEBGL(gl) { 932 const ext = gl.getExtension('WEBGL_multi_draw'); 933 if (!ext) { 934 throw 'Should not have run this test without WEBGL_multi_draw'; 935 } 936 937 ext.multiDrawArraysWEBGL( 938 gl.TRIANGLES, 939 [0], 0, // firsts 940 [6], 0, // counts 941 1, // drawCount 942 ); 943 } 944 945 function multiDrawElementsWEBGL(gl) { 946 const ext = gl.getExtension('WEBGL_multi_draw'); 947 if (!ext) { 948 throw 'Should not have run this test without WEBGL_multi_draw'; 949 } 950 951 ext.multiDrawElementsWEBGL( 952 gl.TRIANGLES, 953 [6], 0, // counts 954 gl.UNSIGNED_BYTE, 955 [0], 0, // offsets 956 1, // drawCount 957 ); 958 } 959 960 function multiDrawArraysInstancedWEBGL(gl) { 961 const ext = gl.getExtension('WEBGL_multi_draw'); 962 if (!ext) { 963 throw 'Should not have run this test without WEBGL_multi_draw'; 964 } 965 ext.multiDrawArraysInstancedWEBGL( 966 gl.TRIANGLES, 967 [0], 0, // firsts 968 [6], 0, // counts 969 [1], 0, // instances 970 1, // drawCount 971 ); 972 } 973 974 function multiDrawElementsInstancedWEBGL(gl) { 975 const ext = gl.getExtension('WEBGL_multi_draw'); 976 if (!ext) { 977 throw 'Should not have run this test without WEBGL_multi_draw'; 978 } 979 ext.multiDrawElementsInstancedWEBGL( 980 gl.TRIANGLES, 981 [6], 0, // counts 982 gl.UNSIGNED_BYTE, 983 [0], 0, // offsets 984 [1], 0, // instances 985 1, // drawCount 986 ); 987 } 988 989 await testFn(drawArrays); // sanity check 990 await testFn(drawElements); // sanity check 991 992 // It's only legal to call testFn if the extension is supported, 993 // since the invalid vertex attrib tests, in particular, expect the 994 // draw function to have an effect. 995 if (gl.getExtension('WEBGL_multi_draw')) { 996 await testFn(multiDrawArraysWEBGL); 997 await testFn(multiDrawElementsWEBGL); 998 await testFn(multiDrawArraysInstancedWEBGL); 999 await testFn(multiDrawElementsInstancedWEBGL); 1000 } 1001 } 1002 1003 async function runCompositingTests() { 1004 const compositingTestFn = createCompositingTestFn({ 1005 webglVersion: 1, 1006 shadersFn(gl) { 1007 const vs = `\ 1008 //#extension GL_ANGLE_multi_draw : enable 1009 attribute vec4 position; 1010 void main() { 1011 gl_Position = position; 1012 } 1013 `; 1014 const fs = `\ 1015 precision mediump float; 1016 void main() { 1017 gl_FragColor = vec4(1, 0, 0, 1); 1018 } 1019 `; 1020 return [vs, fs]; 1021 }, 1022 }); 1023 await runDrawTests(compositingTestFn); 1024 } 1025 1026 async function runInvalidAttribTests(gl) { 1027 const invalidAttribTestFn = createInvalidAttribTestFn(gl); 1028 await runDrawTests(invalidAttribTestFn); 1029 } 1030 1031 function testSideEffects() { 1032 debug("") 1033 debug("Testing that ANGLE_instanced_arrays is implicitly enabled") 1034 const canvas = document.createElement("canvas"); 1035 const gl = wtu.create3DContext(canvas, undefined, 1); 1036 if (!gl) { 1037 testFailed('WebGL context creation failed'); 1038 return; 1039 } 1040 gl.enableVertexAttribArray(0); 1041 const VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE; 1042 gl.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); 1043 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "divisor enum unknown"); 1044 const ext = gl.getExtension("WEBGL_multi_draw"); 1045 if (ext) { 1046 debug("WEBGL_multi_draw enabled"); 1047 gl.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); 1048 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "divisor enum known"); 1049 } 1050 } 1051 1052 async function main() { 1053 runTest(); 1054 testSideEffects(); 1055 await runInvalidAttribTests(gl); 1056 await runCompositingTests(); 1057 finishTest(); 1058 } 1059 main(); 1060 1061 var successfullyParsed = true; 1062 </script> 1063 </body> 1064 </html>