| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>WebGL ANGLE_multi_draw Conformance Tests</title> |
| <link rel="stylesheet" href="../../resources/js-test-style.css"/> |
| <script src="../../js/desktop-gl-constants.js"></script> |
| <script src="../../js/js-test-pre.js"></script> |
| <script src="../../js/webgl-test-utils.js"></script> |
| </head> |
| <body> |
| <script id="vshaderIllegalDrawID" type="x-shader/x-vertex"> |
| attribute vec2 vPosition; |
| varying vec4 color; |
| void main() |
| { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_Position = vec4(vPosition * 2.0 - 1.0, gl_DrawID, 1); |
| } |
| </script> |
| <script id="vshaderDrawIDZero" type="x-shader/x-vertex"> |
| #extension GL_ANGLE_multi_draw : require |
| attribute vec2 vPosition; |
| varying vec4 color; |
| void main() |
| { |
| if (gl_DrawID == 0) { |
| color = vec4(0, 1, 0, 1); |
| } else { |
| color = vec4(1, 0, 0, 1); |
| } |
| gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1); |
| } |
| </script> |
| <!-- The behavior of the shaders below is described in runPixelTests() --> |
| <script id="vshaderWithDrawID" type="x-shader/x-vertex"> |
| #extension GL_ANGLE_multi_draw : require |
| attribute vec2 vPosition; |
| attribute float vInstance; |
| varying vec4 color; |
| void main() |
| { |
| // color_id = (gl_DrawID / 2) % 3 |
| float quad_id = float(gl_DrawID / 2); |
| float color_id = quad_id - (3.0 * floor(quad_id / 3.0)); |
| if (color_id < 0.5) { |
| color = vec4(1, 0, 0, 1); |
| } else if (color_id < 1.5) { |
| color = vec4(0, 1, 0, 1); |
| } else { |
| color = vec4(0, 0, 1, 1); |
| } |
| mat3 transform = mat3(1.0); |
| // vInstance starts at 1.0 on instanced calls |
| if (vInstance >= 1.0) { |
| transform[0][0] = 0.5; |
| transform[1][1] = 0.5; |
| } |
| if (vInstance == 1.0) { |
| } else if (vInstance == 2.0) { |
| transform[2][0] = 0.5; |
| } else if (vInstance == 3.0) { |
| transform[2][1] = 0.5; |
| } else if (vInstance == 4.0) { |
| transform[2][0] = 0.5; |
| transform[2][1] = 0.5; |
| } |
| gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1); |
| } |
| </script> |
| <script id="vshaderEmulatedDrawID" type="x-shader/x-vertex"> |
| uniform int drawID; |
| attribute vec2 vPosition; |
| attribute float vInstance; |
| varying vec4 color; |
| void main() |
| { |
| float quad_id = float(drawID / 2); |
| float color_id = quad_id - (3.0 * floor(quad_id / 3.0)); |
| if (color_id == 0.0) { |
| color = vec4(1, 0, 0, 1); |
| } else if (color_id == 1.0) { |
| color = vec4(0, 1, 0, 1); |
| } else { |
| color = vec4(0, 0, 1, 1); |
| } |
| mat3 transform = mat3(1.0); |
| // vInstance starts at 1.0 on instanced calls |
| if (vInstance >= 1.0) { |
| transform[0][0] = 0.5; |
| transform[1][1] = 0.5; |
| } |
| if (vInstance == 1.0) { |
| } else if (vInstance == 2.0) { |
| transform[2][0] = 0.5; |
| } else if (vInstance == 3.0) { |
| transform[2][1] = 0.5; |
| } else if (vInstance == 4.0) { |
| transform[2][0] = 0.5; |
| transform[2][1] = 0.5; |
| } |
| gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1); |
| } |
| </script> |
| <script id="vshaderNoDrawID" type="x-shader/x-vertex"> |
| attribute vec2 vPosition; |
| attribute float vInstance; |
| varying vec4 color; |
| void main() |
| { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| mat3 transform = mat3(1.0); |
| // vInstance starts at 1.0 on instanced calls |
| if (vInstance >= 1.0) { |
| transform[0][0] = 0.5; |
| transform[1][1] = 0.5; |
| } |
| if (vInstance == 1.0) { |
| } else if (vInstance == 2.0) { |
| transform[2][0] = 0.5; |
| } else if (vInstance == 3.0) { |
| transform[2][1] = 0.5; |
| } else if (vInstance == 4.0) { |
| transform[2][0] = 0.5; |
| transform[2][1] = 0.5; |
| } |
| gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1); |
| } |
| </script> |
| <script id="fshader" type="x-shader/x-fragment"> |
| precision mediump float; |
| varying vec4 color; |
| void main() { |
| gl_FragColor = color; |
| } |
| </script> |
| <div id="description"></div> |
| <canvas id="canvas" width="128" height="128"> </canvas> |
| <div id="console"></div> |
| |
| <script> |
| "use strict"; |
| description("This test verifies the functionality of the ANGLE_multi_draw extension, if it is available."); |
| |
| const wtu = WebGLTestUtils; |
| const canvas = document.getElementById("canvas"); |
| const gl = wtu.create3DContext(canvas); |
| const instancedExt = gl && gl.getExtension('ANGLE_instanced_arrays'); |
| const bufferUsageSet = [ gl.STATIC_DRAW, gl.DYNAMIC_DRAW ]; |
| |
| // Check if the extension is either both enabled and supported or |
| // not enabled and not supported. |
| function runSupportedTest(extensionName, extensionEnabled) { |
| const supported = gl.getSupportedExtensions(); |
| if (supported.indexOf(extensionName) >= 0) { |
| if (extensionEnabled) { |
| testPassed(extensionName + ' listed as supported and getExtension succeeded'); |
| return true; |
| } else { |
| testFailed(extensionName + ' listed as supported but getExtension failed'); |
| } |
| } else { |
| if (extensionEnabled) { |
| testFailed(extensionName + ' not listed as supported but getExtension succeeded'); |
| } else { |
| testPassed(extensionName + ' not listed as supported and getExtension failed -- this is legal'); |
| } |
| } |
| return false; |
| } |
| |
| function runTest() { |
| if (!gl) { |
| return function() { |
| testFailed('WebGL context does not exist'); |
| } |
| } |
| |
| const extensionName = 'WEBGL_multi_draw'; |
| const ext = gl.getExtension(extensionName); |
| if (!runSupportedTest(extensionName, ext)) { |
| return; |
| } |
| |
| doTest(ext, false); |
| doTest(ext, true); |
| } |
| |
| function doTest(ext, instanced) { |
| |
| function runValidationTests(bufferUsage) { |
| const vertexBuffer = gl.createBuffer(); |
| gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0.2,0.2, 0.8,0.2, 0.5,0.8 ]), bufferUsage); |
| |
| const indexBuffer = gl.createBuffer(); |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); |
| gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([ 0, 1, 2, 0]), bufferUsage); |
| |
| const instanceBuffer = gl.createBuffer(); |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 2, 3 ]), bufferUsage); |
| |
| const program = wtu.setupProgram(gl, ["vshaderNoDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); |
| expectTrue(program != null, "can compile simple program"); |
| |
| function setupDrawArrays() { |
| gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); |
| } |
| |
| function setupDrawElements() { |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); |
| gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); |
| } |
| |
| function setupInstanced() { |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); |
| gl.enableVertexAttribArray(1); |
| gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); |
| if (wtu.getDefault3DContextVersion() < 2) { |
| instancedExt.vertexAttribDivisorANGLE(1, 1); |
| } else { |
| gl.vertexAttribDivisor(1, 1); |
| } |
| } |
| |
| function setupDrawArraysInstanced() { |
| setupDrawArrays(); |
| setupInstanced(); |
| } |
| |
| function setupDrawElementsInstanced() { |
| setupDrawElements(); |
| setupInstanced(); |
| } |
| |
| // Wrap a draw call in a function to setup the draw call, execute, |
| // and check errors. |
| // The `drawFunc` is one of the extension entrypoints being tested. It may |
| // be undefined if that entrypoint is not supported on the context |
| function makeDrawCheck(drawFunc, setup) { |
| if (!drawFunc) { |
| return function() {}; |
| } |
| return function(f_args, expect, msg) { |
| setup(); |
| drawFunc.apply(ext, f_args); |
| wtu.glErrorShouldBe(gl, expect, drawFunc.name + " " + msg); |
| gl.disableVertexAttribArray(0); |
| gl.disableVertexAttribArray(1); |
| } |
| } |
| |
| const checkMultiDrawArrays = makeDrawCheck( |
| ext.multiDrawArraysWEBGL, setupDrawArrays); |
| const checkMultiDrawElements = makeDrawCheck( |
| ext.multiDrawElementsWEBGL, setupDrawElements); |
| const checkMultiDrawArraysInstanced = makeDrawCheck( |
| ext.multiDrawArraysInstancedWEBGL, setupDrawArraysInstanced); |
| const checkMultiDrawElementsInstanced = makeDrawCheck( |
| ext.multiDrawElementsInstancedWEBGL, setupDrawElementsInstanced); |
| |
| gl.useProgram(program); |
| |
| // Check that drawing a single triangle works |
| if (!instanced) { |
| checkMultiDrawArrays( |
| [gl.TRIANGLES, [0], 0, [3], 0, 1], |
| gl.NO_ERROR, "with gl.TRIANGLES"); |
| checkMultiDrawElements( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, 1], |
| gl.NO_ERROR, "with gl.TRIANGLES"); |
| } else { |
| checkMultiDrawElementsInstanced( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 1], |
| gl.NO_ERROR, "with gl.TRIANGLES"); |
| checkMultiDrawArraysInstanced( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, 1], |
| gl.NO_ERROR, "with gl.TRIANGLES"); |
| } |
| |
| // Zero drawcount permitted |
| if (!instanced) { |
| checkMultiDrawArrays( |
| [gl.TRIANGLES, [0], 0, [3], 0, 0], |
| gl.NO_ERROR, "with drawcount == 0"); |
| checkMultiDrawElements( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, 0], |
| gl.NO_ERROR, "with drawcount == 0"); |
| } else { |
| checkMultiDrawElementsInstanced( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 0], |
| gl.NO_ERROR, "with drawcount == 0"); |
| checkMultiDrawArraysInstanced( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, 0], |
| gl.NO_ERROR, "with drawcount == 0"); |
| } |
| |
| // Check negative drawcount |
| if (!instanced) { |
| checkMultiDrawArrays( |
| [gl.TRIANGLES, [0], 0, [3], 0, -1], |
| gl.INVALID_VALUE, "with drawcount < 0"); |
| checkMultiDrawElements( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, -1], |
| gl.INVALID_VALUE, "with drawcount < 0"); |
| } else { |
| checkMultiDrawElementsInstanced( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, -1], |
| gl.INVALID_VALUE, "with drawcount < 0"); |
| checkMultiDrawArraysInstanced( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, -1], |
| gl.INVALID_VALUE, "with drawcount < 0"); |
| } |
| |
| // Check offsets greater than array length |
| if (!instanced) { |
| checkMultiDrawArrays( |
| [gl.TRIANGLES, [0], 1, [3], 0, 1], |
| gl.INVALID_OPERATION, "with firstsStart >= firstsList.length"); |
| checkMultiDrawArrays( |
| [gl.TRIANGLES, [0], 0, [3], 1, 1], |
| gl.INVALID_OPERATION, "with countsStart >= countsList.length"); |
| |
| checkMultiDrawElements( |
| [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, 1], |
| gl.INVALID_OPERATION, "with countsStart >= countsList.length"); |
| checkMultiDrawElements( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, 1], |
| gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length"); |
| } else { |
| checkMultiDrawArraysInstanced( |
| [gl.TRIANGLES, [0], 1, [3], 0, [1], 0, 1], |
| gl.INVALID_OPERATION, "with firstsStart >= firstsList.length"); |
| checkMultiDrawArraysInstanced( |
| [gl.TRIANGLES, [0], 0, [3], 1, [1], 0, 1], |
| gl.INVALID_OPERATION, "with countsStart >= countsList.length"); |
| checkMultiDrawArraysInstanced( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 1, 1], |
| gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length"); |
| |
| checkMultiDrawElementsInstanced( |
| [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, [1], 0, 1], |
| gl.INVALID_OPERATION, "with countsStart >= countsList.length"); |
| checkMultiDrawElementsInstanced( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, [1], 0, 1], |
| gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length"); |
| checkMultiDrawElementsInstanced( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 1, 1], |
| gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length"); |
| } |
| } |
| |
| function runShaderTests(bufferUsage) { |
| const illegalProgram = wtu.setupProgram(gl, ["vshaderIllegalDrawID", "fshader"], ["vPosition"], [0]); |
| expectTrue(illegalProgram == null, "cannot compile program with gl_DrawID but no extension directive"); |
| |
| const drawIDProgram = wtu.setupProgram(gl, ["vshaderDrawIDZero", "fshader"], ["vPosition"], [0]); |
| wtu.setupProgram(gl, ["vshaderDrawIDZero", "fshader"], ["vPosition"], [0]); |
| expectTrue(drawIDProgram !== null, "can compile program with gl_DrawID"); |
| gl.useProgram(drawIDProgram); |
| gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0,1, 0,1, 1,0, 1,1 ]), bufferUsage); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); |
| gl.drawArrays(gl.TRIANGLES, 0, 6); |
| wtu.checkCanvas(gl, [0, 255, 0, 255], "gl_DrawID is 0 for non-Multi* draw calls", 0); |
| } |
| |
| function runPixelTests(bufferUsage, useSharedArrayBuffer) { |
| // An array of quads is tiled across the screen. |
| // gl_DrawID is checked by using it to select the color of the draw. |
| // Instanced entrypoints are tested here scaling and then instancing the |
| // array of quads over four quadrants on the screen. |
| |
| // These tests also include "manyDraw" tests which emulate a multiDraw with |
| // a Javascript for-loop and gl_DrawID with a uniform constiable. They are |
| // included to ensure the test is written correctly. |
| |
| const width = gl.canvas.width; |
| const height = gl.canvas.height; |
| const x_count = 8; |
| const y_count = 8; |
| const quad_count = x_count * y_count; |
| const tri_count = quad_count * 2; |
| const tileSize = [ 1/x_count, 1/y_count ]; |
| const tilePixelSize = [ Math.floor(width / x_count), Math.floor(height / y_count) ]; |
| const quadRadius = [ 0.25 * tileSize[0], 0.25 * tileSize[1] ]; |
| const pixelCheckSize = [ Math.floor(quadRadius[0] * width), Math.floor(quadRadius[1] * height) ]; |
| |
| function getTileCenter(x, y) { |
| return [ tileSize[0] * (0.5 + x), tileSize[1] * (0.5 + y) ]; |
| } |
| |
| function getQuadVertices(x, y) { |
| const center = getTileCenter(x, y); |
| return [ |
| [center[0] - quadRadius[0], center[1] - quadRadius[1], 0], |
| [center[0] + quadRadius[0], center[1] - quadRadius[1], 0], |
| [center[0] + quadRadius[0], center[1] + quadRadius[1], 0], |
| [center[0] - quadRadius[0], center[1] + quadRadius[1], 0], |
| ] |
| } |
| |
| const indicesData = []; |
| const verticesData = []; |
| const nonIndexedVerticesData = []; |
| { |
| const is = new Uint16Array([0, 1, 2, 0, 2, 3]); |
| for (let y = 0; y < y_count; ++y) { |
| for (let x = 0; x < x_count; ++x) { |
| const quadIndex = y * x_count + x; |
| const starting_index = 4 * quadIndex; |
| const vs = getQuadVertices(x, y); |
| for (let i = 0; i < is.length; ++i) { |
| indicesData.push(starting_index + is[i]); |
| } |
| for (let i = 0; i < vs.length; ++i) { |
| for (let v = 0; v < vs[i].length; ++v) verticesData.push(vs[i][v]); |
| } |
| for (let i = 0; i < is.length; ++i) { |
| for (let v = 0; v < vs[is[i]].length; ++v) nonIndexedVerticesData.push(vs[is[i]][v]); |
| } |
| } |
| } |
| } |
| |
| const indices = new Uint16Array(indicesData); |
| const vertices = new Float32Array(verticesData); |
| const nonIndexedVertices = new Float32Array(nonIndexedVerticesData); |
| |
| const indexBuffer = gl.createBuffer(); |
| const vertexBuffer = gl.createBuffer(); |
| const nonIndexedVertexBuffer = gl.createBuffer(); |
| const instanceBuffer = gl.createBuffer(); |
| |
| gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage); |
| |
| gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, vertices, bufferUsage); |
| |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); |
| gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, bufferUsage); |
| |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([1, 2, 3, 4]), bufferUsage); |
| |
| function checkResult(config, msg) { |
| const rects = []; |
| const expected = [ |
| [255, 0, 0, 255], |
| [0, 255, 0, 255], |
| [0, 0, 255, 255], |
| ]; |
| for (let y = 0; y < y_count; ++y) { |
| for (let x = 0; x < x_count; ++x) { |
| const center_x = x * tilePixelSize[0] + Math.floor(tilePixelSize[0] / 2); |
| const center_y = y * tilePixelSize[1] + Math.floor(tilePixelSize[1] / 2); |
| const quadID = y * x_count + x; |
| const colorID = config.drawID ? quadID % 3 : 0; |
| if (config.instanced) { |
| rects.push(wtu.makeCheckRect( |
| center_x / 2 - Math.floor(pixelCheckSize[0] / 4), |
| center_y / 2 - Math.floor(pixelCheckSize[1] / 4), |
| pixelCheckSize[0] / 2, |
| pixelCheckSize[1] / 2, |
| expected[colorID], |
| msg + " (" + x + "," + y + ")", 0)); |
| rects.push(wtu.makeCheckRect( |
| center_x / 2 - Math.floor(pixelCheckSize[0] / 4) + width / 2, |
| center_y / 2 - Math.floor(pixelCheckSize[1] / 4), |
| pixelCheckSize[0] / 2, |
| pixelCheckSize[1] / 2, |
| expected[colorID], |
| msg + " (" + x + "," + y + ")", 0)); |
| rects.push(wtu.makeCheckRect( |
| center_x / 2 - Math.floor(pixelCheckSize[0] / 4), |
| center_y / 2 - Math.floor(pixelCheckSize[1] / 4) + height / 2, |
| pixelCheckSize[0] / 2, |
| pixelCheckSize[1] / 2, |
| expected[colorID], |
| msg + " (" + x + "," + y + ")", 0)); |
| rects.push(wtu.makeCheckRect( |
| center_x / 2 - Math.floor(pixelCheckSize[0] / 4) + width / 2, |
| center_y / 2 - Math.floor(pixelCheckSize[1] / 4) + height / 2, |
| pixelCheckSize[0] / 2, |
| pixelCheckSize[1] / 2, |
| expected[colorID], |
| msg + " (" + x + "," + y + ")", 0)); |
| } else { |
| rects.push(wtu.makeCheckRect( |
| center_x - Math.floor(pixelCheckSize[0] / 2), |
| center_y - Math.floor(pixelCheckSize[1] / 2), |
| pixelCheckSize[0], |
| pixelCheckSize[1], |
| expected[colorID], |
| msg + " (" + x + "," + y + ")", 0)); |
| } |
| } |
| } |
| wtu.checkCanvasRects(gl, rects); |
| } |
| |
| function newIntArray(count) { |
| if (!useSharedArrayBuffer) { |
| return new Int32Array(count); |
| } |
| let sab = new SharedArrayBuffer(count * Int32Array.BYTES_PER_ELEMENT); |
| return new Int32Array(sab); |
| } |
| |
| const firsts = newIntArray(tri_count); |
| const counts = newIntArray(tri_count); |
| const offsets = newIntArray(tri_count); |
| const instances = newIntArray(tri_count); |
| |
| for (let i = 0; i < firsts.length; ++i) firsts[i] = i * 3; |
| counts.fill(3); |
| for (let i = 0; i < offsets.length; ++i) offsets[i] = i * 3 * 2; |
| instances.fill(4); |
| |
| const firstsOffset = 47; |
| const countsOffset = firstsOffset + firsts.length; |
| const offsetsOffset = countsOffset + counts.length; |
| const instancesOffset = offsetsOffset + instances.length; |
| |
| const buffer = newIntArray(firstsOffset + firsts.length + counts.length + offsets.length + instances.length); |
| buffer.set(firsts, firstsOffset); |
| buffer.set(counts, countsOffset); |
| buffer.set(offsets, offsetsOffset); |
| buffer.set(instances, instancesOffset); |
| |
| let drawIDLocation; |
| |
| const multiDrawArrays = function() { |
| ext.multiDrawArraysWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, tri_count); |
| } |
| |
| const multiDrawArraysWithNonzeroOffsets = function() { |
| ext.multiDrawArraysWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, tri_count); |
| } |
| |
| const multiDrawElements = function() { |
| ext.multiDrawElementsWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, tri_count); |
| } |
| |
| const multiDrawElementsWithNonzeroOffsets = function() { |
| ext.multiDrawElementsWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, tri_count); |
| } |
| |
| const multiDrawArraysInstanced = function() { |
| ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, firsts, 0, counts, 0, instances, 0, tri_count); |
| } |
| |
| const multiDrawArraysInstancedWithNonzeroOffsets = function() { |
| ext.multiDrawArraysInstancedWEBGL(gl.TRIANGLES, buffer, firstsOffset, buffer, countsOffset, buffer, instancesOffset, tri_count); |
| } |
| |
| const multiDrawElementsInstanced = function() { |
| ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, counts, 0, gl.UNSIGNED_SHORT, offsets, 0, instances, 0, tri_count); |
| } |
| |
| const multiDrawElementsInstancedWithNonzeroOffsets = function() { |
| ext.multiDrawElementsInstancedWEBGL(gl.TRIANGLES, buffer, countsOffset, gl.UNSIGNED_SHORT, buffer, offsetsOffset, buffer, instancesOffset, tri_count); |
| } |
| |
| const manyDrawArrays = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| gl.drawArrays(gl.TRIANGLES, firsts[i], counts[i]); |
| } |
| } |
| |
| const manyDrawElements = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| gl.drawElements(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i]); |
| } |
| } |
| |
| const manyDrawArraysEmulateDrawID = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| gl.uniform1i(drawIDLocation, i); |
| gl.drawArrays(gl.TRIANGLES, firsts[i], counts[i]); |
| } |
| } |
| |
| const manyDrawElementsEmulateDrawID = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| gl.uniform1i(drawIDLocation, i); |
| gl.drawElements(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i]); |
| } |
| } |
| |
| function drawArraysInstanced() { |
| if (wtu.getDefault3DContextVersion() < 2) { |
| instancedExt.drawArraysInstancedANGLE.apply(instancedExt, arguments); |
| } else { |
| gl.drawArraysInstanced.apply(gl, arguments); |
| } |
| } |
| |
| function drawElementsInstanced() { |
| if (wtu.getDefault3DContextVersion() < 2) { |
| instancedExt.drawElementsInstancedANGLE.apply(instancedExt, arguments); |
| } else { |
| gl.drawElementsInstanced.apply(gl, arguments); |
| } |
| } |
| |
| function vertexAttribDivisor(attrib, divisor) { |
| if (wtu.getDefault3DContextVersion() < 2) { |
| instancedExt.vertexAttribDivisorANGLE(attrib, divisor); |
| } else { |
| gl.vertexAttribDivisor(attrib, divisor); |
| } |
| } |
| |
| const manyDrawArraysInstanced = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| drawArraysInstanced(gl.TRIANGLES, firsts[i], counts[i], 4); |
| } |
| } |
| |
| const manyDrawElementsInstanced = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| drawElementsInstanced(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i], 4); |
| } |
| } |
| |
| const manyDrawArraysInstancedEmulateDrawID = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| gl.uniform1i(drawIDLocation, i); |
| drawArraysInstanced(gl.TRIANGLES, firsts[i], counts[i], 4); |
| } |
| } |
| |
| const manyDrawElementsInstancedEmulateDrawID = function() { |
| for (let i = 0; i < tri_count; ++i) { |
| gl.uniform1i(drawIDLocation, i); |
| drawElementsInstanced(gl.TRIANGLES, counts[i], gl.UNSIGNED_SHORT, offsets[i], 4); |
| } |
| } |
| |
| function checkDraw(config) { |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| |
| if (config.indexed) { |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); |
| gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); |
| } else { |
| gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); |
| } |
| |
| if (config.instanced) { |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); |
| gl.enableVertexAttribArray(1); |
| gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); |
| vertexAttribDivisor(1, 1); |
| } |
| |
| config.drawFunc(); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); |
| checkResult(config, config.drawFunc.name + ( |
| config.instanced ? ' instanced' : '' |
| ) + ( |
| config.drawID ? ' with gl_DrawID' : '' |
| ) + ( |
| useSharedArrayBuffer ? ' and SharedArrayBuffer' : '' |
| )); |
| |
| gl.disableVertexAttribArray(0); |
| gl.disableVertexAttribArray(1); |
| } |
| |
| const noDrawIDProgram = wtu.setupProgram(gl, ["vshaderNoDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); |
| expectTrue(noDrawIDProgram != null, "can compile simple program"); |
| if (noDrawIDProgram) { |
| gl.useProgram(noDrawIDProgram); |
| |
| if (!instanced) { |
| checkDraw({ |
| drawFunc: multiDrawArrays, |
| drawID: false, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawArraysWithNonzeroOffsets, |
| drawID: false, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElements, |
| indexed: true, |
| drawID: false, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElementsWithNonzeroOffsets, |
| indexed: true, |
| drawID: false, |
| }); |
| checkDraw({ |
| drawFunc: manyDrawArrays, |
| drawID: false, |
| }); |
| checkDraw({ |
| drawFunc: manyDrawElements, |
| indexed: true, |
| drawID: false, |
| }); |
| } else { |
| checkDraw({ |
| drawFunc: multiDrawArraysInstanced, |
| drawID: false, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawArraysInstancedWithNonzeroOffsets, |
| drawID: false, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElementsInstanced, |
| indexed: true, |
| drawID: false, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElementsInstancedWithNonzeroOffsets, |
| indexed: true, |
| drawID: false, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: manyDrawArraysInstanced, |
| drawID: false, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: manyDrawElementsInstanced, |
| indexed: true, |
| drawID: false, |
| instanced: true, |
| }); |
| } |
| } |
| |
| const withDrawIDProgram = wtu.setupProgram(gl, ["vshaderWithDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); |
| expectTrue(withDrawIDProgram != null, "can compile program with ANGLE_multi_draw"); |
| if (withDrawIDProgram) { |
| gl.useProgram(withDrawIDProgram); |
| |
| if (!instanced) { |
| checkDraw({ |
| drawFunc: multiDrawArrays, |
| drawID: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawArraysWithNonzeroOffsets, |
| drawID: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElements, |
| indexed: true, |
| drawID: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElementsWithNonzeroOffsets, |
| indexed: true, |
| drawID: true, |
| }); |
| } else { |
| checkDraw({ |
| drawFunc: multiDrawArraysInstanced, |
| drawID: true, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawArraysInstancedWithNonzeroOffsets, |
| drawID: true, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElementsInstanced, |
| indexed: true, |
| drawID: true, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: multiDrawElementsInstancedWithNonzeroOffsets, |
| indexed: true, |
| drawID: true, |
| instanced: true, |
| }); |
| } |
| } |
| |
| const emulatedDrawIDProgram = wtu.setupProgram(gl, ["vshaderEmulatedDrawID", "fshader"], ["vPosition", "vInstance"], [0, 1]); |
| expectTrue(emulatedDrawIDProgram != null, "can compile program to emulate gl_DrawID"); |
| drawIDLocation = gl.getUniformLocation(emulatedDrawIDProgram, "drawID"); |
| if (emulatedDrawIDProgram) { |
| gl.useProgram(emulatedDrawIDProgram); |
| |
| if (!instanced) { |
| checkDraw({ |
| drawFunc: manyDrawArraysEmulateDrawID, |
| drawID: true, |
| }); |
| checkDraw({ |
| drawFunc: manyDrawElementsEmulateDrawID, |
| indexed: true, |
| drawID: true, |
| }); |
| } else { |
| checkDraw({ |
| drawFunc: manyDrawArraysInstancedEmulateDrawID, |
| drawID: true, |
| instanced: true, |
| }); |
| checkDraw({ |
| drawFunc: manyDrawElementsInstancedEmulateDrawID, |
| indexed: true, |
| drawID: true, |
| instanced: true, |
| }); |
| } |
| } |
| } |
| |
| for (let i = 0; i < bufferUsageSet.length; i++) { |
| let bufferUsage = bufferUsageSet[i]; |
| debug("Testing with BufferUsage = " + bufferUsage); |
| runValidationTests(bufferUsage); |
| runShaderTests(bufferUsage); |
| runPixelTests(bufferUsage, false); |
| } |
| |
| // Run a subset of the pixel tests with SharedArrayBuffer if supported. |
| if (window.SharedArrayBuffer) { |
| runPixelTests(bufferUsageSet[0], true); |
| } |
| } |
| |
| runTest(); |
| |
| const successfullyParsed = true; |
| </script> |
| <script src="../../js/js-test-post.js"></script> |
| </body> |
| </html> |