webgl-multi-draw-instanced-base-vertex-base-instance.html (35571B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"> 5 <title>WebGL ANGLE_base_vertex_base_instance 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="vshaderBaseInstanceWithoutExt" type="x-shader/x-vertex">#version 300 es 15 layout(location = 0) in vec2 vPosition; 16 out 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_BaseInstance, 1); 21 } 22 </script> 23 <!-- Check gl_InstanceID starts at 0 regardless of gl_BaseInstance --> 24 <script id="vshaderInstanceIDCheck" type="x-shader/x-vertex">#version 300 es 25 layout(location = 0) in vec2 vPosition; 26 out vec4 color; 27 void main() 28 { 29 if (gl_InstanceID == 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 <script id="vshaderBaseVertexWithoutExt" type="x-shader/x-vertex">#version 300 es 38 layout(location = 0) in vec2 vPosition; 39 out vec4 color; 40 void main() 41 { 42 color = vec4(1.0, 0.0, 0.0, 1.0); 43 gl_Position = vec4(vPosition * 2.0 - 1.0, gl_BaseVertex, 1); 44 } 45 </script> 46 <script id="vshaderWithExt" type="x-shader/x-vertex">#version 300 es 47 #extension GL_ANGLE_base_vertex_base_instance : require 48 layout(location = 0) in vec2 vPosition; 49 out vec4 color; 50 void main() 51 { 52 color = vec4(0, 1, 0, 1); 53 gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1); 54 } 55 </script> 56 <!-- Check gl_VertexID starts at gl_BaseVertex --> 57 <script id="vshaderVertexIDCheck" type="x-shader/x-vertex">#version 300 es 58 layout(location = 0) in vec2 vPosition; 59 out vec4 color; 60 void main() 61 { 62 if (gl_VertexID >= 3) { 63 color = vec4(0, 1, 0, 1); 64 } else { 65 color = vec4(1, 0, 0, 1); 66 } 67 gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1); 68 } 69 </script> 70 <script id="vshaderSimple" type="x-shader/x-vertex">#version 300 es 71 layout(location = 0) in vec2 vPosition; 72 layout(location = 1) in float vInstance; 73 out vec4 color; 74 void main() 75 { 76 if (vInstance <= 0.0) { 77 color = vec4(1.0, 0.0, 0.0, 1.0); 78 } else if (vInstance <= 1.0) { 79 color = vec4(0.0, 1.0, 0.0, 1.0); 80 } else if (vInstance <= 2.0) { 81 color = vec4(0.0, 0.0, 1.0, 1.0); 82 } else { 83 color = vec4(0.0, 0.0, 0.0, 1.0); 84 } 85 86 gl_Position = vec4(vec3(vPosition, 1.0) * 2.0 - 1.0, 1); 87 } 88 </script> 89 <script id="fshader" type="x-shader/x-fragment">#version 300 es 90 precision mediump float; 91 in vec4 color; 92 out vec4 oColor; 93 void main() { 94 oColor = color; 95 } 96 </script> 97 <div id="description"></div> 98 <canvas id="canvas" width="128" height="128"> </canvas> 99 <div id="console"></div> 100 101 <script> 102 "use strict"; 103 description("This test verifies the functionality of the WEBGL_[multi]_draw_basevertex_base_instance extension, if it is available."); 104 105 const wtu = WebGLTestUtils; 106 const canvas = document.getElementById("canvas"); 107 canvas.style.backgroundColor = '#000'; 108 canvas.style.imageRendering = 'pixelated'; // Because Chrome doesn't support crisp-edges. 109 canvas.style.imageRendering = 'crisp-edges'; 110 const attribs = { 111 antialias: false, 112 }; 113 const gl = wtu.create3DContext(canvas, attribs, 2); 114 115 const width = gl.canvas.width; 116 const height = gl.canvas.height; 117 const x_count = 8; 118 const y_count = 8; 119 const quad_count = x_count * y_count; 120 const tri_count = quad_count * 2; 121 const tileSize = [ 1/x_count, 1/y_count ]; 122 const tilePixelSize = [ Math.floor(width / x_count), Math.floor(height / y_count) ]; 123 const quadRadius = [ 0.25 * tileSize[0], 0.25 * tileSize[1] ]; 124 const pixelCheckSize = [ Math.floor(quadRadius[0] * width), Math.floor(quadRadius[1] * height) ]; 125 const bufferUsageSet = [ gl.STATIC_DRAW, gl.DYNAMIC_DRAW ]; 126 127 function getTileCenter(x, y) { 128 return [ tileSize[0] * (0.5 + x), tileSize[1] * (0.5 + y) ]; 129 } 130 131 function getQuadVertices(x, y) { 132 const center = getTileCenter(x, y); 133 return [ 134 [center[0] - quadRadius[0], center[1] - quadRadius[1], 0], 135 [center[0] + quadRadius[0], center[1] - quadRadius[1], 0], 136 [center[0] + quadRadius[0], center[1] + quadRadius[1], 0], 137 [center[0] - quadRadius[0], center[1] + quadRadius[1], 0], 138 ]; 139 } 140 141 const indicesData = []; 142 let verticesData = []; 143 let nonIndexedVerticesData = []; 144 const instanceIDsData = Array.from(Array(x_count).keys()); 145 const is = new Uint16Array([0, 1, 2, 0, 2, 3]); 146 // Rects in the same column are within a vertex array, testing gl_VertexID, gl_BaseVertex 147 // Rects in the same row are drawn by instancing, testing gl_InstanceID, gl_BaseInstance 148 for (let y = 0; y < y_count; ++y) { 149 // v3 ---- v2 150 // | | 151 // | | 152 // v0 ---- v1 153 154 // Get only one column of quad vertices as our geometry 155 // Rely on BaseInstance to duplicate on x axis 156 const vs = getQuadVertices(0, y); 157 158 for (let i = 0; i < vs.length; ++i) { 159 verticesData = verticesData.concat(vs[i]); 160 } 161 162 for (let i = 0; i < is.length; ++i) { 163 nonIndexedVerticesData = nonIndexedVerticesData.concat(vs[is[i]]); 164 } 165 } 166 167 // Build the indicesData used by drawElements* 168 for (let i = 0; i < y_count; ++i) { 169 let oi = 6 * i; 170 let ov = 4 * i; 171 for (let j = 0; j < is.length; ++j) { 172 indicesData[oi + j] = is[j] + ov; 173 } 174 } 175 176 const indices = new Uint16Array(indicesData); 177 const vertices = new Float32Array(verticesData); 178 const nonIndexedVertices = new Float32Array(nonIndexedVerticesData); 179 const instanceIDs = new Float32Array(instanceIDsData); 180 181 const indexBuffer = gl.createBuffer(); 182 const vertexBuffer = gl.createBuffer(); 183 const nonIndexedVertexBuffer = gl.createBuffer(); 184 const instanceIDBuffer = gl.createBuffer(); 185 186 const drawArraysDrawCount = x_count / 2; 187 let drawArraysParams = { 188 drawCount: drawArraysDrawCount, 189 firsts: new Uint32Array(drawArraysDrawCount).fill(0), 190 counts: new Uint32Array(drawArraysDrawCount).fill(y_count * 6), 191 instances: new Uint32Array(drawArraysDrawCount).fill(2), 192 baseInstances: new Uint32Array(drawArraysDrawCount) 193 }; 194 195 for (let i = 0; i < x_count / 2; ++i) { 196 drawArraysParams.baseInstances[i] = i * 2; 197 } 198 199 const drawElementsDrawCount = x_count * y_count / 2; 200 let drawElementsParams = { 201 drawCount: drawElementsDrawCount, 202 offsets: new Uint32Array(drawElementsDrawCount).fill(0), 203 counts: new Uint32Array(drawElementsDrawCount).fill(6), 204 instances: new Uint32Array(drawElementsDrawCount).fill(2), 205 baseVertices: new Uint32Array(drawElementsDrawCount), 206 baseInstances: new Uint32Array(drawElementsDrawCount) 207 }; 208 209 let b = 0; 210 for (let v = 0; v < y_count; ++v) { 211 for (let i = 0; i < x_count; i+=2) { 212 drawElementsParams.baseVertices[b] = v * 4; 213 drawElementsParams.baseInstances[b] = i; 214 ++b; 215 } 216 } 217 218 function setupGeneralBuffers(bufferUsage) { 219 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 220 gl.bufferData(gl.ARRAY_BUFFER, vertices, bufferUsage); 221 222 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 223 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, bufferUsage); 224 225 gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer); 226 gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage); 227 228 gl.bindBuffer(gl.ARRAY_BUFFER, instanceIDBuffer); 229 gl.bufferData(gl.ARRAY_BUFFER, instanceIDs, bufferUsage); 230 } 231 232 // Check if the extension is either both enabled and supported or 233 // not enabled and not supported. 234 function runSupportedTest(extensionName, extensionEnabled) { 235 const supported = gl.getSupportedExtensions(); 236 if (supported.indexOf(extensionName) >= 0) { 237 if (extensionEnabled) { 238 testPassed(extensionName + ' listed as supported and getExtension succeeded'); 239 return true; 240 } else { 241 testFailed(extensionName + ' listed as supported but getExtension failed'); 242 } 243 } else { 244 if (extensionEnabled) { 245 testFailed(extensionName + ' not listed as supported but getExtension succeeded'); 246 } else { 247 testPassed(extensionName + ' not listed as supported and getExtension failed -- this is legal'); 248 } 249 } 250 return false; 251 } 252 253 function runTest() { 254 if (!gl) { 255 return function() { 256 testFailed('WebGL context does not exist'); 257 } 258 } 259 260 doTest('WEBGL_draw_instanced_base_vertex_base_instance', false); 261 doTest('WEBGL_multi_draw_instanced_base_vertex_base_instance', true); 262 263 testGlslBuiltins(); 264 } 265 266 // - 267 268 function* range(n) { 269 for (let i = 0; i < n; i++) { 270 yield i; 271 } 272 } 273 274 function crossCombine(...args) { 275 function crossCombine2(listA, listB) { 276 const listC = []; 277 for (const a of listA) { 278 for (const b of listB) { 279 const c = Object.assign({}, a, b); 280 listC.push(c); 281 } 282 } 283 return listC; 284 } 285 286 let res = [{}]; 287 while (args.length) { 288 const next = args.shift(); 289 next[0].defined; 290 res = crossCombine2(res, next); 291 } 292 return res; 293 } 294 295 // - 296 297 const PASSTHROUGH_FRAG_SRC = `\ 298 #version 300 es 299 precision mediump float; 300 in vec4 v_color; 301 out vec4 o_color; 302 303 void main() { 304 o_color = v_color; 305 } 306 `; 307 308 function testGlslBuiltins() { 309 const EXT = gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance'); 310 311 const vertid_prog = (() => { 312 const vert_src = `\ 313 #version 300 es 314 #line 405 315 layout(location = 0) in int a_vertex_id; // Same as gl_VertexID 316 out vec4 v_color; 317 318 void main() { 319 gl_Position = vec4(0,0,0,1); 320 gl_PointSize = 1.0; 321 v_color = vec4(float(gl_VertexID), float(a_vertex_id),0,0); 322 v_color /= 255.0; 323 } 324 `; 325 const prog = wtu.setupProgram(gl, [vert_src, PASSTHROUGH_FRAG_SRC], 326 undefined, undefined, /*logShaders*/ true); 327 expectTrue(!!prog, `make_vertid_prog failed`); 328 return prog; 329 })(); 330 331 const instid_prog = (() => { 332 const vert_src = `\ 333 #version 300 es 334 #line 425 335 layout(location = 0) in int a_vertex_id; // Same as gl_VertexID 336 layout(location = 1) in int a_instance_div1; // Same as base_instance+gl_InstanceID 337 layout(location = 2) in int a_instance_div2; // Same as base_instance+floor(gl_InstanceID/2) 338 layout(location = 3) in int a_instance_div3; // Same as base_instance+floor(gl_InstanceID/3) 339 out vec4 v_color; 340 341 void main() { 342 gl_Position = vec4(0,0,0,1); 343 gl_PointSize = 1.0; 344 v_color = vec4(float(gl_InstanceID), float(a_instance_div1), 345 float(a_instance_div2), float(a_instance_div3)); 346 v_color /= 255.0; 347 } 348 `; 349 const prog = wtu.setupProgram(gl, [vert_src, PASSTHROUGH_FRAG_SRC], 350 undefined, undefined, /*logShaders*/ true); 351 expectTrue(!!prog, `make_instid_prog failed`); 352 return prog; 353 })(); 354 355 const COUNT_UP_DATA = new Int32Array(1000); 356 for (const i in COUNT_UP_DATA) { 357 COUNT_UP_DATA[i] = i; 358 } 359 360 const vertex_id_buf = gl.createBuffer(); 361 gl.bindBuffer(gl.ARRAY_BUFFER, vertex_id_buf); 362 gl.bufferData(gl.ARRAY_BUFFER, COUNT_UP_DATA, gl.STATIC_DRAW); 363 gl.enableVertexAttribArray(0); 364 gl.vertexAttribIPointer(0, 1, gl.INT, 0, 0); 365 366 gl.enableVertexAttribArray(1); 367 gl.vertexAttribIPointer(1, 1, gl.INT, 0, 0); 368 gl.vertexAttribDivisor(1, 1); 369 370 gl.enableVertexAttribArray(2); 371 gl.vertexAttribIPointer(2, 1, gl.INT, 0, 0); 372 gl.vertexAttribDivisor(2, 2); 373 374 gl.enableVertexAttribArray(3); 375 gl.vertexAttribIPointer(3, 1, gl.INT, 0, 0); 376 gl.vertexAttribDivisor(3, 3); 377 378 const index_buf = gl.createBuffer(); 379 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buf); 380 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, COUNT_UP_DATA, gl.STATIC_DRAW); 381 382 gl.canvas.width = gl.canvas.height = 1; 383 gl.canvas.style.width = gl.canvas.style.height = '1em'; 384 gl.viewport(0, 0, 1, 1); 385 386 const expect_pixel = (() => { 387 const was = new Uint8Array(4); 388 return (desc, subtest, expected) => { 389 gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, was); 390 if (!areArraysEqual(was, expected)) { 391 testFailed(`${subtest}: Expected [${expected}], was [${was}]. desc: ${JSON.stringify(desc)}`); 392 } else { 393 debug(`${subtest}: Was [${was}] as expected.`); 394 } 395 }; 396 })(); 397 398 // Common setup complete 399 // - 400 // Create testcases 401 402 const DRAW_FUNC_COMBINER = [{ 403 name: 'drawArraysInstanced', 404 draw: desc => { 405 if (desc.base_vert) return false; 406 if (desc.base_inst) return false; 407 gl.drawArraysInstanced(gl[desc.mode], desc.first_vert, 408 desc.vert_count, desc.inst_count); 409 return true; 410 }, 411 }, { 412 name: 'drawElementsInstanced', 413 draw: desc => { 414 if (desc.base_vert) return false; 415 if (desc.base_inst) return false; 416 gl.drawElementsInstanced(gl[desc.mode], desc.vert_count, 417 gl.UNSIGNED_INT, 4*desc.first_vert, desc.inst_count); 418 return true; 419 }, 420 }, { 421 name: 'drawArraysInstancedBaseInstanceWEBGL', 422 draw: desc => { 423 if (desc.base_vert) return false; 424 if (!EXT) return false; 425 EXT.drawArraysInstancedBaseInstanceWEBGL(gl[desc.mode], 426 desc.first_vert, desc.vert_count, desc.inst_count, 427 desc.base_inst); 428 return true; 429 }, 430 }, { 431 name: 'drawElementsInstancedBaseVertexBaseInstanceWEBGL', 432 draw: desc => { 433 if (!EXT) return false; 434 EXT.drawElementsInstancedBaseVertexBaseInstanceWEBGL( 435 gl[desc.mode], desc.vert_count, gl.UNSIGNED_INT, 4*desc.first_vert, 436 desc.inst_count, desc.base_vert, desc.base_inst); 437 return true; 438 }, 439 }]; 440 441 // - 442 443 function make_key_combiner(key, vals) { 444 const ret = []; 445 for (const v of vals) { 446 const cur = {}; 447 cur[key] = v; 448 ret.push(cur); 449 } 450 return ret; 451 } 452 453 const TEST_DESCS = crossCombine( 454 DRAW_FUNC_COMBINER, 455 make_key_combiner('base_vert', [0,1,2]), 456 make_key_combiner('vert_count', [0,1,2]), 457 make_key_combiner('base_inst', [0,1,2]), 458 make_key_combiner('inst_count', range(10)), 459 make_key_combiner('first_vert', [0,1,2]), 460 ); 461 console.log('TEST_DESCS', TEST_DESCS); 462 463 // - 464 // Run testcases 465 466 gl.disable(gl.DEPTH_TEST); 467 gl.disable(gl.STENCIL_TEST); 468 gl.disable(gl.BLEND); 469 470 for (const desc of TEST_DESCS) { 471 gl.disable(gl.SCISSOR_TEST); 472 gl.clearBufferfv(gl.COLOR, 0, [1,0,0,1]); 473 474 // From OpenGL ES 3.2 spec section 10.5 475 // https://www.khronos.org/registry/OpenGL/specs/es/3.2/es_spec_3.2.pdf 476 // The index of any element transferred to the GL by DrawArraysOneInstance 477 // is referred to as its vertex ID, and may be read by a vertex shader as gl_VertexID. 478 // The vertex ID of the ith element transferred is first + i. 479 const last_gl_vert_id = desc.base_vert + desc.first_vert + desc.vert_count - 1; 480 const last_vert_id = last_gl_vert_id; 481 const last_inst_id = desc.inst_count - 1; 482 const last_inst_div1 = desc.base_inst + last_inst_id; 483 const last_inst_div2 = desc.base_inst + Math.floor(last_inst_id / 2); 484 const last_inst_div3 = desc.base_inst + Math.floor(last_inst_id / 3); 485 486 gl.useProgram(vertid_prog); 487 if (!desc.draw(desc)) continue; 488 debug('\ndesc: ' + JSON.stringify(desc)); 489 490 wtu.glErrorAssert(gl, 0); 491 if (!desc.vert_count || !desc.inst_count) { 492 expect_pixel(desc, 'vertid_prog', [255, 0, 0, 255]); 493 continue; 494 } 495 496 expect_pixel(desc, 'vertid_prog', [last_gl_vert_id, last_vert_id, 0, 0]); 497 498 gl.useProgram(instid_prog); 499 desc.draw(desc); 500 expect_pixel(desc, 'instid_prog', [last_inst_id, last_inst_div1, last_inst_div2, last_inst_div3]); 501 } 502 } 503 504 // - 505 506 function doTest(extensionName, multiDraw) { 507 const ext = gl.getExtension(extensionName); 508 if (!runSupportedTest(extensionName, ext)) { 509 return; 510 } 511 512 function getShaderSource(countX, countY, config) { 513 const vs = [ 514 '#version 300 es', 515 config.isMultiDraw ? '#extension GL_ANGLE_multi_draw : require' : '', 516 '#define kCountX ' + countX.toString(), 517 '#define kCountY ' + countY.toString(), 518 'layout(location = 0) in vec2 vPosition;', 519 'layout(location = 1) in float vInstanceID;', 520 'out vec4 color;', 521 'void main()', 522 '{', 523 ' const float xStep = 1.0 / float(kCountX);', 524 ' const float yStep = 1.0 / float(kCountY);', 525 ' float xID = vInstanceID;', 526 ' float xColor = 1.0 - xStep * xID;', 527 ' float yID = floor(float(gl_VertexID) / ' + (config.isDrawArrays ? '6.0' : '4.0') + ' + 0.01);', 528 ' color = vec4(xColor, 1.0 - yStep * yID, 1.0', 529 ' , 1.0);', 530 ' mat3 transform = mat3(1.0);', 531 ' transform[2][0] = xID * xStep;', 532 ' gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1.0);', 533 '}' 534 ].join('\n'); 535 536 const fs = document.getElementById('fshader').text.trim(); 537 538 return [vs, fs]; 539 } 540 541 function runValidationTests(bufferUsage) { 542 const vertexBuffer = gl.createBuffer(); 543 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 544 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.2,0.2, 0.8,0.2, 0.5,0.8 ]), bufferUsage); 545 546 const indexBuffer = gl.createBuffer(); 547 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 548 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([ 0, 1, 2 ]), bufferUsage); 549 550 const instanceBuffer = gl.createBuffer(); 551 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); 552 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 2 ]), bufferUsage); 553 554 const program = wtu.setupProgram(gl, ['vshaderSimple', 'fshader'], ['vPosition, vInstanceID'], [0, 1], true); 555 expectTrue(program != null, "can compile simple program"); 556 557 function setupInstanced() { 558 gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); 559 gl.enableVertexAttribArray(1); 560 gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); 561 gl.vertexAttribDivisor(1, 1); 562 } 563 564 setupInstanced(); 565 566 function setupDrawArrays() { 567 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 568 gl.enableVertexAttribArray(0); 569 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 570 } 571 572 function setupDrawElements() { 573 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 574 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 575 gl.enableVertexAttribArray(0); 576 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 577 } 578 579 function makeDrawValidationCheck(drawFunc, setup) { 580 if (!drawFunc) { 581 return function() {}; 582 } 583 return function(f_args, expect, msg) { 584 setup(); 585 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 586 drawFunc.apply(ext, f_args); 587 wtu.glErrorShouldBe(gl, expect, drawFunc.name + " " + msg); 588 gl.disableVertexAttribArray(0); 589 } 590 } 591 592 if (!multiDraw) { 593 const checkDrawArraysInstancedBaseInstance = makeDrawValidationCheck( 594 ext.drawArraysInstancedBaseInstanceWEBGL, setupDrawArrays); 595 const checkDrawElementsInstancedBaseVertexBaseInstance = makeDrawValidationCheck( 596 ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL, setupDrawElements); 597 checkDrawArraysInstancedBaseInstance( 598 [gl.TRIANGLES, 0, 3, 1, 1], 599 gl.NO_ERROR, "with gl.TRIANGLES" 600 ); 601 checkDrawElementsInstancedBaseVertexBaseInstance( 602 [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 0, 0], 603 gl.NO_ERROR, "with gl.TRIANGLES" 604 ); 605 606 checkDrawArraysInstancedBaseInstance( 607 [gl.TRIANGLES, 0, 3, 1, 3], 608 [gl.NO_ERROR, gl.INVALID_OPERATION], 609 "with baseInstance leading to out of bounds" 610 ); 611 checkDrawElementsInstancedBaseVertexBaseInstance( 612 [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 2, 0], 613 [gl.NO_ERROR, gl.INVALID_OPERATION], 614 "with baseVertex leading to out of bounds" 615 ); 616 checkDrawElementsInstancedBaseVertexBaseInstance( 617 [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 0, 3], 618 [gl.NO_ERROR, gl.INVALID_OPERATION], 619 "with baseInstance leading to out of bounds" 620 ); 621 checkDrawElementsInstancedBaseVertexBaseInstance( 622 [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 2, 3], 623 [gl.NO_ERROR, gl.INVALID_OPERATION], 624 "with both baseVertex and baseInstance leading to out of bounds" 625 ); 626 } else { 627 const checkMultiDrawArraysInstancedBaseInstance = makeDrawValidationCheck( 628 ext.multiDrawArraysInstancedBaseInstanceWEBGL, setupDrawArrays); 629 const checkMultiDrawElementsInstancedBaseVertexBaseInstance = makeDrawValidationCheck( 630 ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL, setupDrawElements); 631 632 // Check that drawing a single triangle works 633 checkMultiDrawArraysInstancedBaseInstance( 634 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, 1], 635 gl.NO_ERROR, "with gl.TRIANGLES" 636 ); 637 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 638 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 1], 639 gl.NO_ERROR, "with gl.TRIANGLES" 640 ); 641 642 checkMultiDrawArraysInstancedBaseInstance( 643 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [3], 0, 1], 644 [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseInstance leads to out of bounds" 645 ); 646 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 647 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [2], 0, [0], 0, 1], 648 [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseVertex leads to out of bounds" 649 ); 650 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 651 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [3], 0, 1], 652 [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseInstance leads to out of bounds" 653 ); 654 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 655 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [2], 0, [3], 0, 1], 656 [gl.NO_ERROR, gl.INVALID_OPERATION], 657 "with both baseVertex and baseInstance lead to out of bounds" 658 ); 659 660 // Zero drawcount permitted 661 checkMultiDrawArraysInstancedBaseInstance( 662 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, 0], 663 gl.NO_ERROR, "with drawcount == 0" 664 ); 665 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 666 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 0], 667 gl.NO_ERROR, "with drawcount == 0" 668 ); 669 670 // Check negative drawcount 671 checkMultiDrawArraysInstancedBaseInstance( 672 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, -1], 673 gl.INVALID_VALUE, "with drawcount < 0" 674 ); 675 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 676 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, -1], 677 gl.INVALID_VALUE, "with drawcount < 0" 678 ); 679 680 // Check offsets greater than array length 681 checkMultiDrawArraysInstancedBaseInstance( 682 [gl.TRIANGLES, [0], 1, [3], 0, [1], 0, [0], 0, 1], 683 gl.INVALID_OPERATION, "with firstsStart >= firstsList.length" 684 ); 685 checkMultiDrawArraysInstancedBaseInstance( 686 [gl.TRIANGLES, [0], 0, [3], 1, [1], 0, [0], 0, 1], 687 gl.INVALID_OPERATION, "with countsStart >= countsList.length" 688 ); 689 checkMultiDrawArraysInstancedBaseInstance( 690 [gl.TRIANGLES, [0], 0, [3], 0, [1], 1, [0], 0, 1], 691 gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length" 692 ); 693 checkMultiDrawArraysInstancedBaseInstance( 694 [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 1, 1], 695 gl.INVALID_OPERATION, "with baseInstancesStart >= baseInstancesList.length" 696 ); 697 698 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 699 [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 1], 700 gl.INVALID_OPERATION, "with countsStart >= countsList.length" 701 ); 702 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 703 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, [1], 0, [0], 0, [0], 0, 1], 704 gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length" 705 ); 706 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 707 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 1, [0], 0, [0], 0, 1], 708 gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length" 709 ); 710 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 711 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 1, [0], 0, 1], 712 gl.INVALID_OPERATION, "with baseVerticesStart >= baseVerticesList.length" 713 ); 714 checkMultiDrawElementsInstancedBaseVertexBaseInstance( 715 [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 1, 1], 716 gl.INVALID_OPERATION, "with baseInstancesStart >= baseInstancesList.length" 717 ); 718 } 719 } 720 721 function runShaderTests(bufferUsage) { 722 let badProgram; 723 724 badProgram = wtu.setupProgram(gl, ["vshaderBaseInstanceWithoutExt", "fshader"]); 725 expectTrue(!badProgram, "cannot compile program with gl_BaseInstance but no extension directive"); 726 badProgram = wtu.setupProgram(gl, ["vshaderBaseVertexWithoutExt", "fshader"]); 727 expectTrue(!badProgram, "cannot compile program with gl_BaseVertex but no extension directive"); 728 729 badProgram = wtu.setupProgram(gl, ["vshaderWithExt", "fshader"]); 730 expectTrue(!badProgram, "cannot compile program with #extension GL_ANGLE_base_vertex_base_instance"); 731 732 const x = Math.floor(width * 0.4); 733 const y = Math.floor(height * 0.4); 734 const xSize = Math.floor(width * 0.2); 735 const ySize = Math.floor(height * 0.2); 736 737 // gl_InstanceID 738 gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 739 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0.5,1, 0,1, 0.5,0, 1,1 ]), bufferUsage); 740 gl.enableVertexAttribArray(0); 741 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 742 743 const instanceIDProgram = wtu.setupProgram(gl, ["vshaderInstanceIDCheck", "fshader"], ["vPosition"], [0]); 744 expectTrue(instanceIDProgram !== null, "can compile program with gl_InstanceID"); 745 gl.useProgram(instanceIDProgram); 746 747 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 748 if (!multiDraw) { 749 ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, 6, 1, 5); 750 } else { 751 ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, [0], 0, [6], 0, [1], 0, [5], 0, 1); 752 } 753 754 wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_InstanceID should always starts from 0"); 755 756 // gl_VertexID 757 gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); 758 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0.5,1, 0,1, 0.5,0, 1,1, 0,0, 1,0, 0.5,1, 0,1 ]), bufferUsage); 759 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer()); 760 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 3, 4, 5]), bufferUsage); 761 gl.enableVertexAttribArray(0); 762 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 763 764 const vertexIDProgram = wtu.setupProgram(gl, ["vshaderVertexIDCheck", "fshader"], ["vPosition"], [0]); 765 expectTrue(vertexIDProgram !== null, "can compile program with gl_VertexID"); 766 gl.useProgram(vertexIDProgram); 767 768 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 769 if (!multiDraw) { 770 ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1, 3, 0); 771 } else { 772 ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, [6], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [3], 0, [0], 0, 1); 773 } 774 775 wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_VertexID should always starts from 0"); 776 } 777 778 function runPixelTests() { 779 780 function checkResult(config) { 781 const rects = []; 782 const expected = [ 783 [255, 0, 0, 255], 784 [0, 255, 0, 255], 785 [0, 0, 255, 255], 786 ]; 787 const msg = config.drawFunc.name + ( 788 config.useBaseVertexBuiltin ? ' gl_BaseVertex' : '' 789 ) + ( 790 config.useBaseInstanceBuiltin ? ' gl_BaseInstance' : ' InstanceIDArray' 791 ); 792 for (let y = 0; y < y_count; ++y) { 793 for (let x = 0; x < x_count; ++x) { 794 const center_x = x * tilePixelSize[0] + Math.floor(tilePixelSize[0] / 2); 795 const center_y = y * tilePixelSize[1] + Math.floor(tilePixelSize[1] / 2); 796 797 rects.push(wtu.makeCheckRect( 798 center_x - Math.floor(pixelCheckSize[0] / 2), 799 center_y - Math.floor(pixelCheckSize[1] / 2), 800 pixelCheckSize[0], 801 pixelCheckSize[1], 802 [ 803 256.0 * (1.0 - x / x_count), 804 256.0 * (1.0 - y / y_count), 805 (!config.isDrawArrays && config.useBaseVertexBuiltin) ? 256.0 * (1.0 - y / y_count) : 255.0, 806 255.0 807 ], 808 msg + ' (' + x + ',' + y + ')', 1.0 809 )); 810 } 811 } 812 wtu.checkCanvasRects(gl, rects); 813 } 814 815 // Draw functions variations 816 817 function drawArraysInstancedBaseInstance() { 818 const countPerDraw = y_count * 6; 819 for (let x = 0; x < x_count; x += 2) { 820 ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, countPerDraw, 2, x); 821 } 822 } 823 824 function multiDrawArraysInstancedBaseInstance() { 825 ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, drawArraysParams.firsts, 0, drawArraysParams.counts, 0, drawArraysParams.instances, 0, drawArraysParams.baseInstances, 0, drawArraysParams.drawCount); 826 } 827 828 function drawElementsInstancedBaseVertexBaseInstance() { 829 const countPerDraw = 6; 830 for (let v = 0; v < y_count; ++v) { 831 for (let x = 0; x < x_count; x += 2) { 832 ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, countPerDraw, gl.UNSIGNED_SHORT, 0, 2, v * 4, x); 833 } 834 } 835 } 836 837 function multiDrawElementsInstancedBaseVertexBaseInstance() { 838 ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, drawElementsParams.counts, 0, gl.UNSIGNED_SHORT, drawElementsParams.offsets, 0, drawElementsParams.instances, 0, drawElementsParams.baseVertices, 0, drawElementsParams.baseInstances, 0, drawElementsParams.drawCount); 839 } 840 841 function checkDraw(config) { 842 const program = wtu.setupProgram( 843 gl, 844 getShaderSource(x_count, y_count, config), 845 !config.useBaseInstanceBuiltin ? ['vPosition'] : ['vPosition', 'vInstanceID'] 846 ); 847 848 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 849 850 if (config.isDrawArrays) { 851 gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer); 852 gl.enableVertexAttribArray(0); 853 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); 854 } else { 855 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); 856 gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); 857 gl.enableVertexAttribArray(0); 858 gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); 859 } 860 861 if (!config.useBaseInstanceBuiltin) { 862 gl.bindBuffer(gl.ARRAY_BUFFER, instanceIDBuffer); 863 gl.enableVertexAttribArray(1); 864 gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); 865 gl.vertexAttribDivisor(1, 1); 866 } 867 868 config.drawFunc(); 869 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); 870 871 checkResult(config); 872 } 873 874 checkDraw({ 875 drawFunc: multiDraw ? multiDrawArraysInstancedBaseInstance : drawArraysInstancedBaseInstance, 876 isDrawArrays: true, 877 isMultiDraw: multiDraw, 878 useBaseVertexBuiltin: false, 879 useBaseInstanceBuiltin: false 880 }); 881 882 checkDraw({ 883 drawFunc: multiDraw ? multiDrawElementsInstancedBaseVertexBaseInstance : drawElementsInstancedBaseVertexBaseInstance, 884 isDrawArrays: false, 885 isMultiDraw: multiDraw, 886 useBaseVertexBuiltin: false, 887 useBaseInstanceBuiltin: false 888 }); 889 } 890 891 for (let i = 0; i < bufferUsageSet.length; i++) { 892 let bufferUsage = bufferUsageSet[i]; 893 debug("Testing with BufferUsage = " + bufferUsage); 894 setupGeneralBuffers(bufferUsage); 895 runValidationTests(bufferUsage); 896 runShaderTests(bufferUsage); 897 runPixelTests(); 898 } 899 } 900 901 902 async function runDrawTests(testFn) { 903 function drawArrays(gl) { 904 gl.drawArrays(gl.TRIANGLES, 0, 6); 905 } 906 907 function drawElements(gl) { 908 gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); 909 } 910 911 function drawArraysInstanced(gl) { 912 gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 1); 913 } 914 915 function drawElementsInstanced(gl) { 916 gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1); 917 } 918 919 function drawArraysInstancedBaseInstanceWEBGL(gl) { 920 const ext = gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance'); 921 if (!ext) { 922 throw 'Should not have run this test without WEBGL_draw_instanced_base_vertex_base_instance'; 923 } 924 925 ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, 6, 1, 0); 926 } 927 928 function drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl) { 929 const ext = gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance'); 930 if (!ext) { 931 throw 'Should not have run this test without WEBGL_draw_instanced_base_vertex_base_instance'; 932 } 933 934 ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1, 0, 0); 935 } 936 937 function multiDrawArraysInstancedBaseInstanceWEBGL(gl) { 938 const ext = gl.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance'); 939 if (!ext) { 940 throw 'Should not have run this test without WEBGL_multi_draw_instanced_base_vertex_base_instance'; 941 } 942 ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, [0], 0, [6], 0, [1], 0, [0], 0, 1); 943 } 944 945 function multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl) { 946 const ext = gl.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance'); 947 if (!ext) { 948 throw 'Should not have run this test without WEBGL_multi_draw_instanced_base_vertex_base_instance'; 949 } 950 ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL( 951 gl.TRIANGLES, 952 [6], 0, // counts 953 gl.UNSIGNED_BYTE, 954 [0], 0, // offsets 955 [1], 0, // instances 956 [0], 0, // baseVerts 957 [0], 0, // baseInstances 958 1, // drawCount 959 ); 960 } 961 962 await testFn(drawArrays); // sanity check 963 await testFn(drawElements); // sanity check 964 await testFn(drawArraysInstanced); // sanity check 965 await testFn(drawElementsInstanced); // sanity check 966 967 // It's only legal to call testFn if the extension is supported, 968 // since the invalid vertex attrib tests, in particular, expect the 969 // draw function to have an effect. 970 if (gl.getExtension('WEBGL_draw_instanced_base_vertex_base_instance')) { 971 await testFn(drawArraysInstancedBaseInstanceWEBGL); 972 await testFn(drawElementsInstancedBaseVertexBaseInstanceWEBGL); 973 } 974 if (gl.getExtension('WEBGL_multi_draw_instanced_base_vertex_base_instance')) { 975 await testFn(multiDrawArraysInstancedBaseInstanceWEBGL); 976 await testFn(multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL); 977 } 978 } 979 980 async function runCompositingTests() { 981 const compositingTestFn = createCompositingTestFn({ 982 webglVersion: 2, 983 shadersFn(gl) { 984 const vs = `\ 985 #version 300 es 986 layout(location = 0) in vec4 position; 987 void main() { 988 gl_Position = position; 989 } 990 `; 991 const fs = `\ 992 #version 300 es 993 precision highp float; 994 out vec4 fragColor; 995 void main() { 996 fragColor = vec4(1, 0, 0, 1); 997 } 998 `; 999 return [vs, fs]; 1000 }, 1001 }); 1002 await runDrawTests(compositingTestFn); 1003 } 1004 1005 async function runInvalidAttribTests(gl) { 1006 const invalidAttribTestFn = createInvalidAttribTestFn(gl); 1007 await runDrawTests(invalidAttribTestFn); 1008 } 1009 1010 async function main() { 1011 runTest(); 1012 await runInvalidAttribTests(gl); 1013 await runCompositingTests(); 1014 finishTest(); 1015 } 1016 main(); 1017 1018 var successfullyParsed = true; 1019 </script> 1020 </body> 1021 </html>