| <!DOCTYPE html> |
| <html> |
| <head> |
| <meta charset="utf-8"> |
| <title>WebGL ANGLE_base_vertex_base_instance 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="vshaderIllegalBaseInstance" type="x-shader/x-vertex">#version 300 es |
| layout(location = 0) in vec2 vPosition; |
| out vec4 color; |
| void main() |
| { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_Position = vec4(vPosition * 2.0 - 1.0, gl_BaseInstance, 1); |
| } |
| </script> |
| <script id="vshaderBaseInstanceZero" type="x-shader/x-vertex">#version 300 es |
| #extension GL_ANGLE_base_vertex_base_instance : require |
| layout(location = 0) in vec2 vPosition; |
| out vec4 color; |
| void main() |
| { |
| if (gl_BaseInstance == 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> |
| <!-- Check gl_InstanceID starts at 0 regardless of gl_BaseInstance --> |
| <script id="vshaderInstanceIDCheck" type="x-shader/x-vertex">#version 300 es |
| layout(location = 0) in vec2 vPosition; |
| out vec4 color; |
| void main() |
| { |
| if (gl_InstanceID == 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> |
| <script id="vshaderIllegalBaseVertex" type="x-shader/x-vertex">#version 300 es |
| layout(location = 0) in vec2 vPosition; |
| out vec4 color; |
| void main() |
| { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| gl_Position = vec4(vPosition * 2.0 - 1.0, gl_BaseVertex, 1); |
| } |
| </script> |
| <script id="vshaderBaseVertexZero" type="x-shader/x-vertex">#version 300 es |
| #extension GL_ANGLE_base_vertex_base_instance : require |
| layout(location = 0) in vec2 vPosition; |
| out vec4 color; |
| void main() |
| { |
| if (gl_BaseVertex == 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> |
| <!-- Check gl_VertexID starts at gl_BaseVertex --> |
| <script id="vshaderVertexIDCheck" type="x-shader/x-vertex">#version 300 es |
| layout(location = 0) in vec2 vPosition; |
| out vec4 color; |
| void main() |
| { |
| if (gl_VertexID >= 3) { |
| color = vec4(0, 1, 0, 1); |
| } else { |
| color = vec4(1, 0, 0, 1); |
| } |
| gl_Position = vec4(vPosition * 2.0 - 1.0, 0, 1); |
| } |
| </script> |
| <script id="vshaderSimple" type="x-shader/x-vertex">#version 300 es |
| #extension GL_ANGLE_base_vertex_base_instance : require |
| layout(location = 0) in vec2 vPosition; |
| layout(location = 1) in float vInstance; |
| out vec4 color; |
| void main() |
| { |
| if (vInstance <= 0.0) { |
| color = vec4(1.0, 0.0, 0.0, 1.0); |
| } else if (vInstance <= 1.0) { |
| color = vec4(0.0, 1.0, 0.0, 1.0); |
| } else if (vInstance <= 2.0) { |
| color = vec4(0.0, 0.0, 1.0, 1.0); |
| } else { |
| color = vec4(0.0, 0.0, 0.0, 1.0); |
| } |
| |
| gl_Position = vec4(vec3(vPosition, 1.0) * 2.0 - 1.0, 1); |
| } |
| </script> |
| <script id="fshader" type="x-shader/x-fragment">#version 300 es |
| precision mediump float; |
| in vec4 color; |
| out vec4 oColor; |
| void main() { |
| oColor = 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 WEBGL_[multi]_draw_basevertex_base_instance extension, if it is available."); |
| |
| const wtu = WebGLTestUtils; |
| const canvas = document.getElementById("canvas"); |
| const gl = wtu.create3DContext(canvas, null, 2); |
| |
| 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) ]; |
| const bufferUsageSet = [ gl.STATIC_DRAW, gl.DYNAMIC_DRAW ]; |
| |
| 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 = []; |
| let verticesData = []; |
| let nonIndexedVerticesData = []; |
| const instanceIDsData = Array.from(Array(x_count).keys()); |
| const is = new Uint16Array([0, 1, 2, 0, 2, 3]); |
| // Rects in the same column are within a vertex array, testing gl_VertexID, gl_BaseVertex |
| // Rects in the same row are drawn by instancing, testing gl_InstanceID, gl_BaseInstance |
| for (let y = 0; y < y_count; ++y) { |
| // v3 ---- v2 |
| // | | |
| // | | |
| // v0 ---- v1 |
| |
| // Get only one column of quad vertices as our geometry |
| // Rely on BaseInstance to duplicate on x axis |
| const vs = getQuadVertices(0, y); |
| |
| for (let i = 0; i < vs.length; ++i) { |
| verticesData = verticesData.concat(vs[i]); |
| } |
| |
| for (let i = 0; i < is.length; ++i) { |
| nonIndexedVerticesData = nonIndexedVerticesData.concat(vs[is[i]]); |
| } |
| } |
| |
| // Build the indicesData used by drawElements* |
| for (let i = 0; i < y_count; ++i) { |
| let oi = 6 * i; |
| let ov = 4 * i; |
| for (let j = 0; j < is.length; ++j) { |
| indicesData[oi + j] = is[j] + ov; |
| } |
| } |
| |
| const indices = new Uint16Array(indicesData); |
| const vertices = new Float32Array(verticesData); |
| const nonIndexedVertices = new Float32Array(nonIndexedVerticesData); |
| const instanceIDs = new Float32Array(instanceIDsData); |
| |
| const indexBuffer = gl.createBuffer(); |
| const vertexBuffer = gl.createBuffer(); |
| const nonIndexedVertexBuffer = gl.createBuffer(); |
| const instanceIDBuffer = gl.createBuffer(); |
| |
| const drawArraysDrawCount = x_count / 2; |
| let drawArraysParams = { |
| drawCount: drawArraysDrawCount, |
| firsts: new Uint32Array(drawArraysDrawCount).fill(0), |
| counts: new Uint32Array(drawArraysDrawCount).fill(y_count * 6), |
| instances: new Uint32Array(drawArraysDrawCount).fill(2), |
| baseInstances: new Uint32Array(drawArraysDrawCount) |
| }; |
| |
| for (let i = 0; i < x_count / 2; ++i) { |
| drawArraysParams.baseInstances[i] = i * 2; |
| } |
| |
| const drawElementsDrawCount = x_count * y_count / 2; |
| let drawElementsParams = { |
| drawCount: drawElementsDrawCount, |
| offsets: new Uint32Array(drawElementsDrawCount).fill(0), |
| counts: new Uint32Array(drawElementsDrawCount).fill(6), |
| instances: new Uint32Array(drawElementsDrawCount).fill(2), |
| baseVertices: new Uint32Array(drawElementsDrawCount), |
| baseInstances: new Uint32Array(drawElementsDrawCount) |
| }; |
| |
| let b = 0; |
| for (let v = 0; v < y_count; ++v) { |
| for (let i = 0; i < x_count; i+=2) { |
| drawElementsParams.baseVertices[b] = v * 4; |
| drawElementsParams.baseInstances[b] = i; |
| ++b; |
| } |
| } |
| |
| function setupGeneralBuffers(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, nonIndexedVertexBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, nonIndexedVertices, bufferUsage); |
| |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceIDBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, instanceIDs, bufferUsage); |
| } |
| |
| // 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'); |
| } |
| } |
| |
| doTest('WEBGL_draw_instanced_base_vertex_base_instance', false); |
| doTest('WEBGL_multi_draw_instanced_base_vertex_base_instance', true); |
| } |
| |
| function doTest(extensionName, multiDraw) { |
| const ext = gl.getExtension(extensionName); |
| if (!runSupportedTest(extensionName, ext)) { |
| return; |
| } |
| |
| function getShaderSource(countX, countY, config) { |
| const vs = [ |
| '#version 300 es', |
| config.isMultiDraw ? '#extension GL_ANGLE_multi_draw : require' : '', |
| '#extension GL_ANGLE_base_vertex_base_instance : require', |
| '#define kCountX ' + countX.toString(), |
| '#define kCountY ' + countY.toString(), |
| 'layout(location = 0) in vec2 vPosition;', |
| config.useBaseInstanceBuiltin ? '' : 'layout(location = 1) in float vInstanceID;', |
| 'out vec4 color;', |
| 'void main()', |
| '{', |
| ' const float xStep = 1.0 / float(kCountX);', |
| ' const float yStep = 1.0 / float(kCountY);', |
| ' float xID = ' + (config.useBaseInstanceBuiltin ? 'float(gl_InstanceID + gl_BaseInstance)' : 'vInstanceID') + ';', |
| ' float xColor = 1.0 - xStep * xID;', |
| ' float yID = floor(float(gl_VertexID) / ' + (config.isDrawArrays ? '6.0' : '4.0') + ' + 0.01);', |
| ' color = vec4(xColor, 1.0 - yStep * yID, ', |
| config.useBaseVertexBuiltin ? '1.0 - yStep * float(gl_BaseVertex) / 4.0' : '1.0', |
| ' , 1.0);', |
| ' mat3 transform = mat3(1.0);', |
| ' transform[2][0] = xID * xStep;', |
| ' gl_Position = vec4(transform * vec3(vPosition, 1.0) * 2.0 - 1.0, 1.0);', |
| '}' |
| ].join('\n'); |
| |
| const fs = document.getElementById('fshader').text.trim(); |
| |
| return [vs, fs]; |
| } |
| |
| 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 ]), bufferUsage); |
| |
| const instanceBuffer = gl.createBuffer(); |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0, 1, 2 ]), bufferUsage); |
| |
| const program = wtu.setupProgram(gl, ['vshaderSimple', 'fshader'], ['vPosition, vInstanceID'], [0, 1]); |
| expectTrue(program != null, "can compile simple program"); |
| |
| function setupInstanced() { |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceBuffer); |
| gl.enableVertexAttribArray(1); |
| gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); |
| gl.vertexAttribDivisor(1, 1); |
| } |
| |
| setupInstanced(); |
| |
| 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.ARRAY_BUFFER, vertexBuffer); |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); |
| } |
| |
| function makeDrawValidationCheck(drawFunc, setup) { |
| if (!drawFunc) { |
| return function() {}; |
| } |
| return function(f_args, expect, msg) { |
| setup(); |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| drawFunc.apply(ext, f_args); |
| wtu.glErrorShouldBe(gl, expect, drawFunc.name + " " + msg); |
| gl.disableVertexAttribArray(0); |
| } |
| } |
| |
| if (!multiDraw) { |
| const checkDrawArraysInstancedBaseInstance = makeDrawValidationCheck( |
| ext.drawArraysInstancedBaseInstanceWEBGL, setupDrawArrays); |
| const checkDrawElementsInstancedBaseVertexBaseInstance = makeDrawValidationCheck( |
| ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL, setupDrawElements); |
| checkDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, 0, 3, 1, 1], |
| gl.NO_ERROR, "with gl.TRIANGLES" |
| ); |
| checkDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 0, 0], |
| gl.NO_ERROR, "with gl.TRIANGLES" |
| ); |
| |
| checkDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, 0, 3, 1, 3], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], |
| "with baseInstance leading to out of bounds" |
| ); |
| checkDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 2, 0], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], |
| "with baseVertex leading to out of bounds" |
| ); |
| checkDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 0, 3], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], |
| "with baseInstance leading to out of bounds" |
| ); |
| checkDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, 3, gl.UNSIGNED_BYTE, 0, 1, 2, 3], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], |
| "with both baseVertex and baseInstance leading to out of bounds" |
| ); |
| } else { |
| const checkMultiDrawArraysInstancedBaseInstance = makeDrawValidationCheck( |
| ext.multiDrawArraysInstancedBaseInstanceWEBGL, setupDrawArrays); |
| const checkMultiDrawElementsInstancedBaseVertexBaseInstance = makeDrawValidationCheck( |
| ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL, setupDrawElements); |
| |
| // Check that drawing a single triangle works |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, 1], |
| gl.NO_ERROR, "with gl.TRIANGLES" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 1], |
| gl.NO_ERROR, "with gl.TRIANGLES" |
| ); |
| |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [3], 0, 1], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseInstance leads to out of bounds" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [2], 0, [0], 0, 1], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseVertex leads to out of bounds" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [3], 0, 1], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], "with baseInstance leads to out of bounds" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [2], 0, [3], 0, 1], |
| [gl.NO_ERROR, gl.INVALID_OPERATION], |
| "with both baseVertex and baseInstance lead to out of bounds" |
| ); |
| |
| // Zero drawcount permitted |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, 0], |
| gl.NO_ERROR, "with drawcount == 0" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 0], |
| gl.NO_ERROR, "with drawcount == 0" |
| ); |
| |
| // Check negative drawcount |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 0, -1], |
| gl.INVALID_VALUE, "with drawcount < 0" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, -1], |
| gl.INVALID_VALUE, "with drawcount < 0" |
| ); |
| |
| // Check offsets greater than array length |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 1, [3], 0, [1], 0, [0], 0, 1], |
| gl.INVALID_OPERATION, "with firstsStart >= firstsList.length" |
| ); |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 0, [3], 1, [1], 0, [0], 0, 1], |
| gl.INVALID_OPERATION, "with countsStart >= countsList.length" |
| ); |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 1, [0], 0, 1], |
| gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length" |
| ); |
| checkMultiDrawArraysInstancedBaseInstance( |
| [gl.TRIANGLES, [0], 0, [3], 0, [1], 0, [0], 1, 1], |
| gl.INVALID_OPERATION, "with baseInstancesStart >= baseInstancesList.length" |
| ); |
| |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 1, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 0, 1], |
| gl.INVALID_OPERATION, "with countsStart >= countsList.length" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 1, [1], 0, [0], 0, [0], 0, 1], |
| gl.INVALID_OPERATION, "with offsetsStart >= offsetsList.length" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 1, [0], 0, [0], 0, 1], |
| gl.INVALID_OPERATION, "with instanceCountsStart >= instanceCountsList.length" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 1, [0], 0, 1], |
| gl.INVALID_OPERATION, "with baseVerticesStart >= baseVerticesList.length" |
| ); |
| checkMultiDrawElementsInstancedBaseVertexBaseInstance( |
| [gl.TRIANGLES, [3], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [0], 0, [0], 1, 1], |
| gl.INVALID_OPERATION, "with baseInstancesStart >= baseInstancesList.length" |
| ); |
| } |
| } |
| |
| function runShaderTests(bufferUsage) { |
| const illegalBaseInstanceProgram = wtu.setupProgram(gl, ["vshaderIllegalBaseInstance", "fshader"]); |
| expectTrue(illegalBaseInstanceProgram == null, "cannot compile program with gl_BaseInstance but no extension directive"); |
| const illegalBaseVertexProgram = wtu.setupProgram(gl, ["vshaderIllegalBaseVertex", "fshader"]); |
| expectTrue(illegalBaseVertexProgram == null, "cannot compile program with gl_BaseVertex but no extension directive"); |
| |
| const x = Math.floor(width * 0.4); |
| const y = Math.floor(height * 0.4); |
| const xSize = Math.floor(width * 0.2); |
| const ySize = Math.floor(height * 0.2); |
| |
| // gl_BaseInstance and gl_InstanceID |
| gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0, 1,0, 0.5,1, 0,1, 0.5,0, 1,1 ]), bufferUsage); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); |
| |
| const baseInstanceZeroProgram = wtu.setupProgram(gl, ["vshaderBaseInstanceZero", "fshader"], ["vPosition"], [0]); |
| expectTrue(baseInstanceZeroProgram !== null, "can compile program with gl_BaseInstance"); |
| gl.useProgram(baseInstanceZeroProgram); |
| |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| if (!multiDraw) { |
| ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, 6, 1, 5); |
| } else { |
| ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, [0], 0, [6], 0, [1], 0, [5], 0, 1); |
| } |
| |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| gl.drawArrays(gl.TRIANGLES, 0, 6); |
| |
| wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_BaseInstance is 0 for non-BaseInstance draw calls"); |
| |
| const instanceIDProgram = wtu.setupProgram(gl, ["vshaderInstanceIDCheck", "fshader"], ["vPosition"], [0]); |
| expectTrue(instanceIDProgram !== null, "can compile program with gl_InstanceID"); |
| gl.useProgram(instanceIDProgram); |
| |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| if (!multiDraw) { |
| ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, 6, 1, 5); |
| } else { |
| ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, [0], 0, [6], 0, [1], 0, [5], 0, 1); |
| } |
| |
| wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_InstanceID should always starts from 0"); |
| |
| // gl_BaseVertex and gl_VertexID |
| gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer()); |
| 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); |
| gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer()); |
| gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array([0, 1, 2, 3, 4, 5]), bufferUsage); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); |
| |
| const baseVertexZeroProgram = wtu.setupProgram(gl, ["vshaderBaseVertexZero", "fshader"], ["vPosition"], [0]); |
| expectTrue(baseVertexZeroProgram !== null, "can compile program with gl_BaseVertex"); |
| gl.useProgram(baseVertexZeroProgram); |
| |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| if (!multiDraw) { |
| ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1, 3, 0); |
| } else { |
| ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, [6], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [3], 0, [0], 0, 1); |
| } |
| |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); |
| |
| wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_BaseVertex is 0 for non-BaseVertex draw calls"); |
| |
| const vertexIDProgram = wtu.setupProgram(gl, ["vshaderVertexIDCheck", "fshader"], ["vPosition"], [0]); |
| expectTrue(vertexIDProgram !== null, "can compile program with gl_VertexID"); |
| gl.useProgram(vertexIDProgram); |
| |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| if (!multiDraw) { |
| ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1, 3, 0); |
| } else { |
| ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, [6], 0, gl.UNSIGNED_BYTE, [0], 0, [1], 0, [3], 0, [0], 0, 1); |
| } |
| |
| wtu.checkCanvasRect(gl, x, y, xSize, ySize, [0, 255, 0, 255], "gl_VertexID should always starts from 0"); |
| } |
| |
| function runPixelTests() { |
| |
| function checkResult(config) { |
| const rects = []; |
| const expected = [ |
| [255, 0, 0, 255], |
| [0, 255, 0, 255], |
| [0, 0, 255, 255], |
| ]; |
| const msg = config.drawFunc.name + ( |
| config.useBaseVertexBuiltin ? ' gl_BaseVertex' : '' |
| ) + ( |
| config.useBaseInstanceBuiltin ? ' gl_BaseInstance' : ' InstanceIDArray' |
| ); |
| 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); |
| |
| rects.push(wtu.makeCheckRect( |
| center_x - Math.floor(pixelCheckSize[0] / 2), |
| center_y - Math.floor(pixelCheckSize[1] / 2), |
| pixelCheckSize[0], |
| pixelCheckSize[1], |
| [ |
| 256.0 * (1.0 - x / x_count), |
| 256.0 * (1.0 - y / y_count), |
| (!config.isDrawArrays && config.useBaseVertexBuiltin) ? 256.0 * (1.0 - y / y_count) : 255.0, |
| 255.0 |
| ], |
| msg + ' (' + x + ',' + y + ')', 1.0 |
| )); |
| } |
| } |
| wtu.checkCanvasRects(gl, rects); |
| } |
| |
| // Draw functions variations |
| |
| function drawArraysInstancedBaseInstance() { |
| const countPerDraw = y_count * 6; |
| for (let x = 0; x < x_count; x += 2) { |
| ext.drawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, 0, countPerDraw, 2, x); |
| } |
| } |
| |
| function multiDrawArraysInstancedBaseInstance() { |
| ext.multiDrawArraysInstancedBaseInstanceWEBGL(gl.TRIANGLES, drawArraysParams.firsts, 0, drawArraysParams.counts, 0, drawArraysParams.instances, 0, drawArraysParams.baseInstances, 0, drawArraysParams.drawCount); |
| } |
| |
| function drawElementsInstancedBaseVertexBaseInstance() { |
| const countPerDraw = 6; |
| for (let v = 0; v < y_count; ++v) { |
| for (let x = 0; x < x_count; x += 2) { |
| ext.drawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, countPerDraw, gl.UNSIGNED_SHORT, 0, 2, v * 4, x); |
| } |
| } |
| } |
| |
| function multiDrawElementsInstancedBaseVertexBaseInstance() { |
| ext.multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL(gl.TRIANGLES, drawElementsParams.counts, 0, gl.UNSIGNED_SHORT, drawElementsParams.offsets, 0, drawElementsParams.instances, 0, drawElementsParams.baseVertices, 0, drawElementsParams.baseInstances, 0, drawElementsParams.drawCount); |
| } |
| |
| function checkDraw(config) { |
| const program = wtu.setupProgram( |
| gl, |
| getShaderSource(x_count, y_count, config), |
| !config.useBaseInstanceBuiltin ? ['vPosition'] : ['vPosition', 'vInstanceID'] |
| ); |
| |
| gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); |
| |
| if (config.isDrawArrays) { |
| gl.bindBuffer(gl.ARRAY_BUFFER, nonIndexedVertexBuffer); |
| gl.enableVertexAttribArray(0); |
| gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); |
| } else { |
| 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); |
| } |
| |
| if (!config.useBaseInstanceBuiltin) { |
| gl.bindBuffer(gl.ARRAY_BUFFER, instanceIDBuffer); |
| gl.enableVertexAttribArray(1); |
| gl.vertexAttribPointer(1, 1, gl.FLOAT, false, 0, 0); |
| gl.vertexAttribDivisor(1, 1); |
| } |
| |
| config.drawFunc(); |
| wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); |
| |
| checkResult(config); |
| } |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawArraysInstancedBaseInstance : drawArraysInstancedBaseInstance, |
| isDrawArrays: true, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: false, |
| useBaseInstanceBuiltin: false |
| }); |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawArraysInstancedBaseInstance : drawArraysInstancedBaseInstance, |
| isDrawArrays: true, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: true, |
| useBaseInstanceBuiltin: false |
| }); |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawArraysInstancedBaseInstance : drawArraysInstancedBaseInstance, |
| isDrawArrays: true, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: false, |
| useBaseInstanceBuiltin: true |
| }); |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawArraysInstancedBaseInstance : drawArraysInstancedBaseInstance, |
| isDrawArrays: true, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: true, |
| useBaseInstanceBuiltin: true |
| }); |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawElementsInstancedBaseVertexBaseInstance : drawElementsInstancedBaseVertexBaseInstance, |
| isDrawArrays: false, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: false, |
| useBaseInstanceBuiltin: false |
| }); |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawElementsInstancedBaseVertexBaseInstance : drawElementsInstancedBaseVertexBaseInstance, |
| isDrawArrays: false, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: true, |
| useBaseInstanceBuiltin: false |
| }); |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawElementsInstancedBaseVertexBaseInstance : drawElementsInstancedBaseVertexBaseInstance, |
| isDrawArrays: false, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: false, |
| useBaseInstanceBuiltin: true |
| }); |
| |
| checkDraw({ |
| drawFunc: multiDraw ? multiDrawElementsInstancedBaseVertexBaseInstance : drawElementsInstancedBaseVertexBaseInstance, |
| isDrawArrays: false, |
| isMultiDraw: multiDraw, |
| useBaseVertexBuiltin: true, |
| useBaseInstanceBuiltin: true |
| }); |
| } |
| |
| for (let i = 0; i < bufferUsageSet.length; i++) { |
| let bufferUsage = bufferUsageSet[i]; |
| debug("Testing with BufferUsage = " + bufferUsage); |
| setupGeneralBuffers(bufferUsage); |
| runValidationTests(bufferUsage); |
| runShaderTests(bufferUsage); |
| runPixelTests(); |
| } |
| } |
| |
| runTest(); |
| |
| const successfullyParsed = true; |
| </script> |
| <script src="../../js/js-test-post.js"></script> |
| </body> |
| </html> |