diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-multi-draw.html | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-multi-draw.html | 1064 |
1 files changed, 1064 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-multi-draw.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-multi-draw.html new file mode 100644 index 0000000000..ae4bbc1af0 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-multi-draw.html @@ -0,0 +1,1064 @@ +<!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> +<script src="../../js/tests/compositing-test.js"></script> +<script src="../../js/tests/invalid-vertex-attrib-test.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); + } +} + +function waitForComposite() { + debug('wait for composite'); + return new Promise(resolve => wtu.waitForComposite(resolve)); +} + +async function testPreserveDrawingBufferFalse(gl, drawFn) { + debug(''); + debug('test preserveDrawingBuffer: false'); + + if (drawFn(gl)) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255], + "canvas should be red"); + + // enable scissor here, before compositing, to make sure it's correctly + // ignored and restored + gl.scissor(0, 10, 10, 10); + gl.enable(gl.SCISSOR_TEST); + + await waitForComposite(); + + // scissor was set earlier + gl.clearColor(0, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + wtu.checkCanvasRect(gl, 0, 10, 10, 10, [0, 0, 255, 255], + "cleared corner should be blue, stencil should be preserved"); + wtu.checkCanvasRect(gl, 0, 0, 10, 10, [0, 0, 0, 0], + "remainder of buffer should be cleared"); + + gl.disable(gl.SCISSOR_TEST); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +async function testPreserveDrawingBufferTrue(gl, drawFn) { + debug(''); + debug('test preserveDrawingBuffer: true'); + if (drawFn(gl)) { + debug('skipped: extension does not exist'); + return; + } + + wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255], + "canvas should be red"); + + await waitForComposite(); + + wtu.checkCanvasRect(gl, 0, 0, 20, 20, [255, 0, 0, 255], + "canvas should be red"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +async function doCompositingTests([glPreserveDrawingBufferFalse, glPreserveDrawingBufferTrue], drawFn) { + debug(''); + debug(drawFn.name); + + await testPreserveDrawingBufferFalse(glPreserveDrawingBufferFalse, drawFn); + await testPreserveDrawingBufferTrue(glPreserveDrawingBufferTrue, drawFn); +} + +async function runDrawTests(testFn) { + function drawArrays(gl) { + gl.drawArrays(gl.TRIANGLES, 0, 6); + } + + function drawElements(gl) { + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); + } + + function drawArraysInstanced(gl) { + gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, 1); + } + + function drawElementsInstanced(gl) { + gl.drawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1); + } + + function multiDrawArraysWEBGL(gl) { + const ext = gl.getExtension('WEBGL_multi_draw'); + if (!ext) { + throw 'Should not have run this test without WEBGL_multi_draw'; + } + + ext.multiDrawArraysWEBGL( + gl.TRIANGLES, + [0], 0, // firsts + [6], 0, // counts + 1, // drawCount + ); + } + + function multiDrawElementsWEBGL(gl) { + const ext = gl.getExtension('WEBGL_multi_draw'); + if (!ext) { + throw 'Should not have run this test without WEBGL_multi_draw'; + } + + ext.multiDrawElementsWEBGL( + gl.TRIANGLES, + [6], 0, // counts + gl.UNSIGNED_BYTE, + [0], 0, // offsets + 1, // drawCount + ); + } + + function multiDrawArraysInstancedWEBGL(gl) { + const ext = gl.getExtension('WEBGL_multi_draw'); + if (!ext) { + throw 'Should not have run this test without WEBGL_multi_draw'; + } + ext.multiDrawArraysInstancedWEBGL( + gl.TRIANGLES, + [0], 0, // firsts + [6], 0, // counts + [1], 0, // instances + 1, // drawCount + ); + } + + function multiDrawElementsInstancedWEBGL(gl) { + const ext = gl.getExtension('WEBGL_multi_draw'); + if (!ext) { + throw 'Should not have run this test without WEBGL_multi_draw'; + } + ext.multiDrawElementsInstancedWEBGL( + gl.TRIANGLES, + [6], 0, // counts + gl.UNSIGNED_BYTE, + [0], 0, // offsets + [1], 0, // instances + 1, // drawCount + ); + } + + await testFn(drawArrays); // sanity check + await testFn(drawElements); // sanity check + + // It's only legal to call testFn if the extension is supported, + // since the invalid vertex attrib tests, in particular, expect the + // draw function to have an effect. + if (gl.getExtension('WEBGL_multi_draw')) { + await testFn(multiDrawArraysWEBGL); + await testFn(multiDrawElementsWEBGL); + await testFn(multiDrawArraysInstancedWEBGL); + await testFn(multiDrawElementsInstancedWEBGL); + } +} + +async function runCompositingTests() { + const compositingTestFn = createCompositingTestFn({ + webglVersion: 1, + shadersFn(gl) { + const vs = `\ + //#extension GL_ANGLE_multi_draw : enable + attribute vec4 position; + void main() { + gl_Position = position; + } + `; + const fs = `\ + precision mediump float; + void main() { + gl_FragColor = vec4(1, 0, 0, 1); + } + `; + return [vs, fs]; + }, + }); + await runDrawTests(compositingTestFn); +} + +async function runInvalidAttribTests(gl) { + const invalidAttribTestFn = createInvalidAttribTestFn(gl); + await runDrawTests(invalidAttribTestFn); +} + +function testSideEffects() { + debug("") + debug("Testing that ANGLE_instanced_arrays is implicitly enabled") + const canvas = document.createElement("canvas"); + const gl = wtu.create3DContext(canvas, undefined, 1); + if (!gl) { + testFailed('WebGL context creation failed'); + return; + } + gl.enableVertexAttribArray(0); + const VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE; + gl.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "divisor enum unknown"); + const ext = gl.getExtension("WEBGL_multi_draw"); + if (ext) { + debug("WEBGL_multi_draw enabled"); + gl.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "divisor enum known"); + } +} + +async function main() { + runTest(); + testSideEffects(); + await runInvalidAttribTests(gl); + await runCompositingTests(); + finishTest(); +} +main(); + +var successfullyParsed = true; +</script> +</body> +</html> |