diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance/extensions')
48 files changed, 14683 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt new file mode 100644 index 0000000000..9a72b67ef0 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt @@ -0,0 +1,46 @@ +--min-version 1.0.3 --max-version 1.9.9 angle-instanced-arrays.html +--min-version 1.0.3 --max-version 1.9.9 angle-instanced-arrays-out-of-bounds.html +--min-version 1.0.3 --max-version 1.9.9 ext-blend-minmax.html +--min-version 1.0.4 ext-color-buffer-half-float.html +--min-version 1.0.4 ext-float-blend.html +--min-version 1.0.4 ext-texture-compression-bptc.html +--min-version 1.0.4 ext-texture-compression-rgtc.html +--min-version 1.0.4 ext-disjoint-timer-query.html +--min-version 1.0.3 --max-version 1.9.9 ext-frag-depth.html +--min-version 1.0.3 --max-version 1.9.9 ext-shader-texture-lod.html +--min-version 1.0.3 --max-version 1.9.9 ext-sRGB.html +--min-version 1.0.2 ext-texture-filter-anisotropic.html +--min-version 1.0.2 get-extension.html +--min-version 1.0.4 khr-parallel-shader-compile.html +--max-version 1.9.9 oes-standard-derivatives.html +--max-version 1.9.9 oes-texture-float-with-canvas.html +--max-version 1.9.9 oes-texture-float-with-image-data.html +--max-version 1.9.9 oes-texture-float-with-image.html +--max-version 1.9.9 oes-texture-float-with-video.html +--max-version 1.9.9 oes-texture-float.html +--max-version 1.9.9 oes-vertex-array-object.html +--min-version 1.0.3 --max-version 1.9.9 oes-vertex-array-object-bufferData.html +--min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float.html +--min-version 1.0.3 oes-texture-float-linear.html +--min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-linear.html +--min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-canvas.html +--min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-image-data.html +--min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-image.html +--min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-video.html +--min-version 1.0.2 --max-version 1.9.9 oes-element-index-uint.html +--min-version 1.0.4 --max-version 1.9.9 oes-fbo-render-mipmap.html +webgl-debug-renderer-info.html +webgl-debug-shaders.html +--min-version 1.0.4 webgl-compressed-texture-astc.html +--min-version 1.0.4 webgl-compressed-texture-etc.html +--min-version 1.0.4 webgl-compressed-texture-etc1.html +--min-version 1.0.3 webgl-compressed-texture-pvrtc.html +--min-version 1.0.4 s3tc-and-rgtc.html +--min-version 1.0.4 webgl-compressed-texture-s3tc-srgb.html +--min-version 1.0.3 webgl-compressed-texture-size-limit.html +--min-version 1.0.2 --max-version 1.9.9 webgl-depth-texture.html +--min-version 1.0.3 --max-version 1.9.9 webgl-draw-buffers.html +--min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-broadcast-return.html +--min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-framebuffer-unsupported.html +--min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-max-draw-buffers.html +--min-version 1.0.4 webgl-multi-draw.html diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays-out-of-bounds.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays-out-of-bounds.html new file mode 100644 index 0000000000..64a063854e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays-out-of-bounds.html @@ -0,0 +1,56 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/out-of-bounds-test.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> + +<script> +"use strict"; +description("Test of drawArraysInstancedANGLE and drawElementsInstancedANGLE with out-of-bounds parameters"); + +var wtu = WebGLTestUtils; + +var gl = wtu.create3DContext(); +var ext = wtu.getExtensionWithKnownPrefixes(gl, "ANGLE_instanced_arrays"); +if (!ext) { + testPassed("No ANGLE_instanced_arrays support -- this is legal"); +} else { + testPassed("Successfully enabled ANGLE_instanced_arrays extension"); + debug(""); + debug("Test with 1 instance without instanced attributes"); + debug(""); + OutOfBoundsTest.runDrawArraysTest("ext.drawArraysInstancedANGLE(gl.TRIANGLES, $(offset), $(count), 1)", gl, wtu, ext); + debug(""); + OutOfBoundsTest.runDrawElementsTest("ext.drawElementsInstancedANGLE(gl.TRIANGLES, $(count), $(type), $(offset), 1)", gl, wtu, ext); + debug(""); + debug("Test with 2 instances without instanced attributes"); + debug(""); + OutOfBoundsTest.runDrawArraysTest("ext.drawArraysInstancedANGLE(gl.TRIANGLES, $(offset), $(count), 2)", gl, wtu, ext); + debug(""); + OutOfBoundsTest.runDrawElementsTest("ext.drawElementsInstancedANGLE(gl.TRIANGLES, $(count), $(type), $(offset), 2)", gl, wtu, ext); + debug(""); + OutOfBoundsTest.runDrawArraysInstancedTest("ext.drawArraysInstancedANGLE(gl.TRIANGLES, $(offset), $(count), $(primcount))", gl, wtu, ext); + debug(""); + OutOfBoundsTest.runDrawElementsInstancedTest("ext.drawElementsInstancedANGLE(gl.TRIANGLES, $(count), $(type), $(offset), $(primcount))", gl, wtu, ext); + debug(""); +} + +var successfullyParsed = true; +</script> + +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays.html new file mode 100644 index 0000000000..f96c732bb2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays.html @@ -0,0 +1,723 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL ANGLE_instanced_arrays 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> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing instanced draws --> +<script id="outputVertexShader" type="x-shader/x-vertex"> +attribute vec4 aPosition; +attribute vec2 aOffset; +attribute vec4 aColor; +varying vec4 vColor; +void main() { + vColor = aColor; + gl_Position = aPosition + vec4(aOffset, 0.0, 0.0); +} +</script> + +<script id="outputFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +varying vec4 vColor; +void main() { + gl_FragColor = vColor; +} +</script> + +<script id="drawArraysTestVertexShader" type="x-shader/x-vertex"> +attribute vec3 aPosition; +attribute vec3 aInstancePos; +uniform vec3 uOffset; +void main() { + gl_Position = vec4(aPosition.xyz + aInstancePos.xyz + uOffset, 1.0); +} +</script> + +<script id="drawArraysTestFragmentShader" type="x-shader/x-fragment"> +void main() { + gl_FragColor = vec4(1.0, 0, 0, 1.0); +} +</script> + +<script> +"use strict"; +description("This test verifies the functionality of the ANGLE_instanced_arrays extension, if it is available."); + +debug(""); + +const wait = ms => new Promise(resolve => setTimeout(resolve, ms)); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var vaoext = null; + +var positionLoc = 0; +var offsetLoc = 2; +var colorLoc = 3; +var program; + +if (!gl) { + testFailed("WebGL context does not exist"); + finishTest(); +} else { + testPassed("WebGL context exists"); + + runDivisorTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "ANGLE_instanced_arrays"); + if (!ext) { + testPassed("No ANGLE_instanced_arrays support -- this is legal"); + + runSupportedTest(false); + finishTest(); + } else { + testPassed("Successfully enabled ANGLE_instanced_arrays extension"); + + (async function() { + runSupportedTest(true); + + runDivisorTestEnabled(); + runUniqueObjectTest(); + + setupCanvas(); + runOutputTests(); + runDrawArraysWithOffsetTest(); + runVAOInstancingInteractionTest(); + await runANGLECorruptionTest(); + await runInvalidAttribTests(gl); + await runCompositingTests(); + finishTest(); + }()); + } +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("ANGLE_instanced_arrays") >= 0) { + if (extensionEnabled) { + testPassed("ANGLE_instanced_arrays listed as supported and getExtension succeeded"); + } else { + testFailed("ANGLE_instanced_arrays listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("ANGLE_instanced_arrays not listed as supported but getExtension succeeded"); + } else { + testPassed("ANGLE_instanced_arrays not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runDivisorTestDisabled() { + debug("Testing VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE with extension disabled"); + + var VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE = 0x88FE; + + gl.getVertexAttrib(0, VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE should not be queryable if extension is disabled"); +} + +function runDivisorTestEnabled() { + debug("Testing VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE with extension enabled"); + + shouldBe("ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE", "0x88FE"); + + var max_vertex_attribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + + for (var i = 0; i < max_vertex_attribs; ++i) { + var queried_value = gl.getVertexAttrib(i, ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); + if(queried_value == 0){ + testPassed("Vertex attribute " + i + " must has a default divisor of 0"); + } + else{ + testFailed("Default divisor of vertex attribute " + i + " should be: 0, returned value was: " + queried_value); + } + } + + ext.vertexAttribDivisorANGLE(max_vertex_attribs, 2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "vertexAttribDivisorANGLE index set greater than or equal to MAX_VERTEX_ATTRIBS should be an invalid value"); + + ext.vertexAttribDivisorANGLE(max_vertex_attribs-1, 2); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "vertexAttribDivisorANGLE index set less than MAX_VERTEX_ATTRIBS should succeed"); + + var queried_value = gl.getVertexAttrib(max_vertex_attribs-1, ext.VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE); + if(queried_value == 2){ + testPassed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE matches expecation"); + } + else{ + testFailed("Set value of VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE should be: 2, returned value was: " + queried_value); + } + + // Reset vertex attrib divisors so they cannot affect following subtests. + ext.vertexAttribDivisorANGLE(max_vertex_attribs-1, 0); +} + +function setupCanvas() { + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + gl.clearColor(0, 0, 0, 0); + + program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['aPosition', 'aOffset', 'aColor'], [positionLoc, offsetLoc, colorLoc]); + ext = gl.getExtension("ANGLE_instanced_arrays"); +} + +function runOutputTests() { + var instanceCount = 4; + + debug("Testing various draws for valid built-in function behavior"); + + var offsets = new Float32Array([ + -1.0, 1.0, + 1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0, + ]); + var offsetBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW); + gl.enableVertexAttribArray(offsetLoc); + gl.vertexAttribPointer(offsetLoc, 2, gl.FLOAT, false, 0, 0); + ext.vertexAttribDivisorANGLE(offsetLoc, 1); + + var colors = new Float32Array([ + 1.0, 0.0, 0.0, 1.0, // Red + 0.0, 1.0, 0.0, 1.0, // Green + 0.0, 0.0, 1.0, 1.0, // Blue + 1.0, 1.0, 0.0, 1.0, // Yellow + // extra data when colorLoc divisor is set back to 0 + 1.0, 1.0, 0.0, 1.0, // Yellow + 1.0, 1.0, 0.0, 1.0, // Yellow + ]); + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); + ext.vertexAttribDivisorANGLE(colorLoc, 1); + + wtu.setupUnitQuad(gl, 0); + + // Draw 1: Regular drawArrays + debug(""); + debug("Testing drawArrays with non-zero divisor"); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "vertex attrib divisor should affect regular drawArrays when the extension is enabled"); + wtu.checkCanvasRect(gl, 0, canvas.height/2, canvas.width/2, canvas.height/2, [255, 0, 0, 255]); + + // Draw 2: Draw Non-indexed instances + debug(""); + debug("Testing drawArraysInstancedANGLE"); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Test drawArraysInstancedANGLE error conditions + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, instanceCount); + wtu.checkCanvasRect(gl, 0, canvas.height/2, canvas.width/2, canvas.height/2, [255, 0, 0, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, canvas.height/2, canvas.width/2, canvas.height/2, [0, 255, 0, 255]); + wtu.checkCanvasRect(gl, 0, 0, canvas.width/2, canvas.height/2, [0, 0, 255, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, 0, canvas.width/2, canvas.height/2, [255, 255, 0, 255]); + + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, -1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawArraysInstancedANGLE cannot have a primcount less than 0"); + + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, -1, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawArraysInstancedANGLE cannot have a count less than 0"); + + ext.vertexAttribDivisorANGLE(positionLoc, 1); + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "There must be at least one vertex attribute with a divisor of zero when calling drawArraysInstancedANGLE"); + ext.vertexAttribDivisorANGLE(positionLoc, 0); + + ext.drawArraysInstancedANGLE(gl.POINTS, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstancedANGLE with POINTS should succeed"); + ext.drawArraysInstancedANGLE(gl.LINES, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstancedANGLE with LINES should succeed"); + ext.drawArraysInstancedANGLE(gl.LINE_LIST, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstancedANGLE with LINE_LIST should return succeed"); + ext.drawArraysInstancedANGLE(gl.TRIANGLE_LIST, 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstancedANGLE with TRIANGLE_LIST should succeed"); + + ext.drawArraysInstancedANGLE(desktopGL['QUAD_STRIP'], 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawArraysInstancedANGLE with QUAD_STRIP should return INVALID_ENUM"); + ext.drawArraysInstancedANGLE(desktopGL['QUADS'], 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawArraysInstancedANGLE with QUADS should return INVALID_ENUM"); + ext.drawArraysInstancedANGLE(desktopGL['POLYGON'], 0, 6, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawArraysInstancedANGLE with POLYGON should return INVALID_ENUM"); + + debug(""); + debug("Testing drawArraysInstancedANGLE with param 'first' > 0"); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.setupQuad(gl, { + positionLocation: 0, + scale: 0.5 + }); + var offsetsHalf = new Float32Array([ + -0.5, 0.5, + 0.5, 0.5, + -0.5, -0.5, + 0.5, -0.5 + ]); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsetsHalf, gl.STATIC_DRAW); + + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 3, 3, instanceCount); + var w = Math.floor(0.25*canvas.width), + h = Math.floor(0.25*canvas.height); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0.5*canvas.height, w, h, [255, 0, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0.5*canvas.height, w, h, [0, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0, w, h, [0, 0, 255, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0, w, h, [255, 255, 0, 255]); + + debug(""); + debug("Testing drawArraysInstancedANGLE with attributes 'divisor' reset to 0"); + debug("Correct rendering output: 4 yellow triangles"); + debug("Possible incorrect rendering output: missing triangles, or triangles with different color at each vertex"); + ext.vertexAttribDivisorANGLE(colorLoc, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 3, 3, instanceCount); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0.5*canvas.height, w, h, [255, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0.5*canvas.height, w, h, [255, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.25*canvas.width), 0, w, h, [255, 255, 0, 255]); + wtu.checkCanvasRect(gl, Math.ceil(0.75*canvas.width), 0, w, h, [255, 255, 0, 255]); + ext.vertexAttribDivisorANGLE(colorLoc, 1); + + wtu.setupUnitQuad(gl, 0); + wtu.setupIndexedQuad(gl, 1, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsets, gl.STATIC_DRAW); + + // Draw 3: Regular drawElements + debug(""); + debug("Testing drawElements with non-zero divisor"); + gl.clear(gl.COLOR_BUFFER_BIT); + // Point to another location in the buffer so that the draw would overflow without the divisor + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 48); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "vertex attrib divisor should affect regular drawElements when the extension is enabled"); + wtu.checkCanvasRect(gl, 0, canvas.height/2, canvas.width/2, canvas.height/2, [255, 255, 0, 255]); + // Restore the vertex attrib pointer + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); + + // Draw 4: Draw indexed instances + debug(""); + debug("Testing drawElementsInstancedANGLE"); + gl.clear(gl.COLOR_BUFFER_BIT); + ext.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.checkCanvasRect(gl, 0, canvas.height/2, canvas.width/2, canvas.height/2, [255, 0, 0, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, canvas.height/2, canvas.width/2, canvas.height/2, [0, 255, 0, 255]); + wtu.checkCanvasRect(gl, 0, 0, canvas.width/2, canvas.height/2, [0, 0, 255, 255]); + wtu.checkCanvasRect(gl, canvas.width/2, 0, canvas.width/2, canvas.height/2, [255, 255, 0, 255]); + + // Test drawElementsInstancedANGLE error conditions + ext.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, -1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawElementsInstancedANGLE cannot have a primcount less than 0"); + + ext.drawElementsInstancedANGLE(gl.TRIANGLES, -1, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "drawElementsInstancedANGLE cannot have a count less than 0"); + + ext.vertexAttribDivisorANGLE(positionLoc, 1); + ext.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "There must be at least one vertex attribute with a divisor of zero when calling drawElementsInstancedANGLE"); + ext.vertexAttribDivisorANGLE(positionLoc, 0); + + ext.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstancedANGLE with UNSIGNED_BYTE should succeed"); + + ext.drawElementsInstancedANGLE(gl.POINTS, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstancedANGLE with POINTS should succeed"); + ext.drawElementsInstancedANGLE(gl.LINES, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstancedANGLE with LINES should succeed"); + ext.drawElementsInstancedANGLE(gl.LINE_LIST, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstancedANGLE with LINE_LIST should return succeed"); + ext.drawElementsInstancedANGLE(gl.TRIANGLE_LIST, 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawElementsInstancedANGLE with TRIANGLE_LIST should succeed"); + + ext.drawElementsInstancedANGLE(desktopGL['QUAD_STRIP'], 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawElementsInstancedANGLE with QUAD_STRIP should return INVALID_ENUM"); + ext.drawElementsInstancedANGLE(desktopGL['QUADS'], 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawElementsInstancedANGLE with QUADS should return INVALID_ENUM"); + ext.drawElementsInstancedANGLE(desktopGL['POLYGON'], 6, gl.UNSIGNED_SHORT, 0, instanceCount); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "drawElementsInstancedANGLE with POLYGON should return INVALID_ENUM"); + + // Reset vertex attrib divisors so they cannot affect following subtests. + ext.vertexAttribDivisorANGLE(colorLoc, 0); + ext.vertexAttribDivisorANGLE(offsetLoc, 0); +} + +function runDrawArraysTest(program, first, count, instanceCount, offset) +{ + // Get the attribute and uniform locations + var positionLoc = gl.getAttribLocation(program, "aPosition"); + var instancePosLoc = gl.getAttribLocation(program, "aInstancePos"); + var uniformLoc = gl.getUniformLocation(program, "uOffset"); + + // Load the vertex positions + var positions = new Float32Array([ + -1, -1, + -1, 0, + 0, 0, + + 0, 0, + 0, -1, + -1, -1, + + 1, -1, + 1, 0, + 0, 0, + + 0, 0, + 0, -1, + 1, -1, + ]); + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + gl.enableVertexAttribArray(positionLoc); + gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); + + // Load the instance positions + var instancePositions = new Float32Array([ + 0, 0, + 1, 0 + ]); + var instancePositionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, instancePositionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, instancePositions, gl.STATIC_DRAW); + gl.enableVertexAttribArray(instancePosLoc); + gl.vertexAttribPointer(instancePosLoc, 2, gl.FLOAT, false, 0, 0); + + // Enable instancing + ext.vertexAttribDivisorANGLE(instancePosLoc, 1); + + // Offset + gl.uniform3fv(uniformLoc, offset); + + // Do the instanced draw + ext.drawArraysInstancedANGLE(gl.TRIANGLES, first, count, instanceCount); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawArraysInstancedANGLE should succeed"); + + // Reset vertex attrib divisors so they cannot affect following subtests. + ext.vertexAttribDivisorANGLE(instancePosLoc, 0); +} + +function runDrawArraysWithOffsetTest() +{ + debug(""); + debug("Testing that the 'first' parameter to drawArraysInstancedANGLE is only an offset into the non-instanced vertex attributes."); + // See: http://crbug.com/457269 and http://crbug.com/447140 + + var drawArraysProgram = wtu.setupProgram(gl, ["drawArraysTestVertexShader", "drawArraysTestFragmentShader"]); + + gl.clear(gl.COLOR_BUFFER_BIT); + + runDrawArraysTest(drawArraysProgram, 0, 6, 2, [0, 0, 0]); + + runDrawArraysTest(drawArraysProgram, 6, 6, 2, [-1, 1, 0]); + + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 0, 0, 255]); +} + +function runUniqueObjectTest() +{ + debug(""); + debug("Testing that getExtension() returns the same object each time"); + ext = null; + gl.getExtension("ANGLE_instanced_arrays").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("ANGLE_instanced_arrays").myProperty', '2'); +} + +function runVAOInstancingInteractionTest() +{ + debug("") + debug("Testing that ANGLE_instanced_arrays interacts correctly with OES_vertex_array_object if present"); + // See: https://github.com/KhronosGroup/WebGL/issues/1228 + + // Query the extension and store globally so shouldBe can access it + vaoext = gl.getExtension("OES_vertex_array_object"); + if (!vaoext) { + testPassed("No OES_vertex_array_object support -- this is legal"); + return; + } + + testPassed("Successfully enabled OES_vertex_array_object extension"); + + gl.useProgram(program); + + var positions = new Float32Array([ + 0.0, 1.0, // Left quad + -1.0, 1.0, + -1.0, -1.0, + 0.0, 1.0, + -1.0, -1.0, + 0.0, -1.0, + + 1.0, 1.0, // Right quad + 0.0, 1.0, + 0.0, -1.0, + 1.0, 1.0, + 0.0, -1.0, + 1.0, -1.0 + ]); + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW); + + var colors = new Float32Array([ + 1.0, 0.0, 0.0, 1.0, // Red + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + + 0.0, 0.0, 1.0, 1.0, // Blue + 0.0, 0.0, 1.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + ]); + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors, gl.STATIC_DRAW); + + // Reset the divisor of the default VAO to 0 + ext.vertexAttribDivisorANGLE(colorLoc, 0); + + // Set up VAO with an attrib divisor + var vao1 = vaoext.createVertexArrayOES(); + vaoext.bindVertexArrayOES(vao1); + { + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.enableVertexAttribArray(positionLoc); + gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); + ext.vertexAttribDivisorANGLE(colorLoc, 1); + + gl.vertexAttrib2fv(offsetLoc, [0.0, 0.0]); + } + vaoext.bindVertexArrayOES(null); + + // Set up VAO with no attrib divisor + var vao2 = vaoext.createVertexArrayOES(); + vaoext.bindVertexArrayOES(vao2); + { + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.enableVertexAttribArray(positionLoc); + gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); + // Note that no divisor is set here, which implies that it's 0 + + gl.vertexAttrib2fv(offsetLoc, [0.0, 0.0]); + } + vaoext.bindVertexArrayOES(null); + + debug(""); + debug("Ensure that Vertex Array Objects retain attrib divisors"); + + vaoext.bindVertexArrayOES(vao1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 12); + // If the divisor is properly managed by the VAO a single red quad will be drawn + wtu.checkCanvas(gl, [255, 0, 0, 255], "entire canvas should be red"); + + vaoext.bindVertexArrayOES(vao2); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 12); + // If the divisor is properly managed by the VAO a red and blue quad will be drawn. + wtu.checkCanvasRects(gl, [ + wtu.makeCheckRect(0, 0, canvas.width * 0.5, canvas.height, [255, 0, 0, 255], "left half of canvas should be red", 1), + wtu.makeCheckRect(canvas.width * 0.5, 0, canvas.width * 0.5, canvas.height, [0, 0, 255, 255], "right half of canvas should be blue", 1) + ]); + + vaoext.bindVertexArrayOES(null); +} + +async function runANGLECorruptionTest() +{ + debug("") + debug("Testing to ensure that rendering isn't corrupt due to an ANGLE bug"); + // See: https://code.google.com/p/angleproject/issues/detail?id=467 + + setupCanvas(); + + var tolerance = 2; // Amount of variance to allow in result pixels - may need to be tweaked higher + var instanceCount = 10; // Must be higher than 6 + + var offsets = new Float32Array([ + 0.0, 0.0, + 0.2, 0.0, + 0.4, 0.0, + 0.6, 0.0, + 0.8, 0.0, + 1.0, 0.0, + 1.2, 0.0, + 1.4, 0.0, + 1.6, 0.0, + 1.8, 0.0, + ]); + var offsetBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferData(gl.ARRAY_BUFFER, offsets.byteLength * 2, gl.STATIC_DRAW); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, offsets); + gl.enableVertexAttribArray(offsetLoc); + gl.vertexAttribPointer(offsetLoc, 2, gl.FLOAT, false, 0, 0); + ext.vertexAttribDivisorANGLE(offsetLoc, 1); + + var colors = new Float32Array([ + 1.0, 0.0, 0.0, 1.0, + 1.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 1.0, 1.0, + 0.0, 0.0, 1.0, 1.0, + 1.0, 0.0, 1.0, 1.0, + 1.0, 0.0, 0.0, 1.0, + 1.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 0.0, 1.0, + 0.0, 1.0, 1.0, 1.0, + ]); + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colors.byteLength * 2, gl.STATIC_DRAW); + gl.bufferSubData(gl.ARRAY_BUFFER, 0, colors); + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); + ext.vertexAttribDivisorANGLE(colorLoc, 1); + + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.setupUnitQuad(gl, 0); + + const totalIterations = 10; + for (let iteration = 0; iteration < totalIterations; ++iteration) + { + // Update the instanced data buffers outside the accessed range. + // This, plus rendering more instances than vertices, triggers the bug. + var nullData = new Float32Array(offsets.length); + gl.bindBuffer(gl.ARRAY_BUFFER, offsetBuffer); + gl.bufferSubData(gl.ARRAY_BUFFER, offsets.byteLength, nullData); + + nullData = new Float32Array(colors.length); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferSubData(gl.ARRAY_BUFFER, colors.byteLength, nullData); + + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, instanceCount); + + // Make sure each color was drawn correctly + var i; + var passed = true; + for (i = 0; i < instanceCount; ++i) { + var w = canvas.width / instanceCount; + var x = w * i; + var color = [colors[(i*4)] * 255, colors[(i*4)+1] * 255, colors[(i*4)+2] * 255, 255] + + wtu.checkCanvasRectColor( + gl, x, 0, w, canvas.height, color, tolerance, + function() {}, + function() { + passed = false; + }, debug); + } + + if (passed) { + testPassed("Passed test " + iteration + " of " + totalIterations); + } else { + testFailed("Failed test " + iteration + " of " + totalIterations); + break; + } + await wait(); + } + ext.vertexAttribDivisorANGLE(offsetLoc, 0); + ext.vertexAttribDivisorANGLE(colorLoc, 0); +} + +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 drawArraysInstancedANGLE(gl) { + const ext = gl.getExtension('ANGLE_instanced_arrays'); + if (!ext) { + return true; + } + + ext.drawArraysInstancedANGLE(gl.TRIANGLES, 0, 6, 1); + } + + function drawElementsInstancedANGLE(gl) { + const ext = gl.getExtension('ANGLE_instanced_arrays'); + if (!ext) { + return true; + } + + ext.drawElementsInstancedANGLE(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0, 1); + } + + await testFn(drawArrays); // sanity check + await testFn(drawElements); // sanity check + + await testFn(drawArraysInstancedANGLE); + await testFn(drawElementsInstancedANGLE); +} + +async function runCompositingTests() { + const compositingTestFn = createCompositingTestFn({ + webglVersion: 1, + shadersFn(gl) { + const vs = `\ + 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); +} + +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-blend-minmax.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-blend-minmax.html new file mode 100644 index 0000000000..445dcb92d4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-blend-minmax.html @@ -0,0 +1,225 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_blend_minmax Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<!-- Shaders to test output --> +<script id="outputVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +void main() { + gl_Position = vPosition; +} +</script> +<script id="outputFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +uniform vec4 uColor; +void main() { + gl_FragColor = uColor; +} +</script> + +<script> +"use strict"; +description("This test verifies the functionality of the EXT_blend_minmax extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; + +// Use the constant directly when we don't have the extension +var MIN_EXT = 0x8007; +var MAX_EXT = 0x8008; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + runBlendTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_blend_minmax"); + if (!ext) { + testPassed("No EXT_blend_minmax support -- this is legal"); + + runSupportedTest(false); + } else { + debug(""); + testPassed("Successfully enabled EXT_blend_minmax extension"); + + runSupportedTest(true); + + runBlendTestEnabled(); + runOutputTests(); + runUniqueObjectTest(); + } +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("EXT_blend_minmax") >= 0) { + if (extensionEnabled) { + testPassed("EXT_blend_minmax listed as supported and getExtension succeeded"); + } else { + testFailed("EXT_blend_minmax listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("EXT_blend_minmax not listed as supported but getExtension succeeded"); + } else { + testPassed("EXT_blend_minmax not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runBlendTestDisabled() { + debug(""); + debug("Testing blending enums with extension disabled"); + + // Set the blend equation to a known-good enum first + gl.blendEquation(gl.FUNC_ADD); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.blendEquation(MIN_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION)", "gl.FUNC_ADD"); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.blendEquation(MAX_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION)", "gl.FUNC_ADD"); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.blendEquationSeparate(MIN_EXT, gl.FUNC_ADD)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_RGB)", "gl.FUNC_ADD"); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.blendEquationSeparate(gl.FUNC_ADD, MIN_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_ALPHA)", "gl.FUNC_ADD"); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.blendEquationSeparate(MAX_EXT, gl.FUNC_ADD)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_RGB)", "gl.FUNC_ADD"); + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.blendEquationSeparate(gl.FUNC_ADD, MAX_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_ALPHA)", "gl.FUNC_ADD"); +} + +function runBlendTestEnabled() { + debug(""); + debug("Testing blending enums with extension enabled"); + + shouldBe("ext.MIN_EXT", "0x8007"); + shouldBe("ext.MAX_EXT", "0x8008"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendEquation(ext.MIN_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION)", "ext.MIN_EXT"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendEquation(ext.MAX_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION)", "ext.MAX_EXT"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendEquationSeparate(ext.MIN_EXT, gl.FUNC_ADD)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_RGB)", "ext.MIN_EXT"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_ALPHA)", "gl.FUNC_ADD"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendEquationSeparate(gl.FUNC_ADD, ext.MIN_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_RGB)", "gl.FUNC_ADD"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_ALPHA)", "ext.MIN_EXT"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendEquationSeparate(ext.MAX_EXT, gl.FUNC_ADD)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_RGB)", "ext.MAX_EXT"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_ALPHA)", "gl.FUNC_ADD"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.blendEquationSeparate(gl.FUNC_ADD, ext.MAX_EXT)"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_RGB)", "gl.FUNC_ADD"); + shouldBe("gl.getParameter(gl.BLEND_EQUATION_ALPHA)", "ext.MAX_EXT"); +} + +function runOutputTests() { + var e = 2; // Amount of variance to allow in result pixels - may need to be tweaked higher + + debug(""); + debug("Testing various draws for valid blending behavior"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + gl.enable(gl.BLEND); + gl.blendFunc(gl.ONE, gl.ONE); + + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['vPosition'], [0]); + var quadParameters = wtu.setupUnitQuad(gl, 0, 1); + var colorUniform = gl.getUniformLocation(program, "uColor"); + + + // Draw 1 + gl.blendEquation(ext.MIN_EXT); + + gl.clearColor(0.2, 0.4, 0.6, 0.8); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.uniform4f(colorUniform, 0.8, 0.6, 0.4, 0.2); + wtu.drawUnitQuad(gl); + + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [51, 102, 102, 51]); + + // Draw 2: + gl.blendEquation(ext.MAX_EXT); + + gl.clearColor(0.2, 0.4, 0.6, 0.8); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.uniform4f(colorUniform, 0.8, 0.6, 0.4, 0.2); + wtu.drawUnitQuad(gl); + + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [204, 153, 153, 204]); + + // Draw 3 + gl.blendEquationSeparate(ext.MIN_EXT, ext.MAX_EXT); + + gl.clearColor(0.2, 0.4, 0.6, 0.8); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.uniform4f(colorUniform, 0.8, 0.6, 0.4, 0.2); + wtu.drawUnitQuad(gl); + + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [51, 102, 102, 204]); + + // Draw 4 + gl.blendEquationSeparate(ext.MAX_EXT, ext.MIN_EXT); + + gl.clearColor(0.2, 0.4, 0.6, 0.8); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.uniform4f(colorUniform, 0.8, 0.6, 0.4, 0.2); + wtu.drawUnitQuad(gl); + + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [204, 153, 153, 51]); +} + +function runUniqueObjectTest() +{ + debug(""); + debug("Testing that getExtension() returns the same object each time"); + ext = null; + gl.getExtension("EXT_blend_minmax").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("EXT_blend_minmax").myProperty', '2'); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-color-buffer-half-float.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-color-buffer-half-float.html new file mode 100644 index 0000000000..752fe98e45 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-color-buffer-half-float.html @@ -0,0 +1,27 @@ +<!-- +Copyright (c) 2020 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_color_buffer_half_float Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script> +var version = 1; +</script> +<script src="../../js/tests/ext-color-buffer-half-float.js"></script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html new file mode 100644 index 0000000000..1fc29cc445 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html @@ -0,0 +1,312 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_disjoint_timer_query Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> + +<script> +"use strict"; +description("This test verifies the functionality of the EXT_disjoint_timer_query extension, if it is available."); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var query = null; +var query2 = null; +var elapsed_query = null; +var timestamp_query1 = null; +var timestamp_query2 = null; +var availability_retry = 500; +var timestamp_counter_bits = 0; + +if (!gl) { + testFailed("WebGL context does not exist"); + finishTest(); +} else { + testPassed("WebGL context exists"); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_disjoint_timer_query"); + if (!ext) { + testPassed("No EXT_disjoint_timer_query support -- this is legal"); + finishTest(); + } else { + if (wtu.getDefault3DContextVersion() > 1) { + testFailed("EXT_disjoint_timer_query must not be advertised on WebGL 2.0 contexts"); + finishTest(); + } else { + runSanityTests(); + + // Clear disjoint value. + gl.getParameter(ext.GPU_DISJOINT_EXT); + + runElapsedTimeTest(); + timestamp_counter_bits = ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT); + if (timestamp_counter_bits > 0) { + runTimeStampTest(); + } + verifyQueryResultsNotAvailable(); + + window.requestAnimationFrame(checkQueryResults); + } + } +} + +function runSanityTests() { + debug(""); + debug("Testing timer query expectations"); + + shouldBe("ext.QUERY_COUNTER_BITS_EXT", "0x8864"); + shouldBe("ext.CURRENT_QUERY_EXT", "0x8865"); + shouldBe("ext.QUERY_RESULT_EXT", "0x8866"); + shouldBe("ext.QUERY_RESULT_AVAILABLE_EXT", "0x8867"); + shouldBe("ext.TIME_ELAPSED_EXT", "0x88BF"); + shouldBe("ext.TIMESTAMP_EXT", "0x8E28"); + shouldBe("ext.GPU_DISJOINT_EXT", "0x8FBB"); + + shouldBe("ext.isQueryEXT(null)", "false"); + + shouldBeTrue("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT) === null"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeTrue("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + shouldBeTrue("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.CURRENT_QUERY_EXT) === null"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + // Certain drivers set timestamp counter bits to 0 as they don't support timestamps + shouldBeTrue("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30 || " + + "ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) === 0"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Testing time elapsed query lifecycle"); + query = ext.createQueryEXT(); + shouldBe("ext.isQueryEXT(query)", "false"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query creation must succeed."); + shouldThrow("ext.beginQueryEXT(ext.TIMESTAMP_EXT, null)"); + ext.beginQueryEXT(ext.TIMESTAMP_EXT, query); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Beginning a timestamp query should fail."); + ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); + shouldBe("ext.isQueryEXT(query)", "true"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Beginning an inactive time elapsed query should succeed."); + ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Attempting to begin an active query should fail."); + ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability of an active query should fail."); + ext.getQueryObjectEXT(query, ext.QUERY_RESULT_EXT); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result of an active query should fail."); + shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); + ext.endQueryEXT(ext.TIME_ELAPSED_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Ending an active time elapsed query should succeed."); + shouldThrow("ext.getQueryObjectEXT(null, ext.QUERY_RESULT_AVAILABLE_EXT)"); + ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Fetching query result availability after query end should succeed."); + ext.endQueryEXT(ext.TIME_ELAPSED_EXT); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Attempting to end an inactive query should fail."); + ext.queryCounterEXT(query, ext.TIMESTAMP_EXT); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Should not be able to use time elapsed query to store a timestamp."); + ext.deleteQueryEXT(query); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Query deletion must succeed."); + ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning a deleted query must fail."); + ext.getQueryObjectEXT(query, ext.QUERY_RESULT_AVAILABLE_EXT); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Fetching query result availability after query deletion should fail."); + shouldBe("ext.isQueryEXT(query)", "false"); + + debug(""); + debug("Testing timestamp counter"); + query = ext.createQueryEXT(); + shouldThrow("ext.queryCounterEXT(null, ext.TIMESTAMP_EXT)"); + ext.queryCounterEXT(query, ext.TIMESTAMP_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Timestamp counter queries should work."); + ext.deleteQueryEXT(query); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Performing parameter sanity checks"); + gl.getParameter(ext.TIMESTAMP_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getParameter timestamp calls should work."); + gl.getParameter(ext.GPU_DISJOINT_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getParameter disjoint calls should work."); + + debug(""); + debug("Testing current query conditions"); + query = ext.createQueryEXT(); + query2 = ext.createQueryEXT(); + shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); + ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query); + shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Testing failed begin query should not change the current query."); + ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query2); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Beginning an elapsed query without ending should fail."); + shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Testing beginning a timestamp query is invalid and should not change the elapsed query."); + ext.beginQueryEXT(ext.TIMESTAMP_EXT, query2) + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM); + shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "query"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Testing timestamp queries end immediately so are never current."); + ext.queryCounterEXT(query2, ext.TIMESTAMP_EXT); + shouldBe("ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.CURRENT_QUERY_EXT)", "null"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Testing ending the query should clear the current query."); + ext.endQueryEXT(ext.TIME_ELAPSED_EXT); + shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug(""); + debug("Testing beginning a elapsed query using a timestamp query should fail and not affect current query.") + ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, query2); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Switching query targets should fail."); + shouldBe("ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT)", "null"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + ext.deleteQueryEXT(query); + ext.deleteQueryEXT(query2); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors at end of sanity tests"); +} + +function runElapsedTimeTest() { + debug(""); + debug("Testing elapsed time query"); + + elapsed_query = ext.createQueryEXT(); + ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, elapsed_query); + gl.clearColor(0, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + ext.endQueryEXT(ext.TIME_ELAPSED_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Time elapsed query should have no errors"); +} + +function runTimeStampTest() { + debug(""); + debug("Testing timestamp query"); + + timestamp_query1 = ext.createQueryEXT(); + timestamp_query2 = ext.createQueryEXT(); + ext.queryCounterEXT(timestamp_query1, ext.TIMESTAMP_EXT); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + ext.queryCounterEXT(timestamp_query2, ext.TIMESTAMP_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Timestamp queries should have no errors"); +} + +function verifyQueryResultsNotAvailable() { + debug(""); + debug("Verifying queries' results don't become available too early"); + + // Verify as best as possible that the implementation doesn't + // allow a query's result to become available the same frame, by + // spin-looping for some time and ensuring that none of the + // queries' results become available. + var startTime = Date.now(); + while (Date.now() - startTime < 2000) { + gl.finish(); + if (ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT)) { + testFailed("One of the queries' results became available too early"); + return; + } + if (timestamp_counter_bits > 0) { + if (ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_AVAILABLE_EXT) || + ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT)) { + testFailed("One of the queries' results became available too early"); + return; + } + } + } + + testPassed("Queries' results didn't become available in a spin loop"); +} + +function checkQueryResults() { + if (availability_retry > 0) { + // Make a reasonable attempt to wait for the queries' results to become available. + if (!ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT) || + (timestamp_counter_bits > 0 && !ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT))) { + var error = gl.getError(); + if (error != gl.NO_ERROR) { + testFailed("getQueryObjectEXT should have no errors: " + wtu.glEnumToString(gl, error)); + debug(""); + finishTest(); + return; + } + availability_retry--; + window.requestAnimationFrame(checkQueryResults); + return; + } + } + + debug(""); + debug("Testing query results"); + + // Make sure queries are available. + shouldBe("ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); + if (timestamp_counter_bits > 0) { + shouldBe("ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); + shouldBe("ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_AVAILABLE_EXT)", "true"); + } + + var disjoint_value = gl.getParameter(ext.GPU_DISJOINT_EXT); + if (disjoint_value) { + // Cannot validate results make sense, but this is okay. + testPassed("Disjoint triggered."); + } else { + var elapsed_result = ext.getQueryObjectEXT(elapsed_query, ext.QUERY_RESULT_EXT); + if (timestamp_counter_bits > 0) { + var timestamp_result1 = ext.getQueryObjectEXT(timestamp_query1, ext.QUERY_RESULT_EXT); + var timestamp_result2 = ext.getQueryObjectEXT(timestamp_query2, ext.QUERY_RESULT_EXT); + } + // Do some basic validity checking of the elapsed time query. There's no way it should + // take more than about half a second for a no-op query. + var halfSecondInNanos = 0.5 * 1000 * 1000 * 1000; + if (elapsed_result < 0 || elapsed_result > halfSecondInNanos) { + testFailed("Time elapsed query returned invalid data: " + elapsed_result); + } else { + testPassed("Time elapsed query results were valid."); + } + + if (timestamp_counter_bits > 0) { + if (timestamp_result1 <= 0 || + timestamp_result2 <= 0 || + timestamp_result2 <= timestamp_result1) { + testFailed("Timestamp queries returned invalid data: timestamp_result1 = " + + timestamp_result1 + ", timestamp_result2 = " + timestamp_result2); + } else { + testPassed("Timestamp query results were valid."); + } + } + } + + debug(""); + finishTest(); +} +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-float-blend.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-float-blend.html new file mode 100644 index 0000000000..a825d78bef --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-float-blend.html @@ -0,0 +1,98 @@ +<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_float_blend Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/ext-float-blend.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_float_blend extension, if it is available.");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas);
+var ext = null;
+
+function extFloatBlendTests(version, config) {
+ debug("");
+ debug("Testing float32 blending without EXT_float_blend");
+ testExtFloatBlend(config.internalFormat);
+ testExtFloatBlendMRT(version, config.drawBuffers);
+ testExtFloatBlendNonFloat32Type(version, config.oesTextureHalfFloat);
+
+ const floatBlend = gl.getExtension("EXT_float_blend");
+ if (!floatBlend) {
+ testPassed("EXT_float_blend is allowed to be missing.");
+ return;
+ }
+
+ debug("");
+ debug("Testing that float32 blending is allowed with EXT_float_blend");
+ testExtFloatBlend(config.internalFormat);
+ testExtFloatBlendMRT(version, config.drawBuffers);
+ testExtFloatBlendNonFloat32Type(version, config.oesTextureHalfFloat);
+}
+
+(function(){
+ if (wtu.getDefault3DContextVersion() < 2) {
+ const oesTextureFloat = gl.getExtension("OES_texture_float");
+ if (!oesTextureFloat) {
+ testPassed("OES_texture_float is allowed to be missing.");
+ return;
+ }
+
+ const colorBufferFloat = gl.getExtension("WEBGL_color_buffer_float");
+ if (!colorBufferFloat) {
+ testPassed("WEBGL_color_buffer_float is allowed to be missing.");
+ return;
+ }
+
+ const drawBuffers = gl.getExtension("WEBGL_draw_buffers");
+ if (!drawBuffers) {
+ debug("WEBGL_draw_buffers is allowed to be missing. MRT tests will be skipped.");
+ }
+
+ const oesTextureHalfFloat = gl.getExtension("OES_texture_half_float");
+ const extColorBufferHalfFloat = gl.getExtension("EXT_color_buffer_half_float");
+ const testHalfFloat = !(!oesTextureHalfFloat || !extColorBufferHalfFloat);
+ if (!testHalfFloat) {
+ debug("OES_texture_half_float or EXT_color_buffer_half_float is allowed to be missing. NonFloat32Type tests will be skipped.");
+ }
+
+ const internalFormat = gl.RGBA;
+ extFloatBlendTests(1, {
+ internalFormat, drawBuffers, oesTextureHalfFloat
+ });
+ } else {
+ const colorBufferFloat = gl.getExtension("EXT_color_buffer_float");
+ if (!colorBufferFloat) {
+ testPassed("EXT_color_buffer_float is allowed to be missing.");
+ return;
+ }
+
+ const internalFormat = gl.RGBA32F;
+ extFloatBlendTests(2, {
+ internalFormat
+ });
+ }
+})();
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-frag-depth.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-frag-depth.html new file mode 100644 index 0000000000..ee2a6df0cd --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-frag-depth.html @@ -0,0 +1,289 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_frag_depth Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing fragment depth writing --> + +<!-- Shader omitting the required #extension pragma --> +<script id="missingPragmaFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +void main() { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragDepthEXT = 1.0; +} +</script> + +<!-- Shader to test macro definition --> +<script id="macroFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +void main() { +#ifdef GL_EXT_frag_depth + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +#else + // Error expected + #error no GL_EXT_frag_depth; +#endif +} +</script> + +<!-- Shader with required #extension pragma --> +<script id="testFragmentShader" type="x-shader/x-fragment"> +#extension GL_EXT_frag_depth : enable +precision mediump float; +void main() { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragDepthEXT = 1.0; +} +</script> +<!-- Shaders to link with test fragment shaders --> +<script id="goodVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +void main() { + gl_Position = vPosition; +} +</script> +<!-- Shaders to test output --> +<script id="outputVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +void main() { + gl_Position = vPosition; +} +</script> +<script id="outputFragmentShader" type="x-shader/x-fragment"> +#extension GL_EXT_frag_depth : enable +precision mediump float; +uniform float uDepth; +void main() { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + gl_FragDepthEXT = uDepth; +} +</script> + +<script> +"use strict"; +description("This test verifies the functionality of the EXT_frag_depth extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; + +// Run all tests once. +runAllTests(); + +// Run all tests against with a new context to test for any cache issues. +debug(""); +debug("Testing new context to catch cache errors"); +gl = wtu.create3DContext(); +ext = null; +runAllTests(); + +function runAllTests() { + if (!gl) { + testFailed("WebGL context does not exist"); + } else { + testPassed("WebGL context exists"); + + runShaderTests(false); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_frag_depth"); + if (!ext) { + testPassed("No EXT_frag_depth support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled EXT_frag_depth extension"); + + runSupportedTest(true); + + runShaderTests(true); + runOutputTests(); + runUniqueObjectTest(); + + // Run deferred link tests. + runDeferredLinkTests(); + } + } + +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("EXT_frag_depth") >= 0) { + if (extensionEnabled) { + testPassed("EXT_frag_depth listed as supported and getExtension succeeded"); + } else { + testFailed("EXT_frag_depth listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("EXT_frag_depth not listed as supported but getExtension succeeded"); + } else { + testPassed("EXT_frag_depth not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runShaderTests(extensionEnabled) { + debug(""); + debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled")); + + // Expect the macro shader to succeed ONLY if enabled + var macroFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "macroFragmentShader"); + if (extensionEnabled) { + if (macroFragmentProgram) { + // Expected result + testPassed("GL_EXT_frag_depth defined in shaders when extension is enabled"); + } else { + testFailed("GL_EXT_frag_depth not defined in shaders when extension is enabled"); + } + } else { + if (macroFragmentProgram) { + testFailed("GL_EXT_frag_depth defined in shaders when extension is disabled"); + } else { + testPassed("GL_EXT_frag_depth not defined in shaders when extension disabled"); + } + } + + // Always expect the shader missing the #pragma to fail (whether enabled or not) + var missingPragmaFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "missingPragmaFragmentShader"); + if (missingPragmaFragmentProgram) { + testFailed("Shader built-ins allowed without #extension pragma"); + } else { + testPassed("Shader built-ins disallowed without #extension pragma"); + } + + // Try to compile a shader using the built-ins that should only succeed if enabled + var testFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShader"); + if (extensionEnabled) { + if (testFragmentProgram) { + testPassed("Shader built-ins compiled successfully when extension enabled"); + } else { + testFailed("Shader built-ins failed to compile when extension enabled"); + } + } else { + if (testFragmentProgram) { + testFailed("Shader built-ins compiled successfully when extension disabled"); + } else { + testPassed("Shader built-ins failed to compile when extension disabled"); + } + } +} + +function runOutputTests() { + var e = 2; // Amount of variance to allow in result pixels - may need to be tweaked higher + + debug("Testing various draws for valid built-in function behavior"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + + // Enable depth testing with a clearDepth of 0.5 + // This makes it so that fragments are only rendered when + // gl_fragDepthEXT is < 0.5 + gl.clearDepth(0.5); + gl.enable(gl.DEPTH_TEST); + + var positionLoc = 0; + var texcoordLoc = 1; + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['vPosition'], [0]); + var quadParameters = wtu.setupUnitQuad(gl, 0, 1); + var depthUniform = gl.getUniformLocation(program, "uDepth"); + + // Draw 1: Greater than clear depth + gl.uniform1f(depthUniform, 1.0); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 255, 255, 255]); + + // Draw 2: Less than clear depth + gl.uniform1f(depthUniform, 0.0); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvasRect(gl, 0, 0, canvas.width, canvas.height, [255, 0, 0, 255]); +} + +function runUniqueObjectTest() +{ + debug("Testing that getExtension() returns the same object each time"); + ext = null; + gl.getExtension("EXT_frag_depth").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("EXT_frag_depth").myProperty', '2'); +} + +function runDeferredLinkTests() { + debug(""); + debug("Testing deferred shader compilation tests."); + + // Test for compilation failures that are caused by missing extensions + // do not succeed if extensions are enabled during linking. This would + // only happen for deferred shader compilations. + + // First test if link succeeds with extension enabled. + var glEnabled = wtu.create3DContext(); + var extEnabled = glEnabled.getExtension("EXT_frag_depth"); + if (!extEnabled) { + testFailed("Deferred link test expects the extension to be supported"); + } + + var vertexShader = wtu.loadShaderFromScript(glEnabled, "goodVertexShader"); + var fragmentShader = wtu.loadShaderFromScript(glEnabled, "macroFragmentShader"); + + if (!vertexShader || !fragmentShader) { + testFailed("Could not create good shaders."); + return; + } + + var program = wtu.setupProgram(glEnabled, [vertexShader, fragmentShader]); + + if (!program) { + testFailed("Compilation with extension enabled failed."); + return; + } + + // Create new context to test link failure without extension enabled. + var glDeferred = wtu.create3DContext(); + + var vertexShader = wtu.loadShaderFromScript(glDeferred, "goodVertexShader", glDeferred.VERTEX_SHADER, undefined, undefined, true); + var fragmentShader = wtu.loadShaderFromScript(glDeferred, "macroFragmentShader", glDeferred.FRAGMENT_SHADER, undefined, undefined, true); + + if (vertexShader == null || fragmentShader == null) { + testFailed("Could not create shaders."); + return; + } + + // Shader compilations should have failed due to extensions not enabled. + glDeferred.getExtension("EXT_frag_depth"); + var program = wtu.setupProgram(glDeferred, [vertexShader, fragmentShader]); + if (program) { + testFailed("Compilation with extension disabled then linking with extension enabled should have failed."); + return; + } + + testPassed("Compilation with extension disabled then linking with extension enabled."); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html new file mode 100644 index 0000000000..7e8353f7f2 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html @@ -0,0 +1,468 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"/> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<canvas id="canvas" width="16" height="16" style="width: 50px; height: 50px; border: 1px solid black;"></canvas> + +<!-- Shaders to test output --> +<script id="vertexShader" type="x-shader/x-vertex"> +attribute vec4 aPosition; +void main() { + gl_Position = aPosition; +} +</script> + +<script id="fragmentShader" type="x-shader/x-fragment"> +precision mediump float; +uniform float uColor; +void main() { + gl_FragColor = vec4(uColor, uColor, uColor, 1); +} +</script> + +<script id="vshader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +attribute vec2 texCoord0; +varying vec2 texCoord; +void main() +{ + gl_Position = vPosition; + texCoord = texCoord0; +} +</script> + +<script id="fshader" type="x-shader/x-fragment"> +precision mediump float; +uniform sampler2D tex; +varying vec2 texCoord; +void main() +{ + gl_FragColor = texture2D(tex, texCoord); +} +</script> + +<script> +"use strict"; + +var wtu = WebGLTestUtils; +var canvas; +var gl; +var ext = null; + +var extConstants = { + "SRGB_EXT": 0x8C40, + "SRGB_ALPHA_EXT": 0x8C42, + "SRGB8_ALPHA8_EXT": 0x8C43, + "FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT": 0x8210 +}; + +function getExtension() { + ext = gl.getExtension("EXT_sRGB"); +} + +function listsExtension() { + var supported = gl.getSupportedExtensions(); + return (supported.indexOf("EXT_sRGB") >= 0); +} + +function toVec3String(val) { + if (typeof(val) == 'number') { + return toVec3String([val, val, val]); + } + return '[' + val[0] + ', ' + val[1] + ', ' + val[2] + ']'; +} + +var e = 2; // Amount of variance to allow in result pixels - may need to be tweaked higher + +function expectResult(target) { + wtu.checkCanvasRect(gl, + Math.floor(gl.drawingBufferWidth / 2), + Math.floor(gl.drawingBufferHeight / 2), + 1, + 1, + [target, target, target, 255], + undefined, + e); +} + +function createGreysRGBTexture(gl, color, format) { + var numPixels = gl.drawingBufferWidth * gl.drawingBufferHeight; + var elements; + switch (format) { + case ext.SRGB_EXT: elements = 3; break; + case ext.SRGB_ALPHA_EXT: elements = 4; break; + default: return null; + } + + var size = numPixels * elements; + var buf = new Uint8Array(size); + for (var ii = 0; ii < numPixels; ++ii) { + var off = ii * elements; + buf[off + 0] = color; + buf[off + 1] = color; + buf[off + 2] = color; + if (format == ext.SRGB_ALPHA_EXT) { + buf[off + 3] = 255; + } + } + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, + 0, + format, + gl.drawingBufferWidth, + gl.drawingBufferHeight, + 0, + format, + gl.UNSIGNED_BYTE, + buf); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + return tex; +} + +function testValidFormat(fn, internalFormat, formatName, enabled) { + if (enabled) { + fn(internalFormat); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "was able to create type " + formatName); + } else { + testInvalidFormat(fn, internalFormat, formatName, enabled); + } +} + +function testInvalidFormat(fn, internalFormat, formatName, enabled) { + fn(internalFormat); + var err = gl.getError(); + if (err == gl.NO_ERROR) { + testFailed("should NOT be able to create type " + formatName); + } else if (err == gl.INVALID_ENUM || err == gl.INVALID_VALUE) { + testPassed("not able to create invalid format: " + formatName); + } +} + +var textureFormatFixture = { + desc: "Checking texture formats", + create: function(format) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, + 0, // level + format, // internalFormat + gl.drawingBufferWidth, // width + gl.drawingBufferHeight, // height + 0, // border + format, // format + gl.UNSIGNED_BYTE, // type + null); // data + }, + tests: [ + { + desc: "Checking valid formats", + fn: testValidFormat, + formats: [ 'SRGB_EXT', 'SRGB_ALPHA_EXT' ] + }, + { + desc: "Checking invalid formats", + fn: testInvalidFormat, + formats: [ 'SRGB8_ALPHA8_EXT' ] + } + ] +}; + +var renderbufferFormatFixture = { + desc: "Checking renderbuffer formats", + create: function(format) { + var rbo = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); + gl.renderbufferStorage(gl.RENDERBUFFER, + format, + gl.drawingBufferWidth, + gl.drawingBufferHeight); + }, + tests: [ + { + desc: "Checking valid formats", + fn: testValidFormat, + formats: [ 'SRGB8_ALPHA8_EXT' ] + }, + { + desc: "Checking invalid formats", + fn: testInvalidFormat, + formats: [ 'SRGB_EXT', 'SRGB_ALPHA_EXT' ] + } + ] +}; + + +description("Test sRGB texture support"); + +debug(""); +debug("Canvas.getContext"); + +canvas = document.getElementById("canvas"); +gl = wtu.create3DContext(canvas); +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + debug(""); + debug("Checking sRGB texture support with extension disabled"); + + runFormatTest(textureFormatFixture, false); + runFormatTest(renderbufferFormatFixture, false); + + debug(""); + debug("Checking sRGB texture support"); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("EXT_sRGB"); + + if (!ext) { + testPassed("No EXT_sRGB support -- this is legal"); + + runSupportedTest(false); + finishTest(); + } else { + testPassed("Successfully enabled EXT_sRGB extension"); + + runSupportedTest(true); + + gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight); + + runConstantsTest(); + runFormatTest(textureFormatFixture, true); + runFormatTest(renderbufferFormatFixture, true); + runTextureReadConversionTest(); + runFramebufferTextureConversionTest(ext.SRGB_EXT); + runFramebufferTextureConversionTest(ext.SRGB_ALPHA_EXT); + runFramebufferRenderbufferConversionTest(); + runGenerateMipmapTest(); + runLoadFromImageTest(function() { + finishTest(); + }); + } +} + +function runConstantsTest() { + debug(""); + debug("Checking extension constants values"); + + for (var constant in extConstants) { + if (constant in ext) { + if (extConstants[constant] != ext[constant]) { + testFailed("Value of " + constant + " should be: " + extConstants[constant] + ", was: " + ext[constant]); + } else { + testPassed("Value of " + constant + " was expected value: " + extConstants[constant]); + } + } else { + testFailed(constant + " not found in extension object"); + } + } +} + +function runSupportedTest(extensionEnabled) { + if (listsExtension()) { + if (extensionEnabled) { + testPassed("EXT_sRGB listed as supported and getExtension succeeded"); + } else { + testFailed("EXT_sRGB listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("EXT_sRGB not listed as supported but getExtension succeeded"); + } else { + testPassed("EXT_sRGB not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runFormatTest(fixture, enabled) { + debug(""); + debug(fixture.desc); + + for (var tt = 0; tt < fixture.tests.length; ++tt) { + var test = fixture.tests[tt]; + debug(test.desc); + + for (var ii = 0; ii < test.formats.length; ++ii) { + var formatName = test.formats[ii]; + test.fn(fixture.create, extConstants[formatName], "ext." + formatName, enabled); + } + + if (tt != fixture.tests.length - 1) + debug(""); + } +} + +function runTextureReadConversionTest() { + debug(""); + debug("Test the conversion of colors from sRGB to linear on texture read"); + + // Draw + var conversions = [ + [ 0, 0 ], + [ 63, 13 ], + [ 127, 54 ], + [ 191, 133 ], + [ 255, 255 ] + ]; + + var program = wtu.setupTexturedQuad(gl); + gl.uniform1i(gl.getUniformLocation(program, "tex"), 0); + + for (var ii = 0; ii < conversions.length; ii++) { + var tex = createGreysRGBTexture(gl, conversions[ii][0], ext.SRGB_EXT); + wtu.drawUnitQuad(gl); + expectResult(conversions[ii][1]); + } +} + +function runFramebufferTextureConversionTest(format) { + var formatString; + var validFormat; + switch (format) { + case ext.SRGB_EXT: formatString = "sRGB"; validFormat = false; break; + case ext.SRGB_ALPHA_EXT: formatString = "sRGB_ALPHA"; validFormat = true; break; + default: return null; + } + debug(""); + debug("Test " + formatString + " framebuffer attachments." + (validFormat ? "" : " (Invalid)")); + + var program = wtu.setupProgram(gl, ['vertexShader', 'fragmentShader'], ['aPosition'], [0]); + var tex = createGreysRGBTexture(gl, 0, format); + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT)', 'ext.SRGB_EXT'); + + if (validFormat) { + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + debug(""); + debug("Test the conversion of colors from linear to " + formatString + " on framebuffer (texture) write"); + + // Draw + var conversions = [ + [ 0, 0 ], + [ 13, 63 ], + [ 54, 127 ], + [ 133, 191 ], + [ 255, 255 ] + ]; + + wtu.setupUnitQuad(gl, 0); + + for (var ii = 0; ii < conversions.length; ii++) { + gl.uniform1f(gl.getUniformLocation(program, "uColor"), conversions[ii][0]/255.0); + wtu.drawUnitQuad(gl, [0, 0, 0, 0]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + expectResult(conversions[ii][1]); + } + } else { + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + + wtu.setupUnitQuad(gl, 0); + gl.uniform1f(gl.getUniformLocation(program, "uColor"), 0.5); + wtu.drawUnitQuad(gl, [0, 0, 0, 0]); + wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION); + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); +} + +function runFramebufferRenderbufferConversionTest() { + debug(""); + debug("Test the conversion of colors from linear to sRGB on framebuffer (renderbuffer) write"); + + function createsRGBFramebuffer(gl, width, height) { + var rbo = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); + gl.renderbufferStorage(gl.RENDERBUFFER, ext.SRGB8_ALPHA8_EXT, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, + gl.RENDERBUFFER, rbo); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT)', 'ext.SRGB_EXT'); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + return fbo; + } + + // Draw + var conversions = [ + [ 0, 0 ], + [ 13, 63 ], + [ 54, 127 ], + [ 133, 191 ], + [ 255, 255 ] + ]; + + var program = wtu.setupProgram(gl, ['vertexShader', 'fragmentShader'], ['aPosition'], [0]); + wtu.setupUnitQuad(gl, 0); + var fbo = createsRGBFramebuffer(gl, gl.drawingBufferWidth, gl.drawingBufferHeight); + + for (var ii = 0; ii < conversions.length; ii++) { + gl.uniform1f(gl.getUniformLocation(program, "uColor"), conversions[ii][0]/255.0); + wtu.drawUnitQuad(gl, [0, 0, 0, 0]); + expectResult(conversions[ii][1]); + } +} + +function runLoadFromImageTest(callback) { + debug(""); + debug("Tests to ensure that SRGB textures can successfully use image elements as their source"); + + var img = wtu.makeImage("../../resources/gray-1024x1024.jpg", function() { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, ext.SRGB_EXT, ext.SRGB_EXT, gl.UNSIGNED_BYTE, img); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.SRGB_ALPHA_EXT, ext.SRGB_ALPHA_EXT, gl.UNSIGNED_BYTE, img); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + callback(); + }, function() { + testFailed("Image could not be loaded"); + callback(); + }); +} + +function runGenerateMipmapTest() +{ + debug(""); + debug("GenerateMipmaps for sRGB textures is forbidden"); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.SRGB_ALPHA_EXT, 2, 2, 0, ext.SRGB_ALPHA_EXT, + gl.UNSIGNED_BYTE, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + gl.generateMipmap(gl.TEXTURE_2D); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); + + gl.deleteTexture(tex); +} + +var successfullyParsed = true; +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-shader-texture-lod.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-shader-texture-lod.html new file mode 100644 index 0000000000..30176bd590 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-shader-texture-lod.html @@ -0,0 +1,341 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_shader_texture_lod 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> +<div id="description"></div> +<canvas id="canvas" style="width: 256px; height: 256px;"> </canvas> +<canvas id="canvas2" style="width: 256px; height: 256px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing texture LOD functions --> + +<!-- Shader omitting the required #extension pragma --> +<script id="missingPragmaFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +varying vec2 texCoord0v; +uniform float lod; +uniform sampler2D tex; +void main() { + vec4 color = texture2DLodEXT(tex, texCoord0v, lod); + gl_FragColor = color; +} +</script> + +<!-- Shader to test macro definition --> +<script id="macroFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +void main() { +#ifdef GL_EXT_shader_texture_lod + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +#else + // Error expected + #error no GL_EXT_shader_texture_lod; +#endif +} +</script> + +<!-- Shader with required #extension pragma --> +<script id="testFragmentShader" type="x-shader/x-fragment"> +#extension GL_EXT_shader_texture_lod : enable +precision mediump float; +varying vec2 texCoord0v; +uniform float lod; +uniform sampler2D tex; +void main() { + vec4 color = texture2DLodEXT(tex, texCoord0v, lod); + gl_FragColor = color; +} +</script> + +<!-- Shaders to link with test fragment shaders --> +<script id="goodVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +attribute vec2 texCoord0; +varying vec2 texCoord0v; +void main() { + texCoord0v = texCoord0; + gl_Position = vPosition; +} +</script> + +<!-- Shaders to test output --> +<script id="outputVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +attribute vec2 texCoord0; +varying vec2 texCoord0v; +void main() { + texCoord0v = texCoord0; + gl_Position = vPosition; +} +</script> +<script id="outputFragmentShader" type="x-shader/x-fragment"> +#extension GL_EXT_shader_texture_lod : require +precision mediump float; +varying vec2 texCoord0v; +uniform float lod; +uniform sampler2D tex; +void main() { + vec4 color = texture2DLodEXT(tex, texCoord0v, lod); + gl_FragColor = color; +} +</script> + +<script> +description("This test verifies the functionality of the EXT_shader_texture_lod extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; + +// Run all tests once. +runAllTests(); + +// Run all tests against with a new context to test for any cache issues. +debug(""); +debug("Testing new context to catch cache errors"); +var canvas2 = document.getElementById("canvas2"); +gl = wtu.create3DContext(canvas2); +ext = null; +runAllTests(); + +function runAllTests() { + if (!gl) { + testFailed("WebGL context does not exist"); + } else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runShaderTests(false); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("EXT_shader_texture_lod"); + if (!ext) { + testPassed("No EXT_shader_texture_lod support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled EXT_shader_texture_lod extension"); + + runSupportedTest(true); + + runShaderTests(true); + runOutputTests(); + runUniqueObjectTest(); + runReferenceCycleTest(); + + // Run deferred link tests. + runDeferredLinkTests(); + } + } + +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("EXT_shader_texture_lod") >= 0) { + if (extensionEnabled) { + testPassed("EXT_shader_texture_lod listed as supported and getExtension succeeded"); + } else { + testFailed("EXT_shader_texture_lod listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("EXT_shader_texture_lod not listed as supported but getExtension succeeded"); + } else { + testPassed("EXT_shader_texture_lod not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runShaderTests(extensionEnabled) { + debug(""); + debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled")); + + // Expect the macro shader to succeed ONLY if enabled + var macroFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "macroFragmentShader"); + if (extensionEnabled) { + if (macroFragmentProgram) { + // Expected result + testPassed("GL_EXT_shader_texture_lod defined in shaders when extension is enabled"); + } else { + testFailed("GL_EXT_shader_texture_lod not defined in shaders when extension is enabled"); + } + } else { + if (macroFragmentProgram) { + testFailed("GL_EXT_shader_texture_lod defined in shaders when extension is disabled"); + } else { + testPassed("GL_EXT_shader_texture_lod not defined in shaders when extension disabled"); + } + } + + // Always expect the shader missing the #pragma to fail (whether enabled or not) + var missingPragmaFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "missingPragmaFragmentShader"); + if (missingPragmaFragmentProgram) { + testFailed("Shader built-ins allowed without #extension pragma"); + } else { + testPassed("Shader built-ins disallowed without #extension pragma"); + } + + // Try to compile a shader using the built-ins that should only succeed if enabled + var testFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShader"); + if (extensionEnabled) { + if (testFragmentProgram) { + testPassed("Shader built-ins compiled successfully when extension enabled"); + } else { + testFailed("Shader built-ins failed to compile when extension enabled"); + } + } else { + if (testFragmentProgram) { + testFailed("Shader built-ins compiled successfully when extension disabled"); + } else { + testPassed("Shader built-ins failed to compile when extension disabled"); + } + } +} + +function runOutputTests() { + debug(""); + debug("Testing various draws for valid built-in function behavior"); + gl.viewport(0, 0, canvas.width, canvas.height); + + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['vPosition', 'texCoord0'], [0, 1]); + var quadParameters = wtu.setupUnitQuad(gl, 0, 1); + + var colors = [ + {name: 'red', color:[255, 0, 0, 255]}, + {name: 'green', color:[0, 255, 0, 255]}, + {name: 'blue', color:[0, 0, 255, 255]}, + {name: 'yellow', color:[255, 255, 0, 255]}, + {name: 'magenta', color:[255, 0, 255, 255]}, + {name: 'cyan', color:[0, 255, 255, 255]}, + {name: 'pink', color:[255, 128, 128, 255]}, + {name: 'gray', color:[128, 128, 128, 255]}, + {name: 'light green', color:[128, 255, 128, 255]}, + ]; + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + + for (var ii = 0; ii < colors.length; ++ii) { + var color = colors[ii]; + var size = Math.pow(2, colors.length - ii - 1); + wtu.fillTexture(gl, tex, size, size, color.color, ii); + } + + var loc = gl.getUniformLocation(program, "lod"); + + for (var ii = 0; ii < colors.length; ++ii) { + gl.uniform1f(loc, ii); + var color = colors[ii]; + wtu.drawUnitQuad(gl); + wtu.checkCanvas( + gl, color.color, + "256x256 texture drawn to 256x256 dest with lod = " + ii + + " should be " + color.name); + } + + wtu.glErrorShouldBe(gl, gl.NO_ERROR); +} + +function runUniqueObjectTest() +{ + debug(""); + debug("Testing that getExtension() returns the same object each time"); + ext = null; + gl.getExtension("EXT_shader_texture_lod").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("EXT_shader_texture_lod").myProperty', '2'); +} + +function runReferenceCycleTest() +{ + // create some reference cycles. The goal is to see if they cause leaks. The point is that + // some browser test runners have instrumentation to detect leaked refcounted objects. + debug(""); + debug("Testing reference cycles between context and extension objects"); + var ext = gl.getExtension("EXT_shader_texture_lod"); + + // create cycle between extension and context, since the context has to hold a reference to the extension + ext.context = gl; + + // create a self-cycle on the extension object + ext.ext = ext; +} + +function runDeferredLinkTests() { + debug(""); + debug("Testing deferred shader compilation tests."); + + // Test for compilation failures that are caused by missing extensions + // do not succeed if extensions are enabled during linking. This would + // only happen for deferred shader compilations. + + // First test if link succeeds with extension enabled. + var glEnabled = wtu.create3DContext(); + var extEnabled = glEnabled.getExtension("EXT_shader_texture_lod"); + if (!extEnabled) { + testFailed("Deferred link test expects the extension to be supported"); + } + + var vertexShader = wtu.loadShaderFromScript(glEnabled, "goodVertexShader"); + var fragmentShader = wtu.loadShaderFromScript(glEnabled, "macroFragmentShader"); + + if (!vertexShader || !fragmentShader) { + testFailed("Could not create good shaders."); + return; + } + + var program = wtu.setupProgram(glEnabled, [vertexShader, fragmentShader]); + + if (!program) { + testFailed("Compilation with extension enabled failed."); + return; + } + + // Create new context to test link failure without extension enabled. + var glDeferred = wtu.create3DContext(); + + var vertexShader = wtu.loadShaderFromScript(glDeferred, "goodVertexShader", glDeferred.VERTEX_SHADER, undefined, undefined, true); + var fragmentShader = wtu.loadShaderFromScript(glDeferred, "macroFragmentShader", glDeferred.FRAGMENT_SHADER, undefined, undefined, true); + + if (vertexShader == null || fragmentShader == null) { + testFailed("Could not create shaders."); + return; + } + + // Shader compilations should have failed due to extensions not enabled. + glDeferred.getExtension("EXT_shader_texture_lod"); + var program = wtu.setupProgram(glDeferred, [vertexShader, fragmentShader]); + if (program) { + testFailed("Compilation with extension disabled then linking with extension enabled should have failed."); + return; + } + + testPassed("Compilation with extension disabled then linking with extension enabled."); +} + +debug(""); +successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-bptc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-bptc.html new file mode 100644 index 0000000000..5b6c10f044 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-bptc.html @@ -0,0 +1,159 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_texture_compression_bptc Conformance Tests</title> +<LINK rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/compressed-texture-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the EXT_texture_compression_bptc extension, if it is available."); + +debug(""); + +var validFormats = { + COMPRESSED_RGBA_BPTC_UNORM_EXT: 0x8E8C, + COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT: 0x8E8D, + COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT: 0x8E8E, + COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT: 0x8E8F +}; + +function expectedByteLength(width, height, format) { + return Math.ceil(width / 4) * Math.ceil(height / 4) * 16; +} + +function getBlockDimensions(format) { + return {width: 4, height: 4}; +} + +var wtu = WebGLTestUtils; +var ctu = CompressedTextureUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var gl = wtu.create3DContext(); +var ext; + +var formats = null; + +function runTestExtension() { + // Test that enum values are listed correctly in supported formats and in the extension object. + ctu.testCompressedFormatsListed(gl, validFormats); + ctu.testCorrectEnumValuesInExt(ext, validFormats); + // Test that texture upload buffer size is validated correctly. + ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions); + // Test TexSubImage validation on dimensions + // CompressedTexSubImage* will result in an + // INVALID_OPERATION error only if one of the following conditions occurs: + // * <width> is not a multiple of four, and <width> plus <xoffset> is not + // equal to TEXTURE_WIDTH; + // * <height> is not a multiple of four, and <height> plus <yoffset> is + // not equal to TEXTURE_HEIGHT; or + // * <xoffset> or <yoffset> is not a multiple of four. + ctu.testTexSubImageDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + 16, 16, [ + { xoffset: 0, yoffset: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "height is not a multiple of 4" }, + { xoffset: 0, yoffset: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "width is not a multiple of 4" }, + { xoffset: 1, yoffset: 0, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "xoffset is not a multiple of 4" }, + { xoffset: 0, yoffset: 1, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "yoffset is not a multiple of 4" }, + { xoffset: 12, yoffset: 12, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + ]); + + // Test TexImage validation on level dimensions combinations. + // When level equals 0, width and height must be a multiple of 4. + // When level is larger than 0, this constraint doesn't apply. + + let npotExpectation, npotMessage; + if (contextVersion >= 2) { + npotExpectation = gl.NO_ERROR; + npotMessage = "valid"; + } else { + npotExpectation = gl.INVALID_VALUE; + npotMessage = "invalid"; + } + + ctu.testTexImageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { level: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "level is 0, height is not a multiple of 4" }, + { level: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "level is 0, width is not a multiple of 4" }, + { level: 0, width: 2, height: 2, + expectation: gl.INVALID_OPERATION, message: "level is 0, width is not a multiple of 4" }, + { level: 0, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + { level: 1, width: 1, height: 1, + expectation: gl.INVALID_OPERATION, message: "implied base mip 2x2 is invalid" }, + { level: 1, width: 1, height: 2, + expectation: gl.INVALID_OPERATION, message: "implied base mip 2x4 is invalid" }, + { level: 1, width: 2, height: 1, + expectation: gl.INVALID_OPERATION, message: "implied base mip 4x2 is invalid" }, + { level: 1, width: 2, height: 2, + expectation: gl.NO_ERROR, message: "implied base mip 4x4 is valid" }, + { level: 2, width: 1, height: 3, + expectation: npotExpectation, message: "implied base mip 4x12 is " + npotMessage }, + ]); + + // Test that BPTC enums are not accepted by texImage2D + { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RGBA_BPTC_UNORM_EXT, 4, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_RGBA_BPTC_UNORM_EXT fails with texImage2D"); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT, 4, 4, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT fails with texImage2D"); + + if (contextVersion >= 2) { + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT, 4, 4, 0, gl.RGB, gl.FLOAT, null); + wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT fails with texImage2D"); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT, 4, 4, 0, gl.RGB, gl.FLOAT, null); + wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT fails with texImage2D"); + } + + gl.deleteTexture(tex); + } +}; + +function runTest() { + if (!gl) { + testFailed("context does not exist"); + } else { + testPassed("context exists"); + + ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4); + + ext = gl.getExtension("EXT_texture_compression_bptc"); + + wtu.runExtensionSupportedTest(gl, "EXT_texture_compression_bptc", ext !== null); + + if (ext !== null) { + runTestExtension(); + } + } +} + +runTest(); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html new file mode 100644 index 0000000000..70dcf9ba7b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html @@ -0,0 +1,162 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_texture_compression_rgtc Conformance Tests</title> +<LINK rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/compressed-texture-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the EXT_texture_compression_rgtc extension, if it is available."); + +debug(""); + +var validFormats = { + COMPRESSED_RED_RGTC1_EXT: 0x8DBB, + COMPRESSED_SIGNED_RED_RGTC1_EXT: 0x8DBC, + COMPRESSED_RED_GREEN_RGTC2_EXT: 0x8DBD, + COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: 0x8DBE +}; + +function expectedByteLength(width, height, format) { + if (format == validFormats.COMPRESSED_RED_RGTC1_EXT || format == validFormats.COMPRESSED_SIGNED_RED_RGTC1_EXT) { + return Math.ceil(width / 4) * Math.ceil(height / 4) * 8; + } + else { + return Math.ceil(width / 4) * Math.ceil(height / 4) * 16; + } +} + +function getBlockDimensions(format) { + return {width: 4, height: 4}; +} + +var wtu = WebGLTestUtils; +var ctu = CompressedTextureUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var gl = wtu.create3DContext(); +var ext; + +var formats = null; + +function runTestExtension() { + // Test that enum values are listed correctly in supported formats and in the extension object. + ctu.testCompressedFormatsListed(gl, validFormats); + ctu.testCorrectEnumValuesInExt(ext, validFormats); + // Test that texture upload buffer size is validated correctly. + ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions); + // Test TexSubImage validation on dimensions + // CompressedTexSubImage* will result in an + // INVALID_OPERATION error only if one of the following conditions occurs: + // * <width> is not a multiple of four, and <width> plus <xoffset> is not + // equal to TEXTURE_WIDTH; + // * <height> is not a multiple of four, and <height> plus <yoffset> is + // not equal to TEXTURE_HEIGHT; or + // * <xoffset> or <yoffset> is not a multiple of four. + ctu.testTexSubImageDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + 16, 16, [ + { xoffset: 0, yoffset: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "height is not a multiple of 4" }, + { xoffset: 0, yoffset: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "width is not a multiple of 4" }, + { xoffset: 1, yoffset: 0, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "xoffset is not a multiple of 4" }, + { xoffset: 0, yoffset: 1, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "yoffset is not a multiple of 4" }, + { xoffset: 12, yoffset: 12, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + ]); + + // Test TexImage validation on level dimensions combinations. + // When level equals 0, width and height must be a multiple of 4. + // When level is larger than 0, this constraint doesn't apply. + + let npotExpectation, npotMessage; + if (contextVersion >= 2) { + npotExpectation = gl.NO_ERROR; + npotMessage = "valid"; + } else { + npotExpectation = gl.INVALID_VALUE; + npotMessage = "invalid"; + } + + ctu.testTexImageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { level: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "level is 0, height is not a multiple of 4" }, + { level: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "level is 0, width is not a multiple of 4" }, + { level: 0, width: 2, height: 2, + expectation: gl.INVALID_OPERATION, message: "level is 0, width is not a multiple of 4" }, + { level: 0, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + { level: 1, width: 1, height: 1, + expectation: gl.INVALID_OPERATION, message: "implied base mip 2x2 is invalid" }, + { level: 1, width: 1, height: 2, + expectation: gl.INVALID_OPERATION, message: "implied base mip 2x4 is invalid" }, + { level: 1, width: 2, height: 1, + expectation: gl.INVALID_OPERATION, message: "implied base mip 4x2 is invalid" }, + { level: 1, width: 2, height: 2, + expectation: gl.NO_ERROR, message: "implied base mip 4x4 is valid" }, + { level: 2, width: 1, height: 3, + expectation: npotExpectation, message: "implied base mip 4x12 is " + npotMessage }, + ]); + + // Test that RGTC enums are not accepted by texImage2D + if (contextVersion >= 2) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RED_RGTC1_EXT, 4, 4, 0, gl.RED, gl.UNSIGNED_BYTE, null); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_RED_RGTC1_EXT fails with texImage2D"); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_SIGNED_RED_RGTC1_EXT, 4, 4, 0, gl.RED, gl.BYTE, null); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_SIGNED_RED_RGTC1_EXT fails with texImage2D"); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RED_GREEN_RGTC2_EXT, 4, 4, 0, gl.RG, gl.UNSIGNED_BYTE, null); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_RED_GREEN_RGTC2_EXT fails with texImage2D"); + + gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, 4, 4, 0, gl.RG, gl.BYTE, null); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT fails with texImage2D"); + + gl.deleteTexture(tex); + } +}; + +function runTest() { + if (!gl) { + testFailed("context does not exist"); + } else { + testPassed("context exists"); + + ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4); + + ext = gl.getExtension("EXT_texture_compression_rgtc"); + + wtu.runExtensionSupportedTest(gl, "EXT_texture_compression_rgtc", ext !== null); + + if (ext !== null) { + runTestExtension(); + } + } +} + +runTest(); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-filter-anisotropic.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-filter-anisotropic.html new file mode 100644 index 0000000000..1376d3e402 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-filter-anisotropic.html @@ -0,0 +1,26 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL EXT_texture_filter_anisotropic Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script> +var contextVersion = 1; +</script> +<script src="../../js/tests/ext-texture-filter-anisotropic.js"></script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/get-extension.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/get-extension.html new file mode 100644 index 0000000000..8d9a9bc6de --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/get-extension.html @@ -0,0 +1,99 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL Extension Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script> +"use strict"; + +var pseudoRandom = (function() { + var seed = 3; + return function() { + seed = (seed * 11 + 17) % 25; + return seed / 25; + }; +})(); + +var randomizeCase = function(str) { + var newChars = []; + for (var ii = 0; ii < str.length; ++ii) { + var c = str.substr(ii, 1); + var m = (pseudoRandom() > 0.5) ? c.toLowerCase() : c.toUpperCase(); + newChars.push(m); + } + return newChars.join(""); +}; + +description(); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); + +var ii; + +debug("check every extension advertised can be enabled"); +debug(""); +var extensionNames = gl.getSupportedExtensions(); +var extensionNamesLower = []; +for (ii = 0; ii < extensionNames.length; ++ii) { + extensionNamesLower.push(extensionNames[ii].toLowerCase()); +} + +for (ii = 0; ii < extensionNames.length; ++ii) { + var originalName = extensionNames[ii]; + var mixedName = randomizeCase(originalName); + var extension = gl.getExtension(mixedName); + assertMsg(extension, "able to get " + originalName + " as " + mixedName); + if (extension) { + var kTestString = "this is a test"; + var kTestNumber = 123; + var kTestFunction = function() { }; + var kTestObject = { }; + extension.testStringProperty = kTestString; + extension.testNumberProperty = kTestNumber; + extension.testFunctionProperty = kTestFunction; + extension.testObjectProperty = kTestObject; + webglHarnessCollectGarbage(); + var extension2 = gl.getExtension(originalName); + assertMsg( + extension === extension2, + "calling getExtension twice for the same extension returns the same object"); + assertMsg( + extension2.testStringProperty === kTestString && + extension2.testFunctionProperty === kTestFunction && + extension2.testObjectProperty === kTestObject && + extension2.testNumberProperty === kTestNumber, + "object returned by 2nd call to getExtension has same properties"); + + var prefixedVariants = wtu.getExtensionPrefixedNames(originalName); + for (var jj = 0; jj < prefixedVariants.length; ++jj) { + assertMsg( + gl.getExtension(prefixedVariants[jj]) === null || + extensionNamesLower.indexOf(prefixedVariants[jj].toLowerCase()) !== -1, + "getExtension('" + prefixedVariants[jj] + "') returns an object only if the name is returned by getSupportedExtensions"); + } + } + debug(""); +} + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html new file mode 100644 index 0000000000..2915a2d2de --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html @@ -0,0 +1,220 @@ +<!-- +Copyright (c) 2020 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<style> +.spinner { + width: 100px; + height: 100px; + border: 20px solid transparent; + border-top: 20px solid black; + border-radius: 100%; + text-align: center; + padding: 10px; +} +@keyframes rotation { + from { transorm: rotate(0); } + to { transform: rotate(360deg); } +} +</style> +</head> +<body> +<div id="description"></div> +<button onclick='compileShaders()'>The spinners below should not stutter when you click this button.</button> +<div class="spinner" style="animation: rotation 2s infinite linear;">CSS</div> +<div class="spinner" id=spinner>JS</div> +<div id="console"></div> +<canvas id=canvas></canvas> +<script> +"use strict"; +description("Test KHR_parallel_shader_compile"); + +function spinSpinner() { + let degrees = (performance.now() / 1000 / 2 % 1.) * 360; + spinner.style.transform = `rotate(${degrees}deg)`; + requestAnimationFrame(spinSpinner); +} +spinSpinner(); + +const wtu = WebGLTestUtils; + +const gl = wtu.create3DContext(); +const loseContext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_lose_context"); + +let counter = 0; +const vertexSource = (extra) => ` +void main() { + vec4 result = vec4(0.${counter++}); +${extra || ''} + gl_Position = result; +}`; +const fragmentSource = (extra) => ` +precision highp float; +void main() { + vec4 result = vec4(0.${counter++}); +${extra || ''} + gl_FragColor = result; +}`; + +let vs = gl.createShader(gl.VERTEX_SHADER); +let fs = gl.createShader(gl.FRAGMENT_SHADER); +let program = gl.createProgram(); +gl.attachShader(program, vs); +gl.attachShader(program, fs); + +const COMPLETION_STATUS_KHR = 0x91B1; + +gl.shaderSource(vs, vertexSource()); +gl.compileShader(vs); +let status = gl.getShaderParameter(vs, COMPLETION_STATUS_KHR); +if (status !== null) testFailed('Extension disabled, status should be null'); +wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "extension disabled"); + +gl.shaderSource(fs, fragmentSource()); +gl.compileShader(fs); + +gl.linkProgram(program); +status = gl.getProgramParameter(program, COMPLETION_STATUS_KHR); +if (status !== null) testFailed('Extension disabled, status should be null'); +wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "extension disabled"); + +const ext = wtu.getExtensionWithKnownPrefixes(gl, "KHR_parallel_shader_compile"); + +let successfullyParsed = false; + +let extraCode = ''; + +(async () => { + + if (!ext) { + testPassed("No KHR_parallel_shader_compile support -- this is legal"); + } else { + testPassed("Successfully enabled KHR_parallel_shader_compile extension"); + + shouldBe("ext.COMPLETION_STATUS_KHR", "0x91B1"); + + debug("Checking that status is a boolean."); + gl.shaderSource(vs, vertexSource()); + gl.compileShader(vs); + let status = gl.getShaderParameter(vs, COMPLETION_STATUS_KHR); + if (status !== true && status !== false) testFailed("status should be a boolean"); + + gl.linkProgram(program); + status = gl.getProgramParameter(program, COMPLETION_STATUS_KHR); + if (status !== true && status !== false) testFailed("status should be a boolean"); + + const minimumShaderCompileDurationMs = 500; + debug(`Constructing shader that takes > ${minimumShaderCompileDurationMs} ms to compile.`); + let measuredCompileDuration = 0; + extraCode = '\n if (true) { result += vec4(0.0000001); }'; + for (let i = 0; measuredCompileDuration < minimumShaderCompileDurationMs; i++) { + extraCode += extraCode; + extraCode += extraCode; + if (i < 4) continue; + gl.shaderSource(vs, vertexSource(extraCode)); + gl.shaderSource(fs, fragmentSource(extraCode)); + gl.compileShader(vs); + gl.compileShader(fs); + gl.linkProgram(program); + const start = performance.now(); + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + testFailed(`Shaders failed to compile. + program: ${gl.getProgramInfoLog(program)} + vs: ${gl.getShaderInfoLog(vs)} + fs: ${gl.getShaderInfoLog(fs)}`); + break; + } + measuredCompileDuration = performance.now() - start; + } + + debug(''); + gl.shaderSource(vs, vertexSource(extraCode)); + gl.shaderSource(fs, fragmentSource(extraCode)); + gl.compileShader(vs); + gl.compileShader(fs); + gl.linkProgram(program); + + let start = performance.now(); + gl.getShaderParameter(fs, COMPLETION_STATUS_KHR); + gl.getShaderParameter(vs, COMPLETION_STATUS_KHR); + let duration = performance.now() - start; + if (duration > 100) + testFailed(`Querying shader status should not wait for compilation. Took ${duration} ms`); + + let frames = 0; + const maximumTimeToWait = measuredCompileDuration * 4; + while (!gl.getProgramParameter(program, COMPLETION_STATUS_KHR) + && performance.now() - start < maximumTimeToWait) { + frames++; + await new Promise(requestAnimationFrame); + } + duration = performance.now() - start; + if (!gl.getProgramParameter(program, COMPLETION_STATUS_KHR)) { + testFailed(`Program took longer than ${maximumTimeToWait} ms to compile. Expected: ${measuredCompileDuration} ms, actual: ${duration} ms`); + } else if (!gl.getShaderParameter(vs, COMPLETION_STATUS_KHR) || !gl.getShaderParameter(fs, COMPLETION_STATUS_KHR)) { + testFailed('Program linked before shaders finished compiling.'); + } else if (frames <= 6) { + testFailed(`Program should have taken many more than 6 frames to compile. Actual value: ${frames} frames, duration ${performance.now() - start} ms.`); + } else { + console.log(`COMPLETION_STATUS_KHR sucessfully transitioned from false to true in ${frames} frames and ${duration} ms.`); + testPassed(`COMPLETION_STATUS_KHR sucessfully transitioned from false to true`); + } + + + debug("Checking that status is true when context is lost."); + if (loseContext) { + gl.shaderSource(vs, vertexSource(extraCode)); + gl.shaderSource(fs, fragmentSource(extraCode)); + gl.compileShader(vs); + gl.compileShader(fs); + gl.linkProgram(program); + loseContext.loseContext(); + status = gl.getShaderParameter(vs, COMPLETION_STATUS_KHR); + if (status !== true) testFailed("shader status should be true when context is lost"); + status = gl.getProgramParameter(program, COMPLETION_STATUS_KHR); + if (status !== true) testFailed("program status should be true when context is lost"); + loseContext.restoreContext(); + vs = gl.createShader(gl.VERTEX_SHADER); + fs = gl.createShader(gl.FRAGMENT_SHADER); + program = gl.createProgram(); + } + } + finishTest(); +})(); + +async function compileShaders() { + console.log('Compiling shaders'); + const gl = canvas.getContext('webgl'); + const vs = gl.createShader(gl.VERTEX_SHADER); + const fs = gl.createShader(gl.FRAGMENT_SHADER); + const program = gl.createProgram(); + gl.getExtension(wtu.getExtensionWithKnownPrefixes(gl, "KHR_parallel_shader_compile")); + gl.attachShader(program, vs); + gl.attachShader(program, fs); + gl.shaderSource(vs, vertexSource(extraCode)); + gl.shaderSource(fs, fragmentSource(extraCode)); + gl.compileShader(vs); + gl.compileShader(fs); + gl.linkProgram(program); + while (!gl.getProgramParameter(program, COMPLETION_STATUS_KHR)) { + gl.getShaderParameter(vs, COMPLETION_STATUS_KHR); + gl.getShaderParameter(fs, COMPLETION_STATUS_KHR); + await new Promise(requestAnimationFrame); + } + gl.getProgramParameter(program, gl.LINK_STATUS); + console.log('Compilation finished.'); +} + +</script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-element-index-uint.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-element-index-uint.html new file mode 100644 index 0000000000..f30acaba28 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-element-index-uint.html @@ -0,0 +1,428 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL OES_element_index_uint Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> + +<script id="vs" type="x-shader/x-vertex"> +attribute vec4 vPosition; +attribute vec4 vColor; +varying vec4 color; +void main() { + gl_Position = vPosition; + color = vColor; +} +</script> +<script id="fs" type="x-shader/x-fragment"> +precision mediump float; +varying vec4 color; +void main() { + gl_FragColor = color; +} +</script> + +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the OES_element_index_uint extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var gl = null; +var ext = null; +var canvas = null; + +// Test both STATIC_DRAW and DYNAMIC_DRAW as a regression test +// for a bug in ANGLE which has since been fixed. +for (var ii = 0; ii < 2; ++ii) { + canvas = document.createElement("canvas"); + canvas.width = 50; + canvas.height = 50; + + gl = wtu.create3DContext(canvas); + + if (!gl) { + testFailed("WebGL context does not exist"); + } else { + testPassed("WebGL context exists"); + + var drawType = (ii == 0) ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW; + debug("Testing " + ((ii == 0) ? "STATIC_DRAW" : "DYNAMIC_DRAW")); + + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("OES_element_index_uint"); + if (!ext) { + testPassed("No OES_element_index_uint support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled OES_element_index_uint extension"); + + runSupportedTest(true); + + runDrawTests(drawType); + + // These tests are tweaked duplicates of the buffers/index-validation* tests + // using unsigned int indices to ensure that behavior remains consistent + runIndexValidationTests(drawType); + runCopiesIndicesTests(drawType); + runResizedBufferTests(drawType); + runVerifiesTooManyIndicesTests(drawType); + runCrashWithBufferSubDataTests(drawType); + + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + } + } +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("OES_element_index_uint") >= 0) { + if (extensionEnabled) { + testPassed("OES_element_index_uint listed as supported and getExtension succeeded"); + } else { + testFailed("OES_element_index_uint listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("OES_element_index_uint not listed as supported but getExtension succeeded"); + } else { + testPassed("OES_element_index_uint not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runDrawTests(drawType) { + debug("Test that draws with unsigned integer indices produce the expected results"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + + var program = wtu.setupSimpleColorProgram(gl); + + function setupDraw(s) { + // Create a vertex buffer that cannot be fully indexed via shorts + var quadArrayLen = 65537 * 3; + var quadArray = new Float32Array(quadArrayLen); + + // Leave all but the last 4 values zero-ed out + var idx = quadArrayLen - 12; + + // Initialized the last 4 values to a quad + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = 0.0; + + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = 0.0; + + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = 0.0; + + quadArray[idx++] = 1.0 * s; + quadArray[idx++] = -1.0 * s; + quadArray[idx++] = 0.0; + + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, quadArray, drawType); + + // Create an unsigned int index buffer that indexes the last 4 vertices + var baseIndex = (quadArrayLen / 3) - 4; + + var indexObject = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array([ + baseIndex + 0, + baseIndex + 1, + baseIndex + 2, + baseIndex + 2, + baseIndex + 3, + baseIndex + 0]), drawType); + + var opt_positionLocation = 0; + gl.enableVertexAttribArray(opt_positionLocation); + gl.vertexAttribPointer(opt_positionLocation, 3, gl.FLOAT, false, 0, 0); + }; + function testPixel(blockList, allowList) { + function testList(list, expected) { + for (var n = 0; n < list.length; n++) { + var l = list[n]; + var x = -Math.floor(l * canvas.width / 2) + canvas.width / 2; + var y = -Math.floor(l * canvas.height / 2) + canvas.height / 2; + wtu.checkCanvasRect(gl, x, y, 1, 1, [expected, expected, expected], + "Draw should pass", 2); + } + } + testList(blockList, 0); + testList(allowList, 255); + }; + function verifyDraw(s) { + gl.clearColor(1.0, 1.0, 1.0, 1.0); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + wtu.setFloatDrawColor(gl, [0.0, 0.0, 0.0, 1.0]); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, 0); + + var blockList = []; + var allowList = []; + var points = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]; + for (var n = 0; n < points.length; n++) { + if (points[n] <= s) { + blockList.push(points[n]); + } else { + allowList.push(points[n]); + } + } + testPixel(blockList, allowList); + }; + + setupDraw(0.5); + verifyDraw(0.5); +} + +function runIndexValidationTests(drawType) { + description("Tests that index validation verifies the correct number of indices"); + + function sizeInBytes(type) { + switch (type) { + case gl.BYTE: + case gl.UNSIGNED_BYTE: + return 1; + case gl.SHORT: + case gl.UNSIGNED_SHORT: + return 2; + case gl.INT: + case gl.UNSIGNED_INT: + case gl.FLOAT: + return 4; + default: + throw "unknown type"; + } + } + + var program = wtu.loadStandardProgram(gl); + + // 3 vertices => 1 triangle, interleaved data + var dataComplete = new Float32Array([0, 0, 0, 1, + 0, 0, 1, + 1, 0, 0, 1, + 0, 0, 1, + 1, 1, 1, 1, + 0, 0, 1]); + var dataIncomplete = new Float32Array([0, 0, 0, 1, + 0, 0, 1, + 1, 0, 0, 1, + 0, 0, 1, + 1, 1, 1, 1]); + var indices = new Uint32Array([0, 1, 2]); + + debug("Testing with valid indices"); + + var bufferComplete = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, bufferComplete); + gl.bufferData(gl.ARRAY_BUFFER, dataComplete, drawType); + var elements = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elements); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, drawType); + gl.useProgram(program); + var vertexLoc = gl.getAttribLocation(program, "a_vertex"); + var normalLoc = gl.getAttribLocation(program, "a_normal"); + gl.vertexAttribPointer(vertexLoc, 4, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 0); + gl.enableVertexAttribArray(vertexLoc); + gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 4 * sizeInBytes(gl.FLOAT)); + gl.enableVertexAttribArray(normalLoc); + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + + debug("Testing with out-of-range indices"); + + var bufferIncomplete = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, bufferIncomplete); + gl.bufferData(gl.ARRAY_BUFFER, dataIncomplete, drawType); + gl.vertexAttribPointer(vertexLoc, 4, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 0); + gl.enableVertexAttribArray(vertexLoc); + gl.disableVertexAttribArray(normalLoc); + debug("Enable vertices, valid"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + debug("Enable normals, out-of-range"); + gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 4 * sizeInBytes(gl.FLOAT)); + gl.enableVertexAttribArray(normalLoc); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + wtu.shouldGenerateGLError(gl, [gl.INVALID_OPERATION, gl.NO_ERROR], + 'gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + + debug("Test with enabled attribute that does not belong to current program"); + + gl.disableVertexAttribArray(normalLoc); + var extraLoc = Math.max(vertexLoc, normalLoc) + 1; + gl.enableVertexAttribArray(extraLoc); + debug("Enable an extra attribute with null"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); + debug("Enable an extra attribute with insufficient data buffer"); + gl.vertexAttribPointer(extraLoc, 3, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), 4 * sizeInBytes(gl.FLOAT)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + debug("Pass large negative index to vertexAttribPointer"); + gl.vertexAttribPointer(normalLoc, 3, gl.FLOAT, false, 7 * sizeInBytes(gl.FLOAT), -2000000000 * sizeInBytes(gl.FLOAT)); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); +} + +function runCopiesIndicesTests(drawType) { + debug("Test that client data is always copied during bufferData and bufferSubData calls"); + + var program = wtu.loadStandardProgram(gl); + + gl.useProgram(program); + var vertexObject = gl.createBuffer(); + gl.enableVertexAttribArray(0); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + // 4 vertices -> 2 triangles + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0,0, 0,1,0, 1,0,0, 1,1,0 ]), drawType); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + + var indexObject = gl.createBuffer(); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject); + var indices = new Uint32Array([ 10000, 0, 1, 2, 3, 10000 ]); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, drawType); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 4)"); + var indexValidationError = wtu.shouldGenerateGLError(gl, [gl.INVALID_OPERATION, gl.NO_ERROR], + "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 0)"); + wtu.shouldGenerateGLError(gl, indexValidationError, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 8)"); + indices[0] = 2; + indices[5] = 1; + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 4)"); + wtu.shouldGenerateGLError(gl, indexValidationError, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 0)"); + wtu.shouldGenerateGLError(gl, indexValidationError, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 8)"); +} + +function runResizedBufferTests(drawType) { + debug("Test that updating the size of a vertex buffer is properly noticed by the WebGL implementation."); + + var program = wtu.setupProgram(gl, ["vs", "fs"], ["vPosition", "vColor"]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after initialization"); + + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( + [-1,1,0, 1,1,0, -1,-1,0, + -1,-1,0, 1,1,0, 1,-1,0]), drawType); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after vertex setup"); + + var texCoordObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array( + [0,0, 1,0, 0,1, + 0,1, 1,0, 1,1]), drawType); + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after texture coord setup"); + + // Now resize these buffers because we want to change what we're drawing. + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + -1,1,0, 1,1,0, -1,-1,0, 1,-1,0, + -1,1,0, 1,1,0, -1,-1,0, 1,-1,0]), drawType); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after vertex redefinition"); + gl.bindBuffer(gl.ARRAY_BUFFER, texCoordObject); + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array([ + 255, 0, 0, 255, + 255, 0, 0, 255, + 255, 0, 0, 255, + 255, 0, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255]), drawType); + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, false, 0, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after texture coordinate / color redefinition"); + + var numQuads = 2; + var indices = new Uint32Array(numQuads * 6); + for (var ii = 0; ii < numQuads; ++ii) { + var offset = ii * 6; + var quad = (ii == (numQuads - 1)) ? 4 : 0; + indices[offset + 0] = quad + 0; + indices[offset + 1] = quad + 1; + indices[offset + 2] = quad + 2; + indices[offset + 3] = quad + 2; + indices[offset + 4] = quad + 1; + indices[offset + 5] = quad + 3; + } + var indexObject = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, drawType); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after setting up indices"); + gl.drawElements(gl.TRIANGLES, numQuads * 6, gl.UNSIGNED_INT, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after drawing"); +} + +function runVerifiesTooManyIndicesTests(drawType) { + description("Tests that index validation for drawElements does not examine too many indices"); + + var program = wtu.loadStandardProgram(gl); + + gl.useProgram(program); + var vertexObject = gl.createBuffer(); + gl.enableVertexAttribArray(0); + gl.disableVertexAttribArray(1); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + // 4 vertices -> 2 triangles + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 0,0,0, 0,1,0, 1,0,0, 1,1,0 ]), drawType); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + + var indexObject = gl.createBuffer(); + + debug("Test out of range indices") + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexObject); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array([ 10000, 0, 1, 2, 3, 10000 ]), drawType); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 4)"); + var indexValidationError = wtu.shouldGenerateGLError(gl, [gl.INVALID_OPERATION, gl.NO_ERROR], + "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 0)"); + wtu.shouldGenerateGLError(gl, indexValidationError, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 8)"); +} + +function runCrashWithBufferSubDataTests(drawType) { + debug('Verifies that the index validation code which is within bufferSubData does not crash.') + + var elementBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 256, drawType); + var data = new Uint32Array(127); + gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, 64, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "after attempting to update a buffer outside of the allocated bounds"); + testPassed("bufferSubData, when buffer object was initialized with null, did not crash"); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-fbo-render-mipmap.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-fbo-render-mipmap.html new file mode 100644 index 0000000000..b560a4c89a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-fbo-render-mipmap.html @@ -0,0 +1,92 @@ +<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL OES_fbo_render_mipmap Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas id="canvas" width="128" height="128"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the OES_fbo_render_mipmap extension, if it is available.");
+
+var wtu = WebGLTestUtils;
+var canvas = document.getElementById("canvas");
+var gl = wtu.create3DContext(canvas);
+var ext = null;
+
+(function(){
+
+ const oesFboRenderMipmap = gl.getExtension("OES_fbo_render_mipmap");
+ if (!oesFboRenderMipmap) {
+ testPassed("OES_fbo_render_mipmap is allowed to be missing.");
+ return;
+ }
+
+ let texture = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texture);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);
+
+ let colors = [
+ [255, 0, 0, 255],
+ [255, 127, 0, 255],
+ [255, 255, 0, 255],
+ [0, 255, 0, 255],
+ [0, 255, 255, 255],
+ [0, 127, 255, 255],
+ [0, 0, 255, 255],
+ [255, 0, 255, 255],
+ ];
+
+ let fbos = [];
+ let maxLevel = 7;
+ for (let level = 0; level <= maxLevel; level++) {
+ let size = Math.pow(2, maxLevel - level);
+ gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, size, size, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ let fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, level);
+ fbos.push(fbo);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Any level of a texture can be attached to a framebuffer object.");
+ }
+
+ for (let level = 0; level <= maxLevel; level++) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbos[level]);
+ shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
+ gl.clearColor(colors[level][0] / 255, colors[level][1] / 255, colors[level][2] / 255, colors[level][3] / 255);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+
+ let program = wtu.setupTexturedQuad(gl);
+
+ for (let level = 0; level <= maxLevel; level++) {
+ let size = Math.pow(2, maxLevel - level);
+ // We use differrent viewport sizes to affect which miplevel is fetched from the texture.
+ gl.viewport(0, 0, size, size);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, colors[level]);
+ }
+
+})();
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+
+</body>
+</html>
+<!--
+Copyright (c) 2019 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-standard-derivatives.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-standard-derivatives.html new file mode 100644 index 0000000000..f6f4de0243 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-standard-derivatives.html @@ -0,0 +1,444 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL OES_standard_derivatives Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing standard derivatives --> + +<!-- Shader omitting the required #extension pragma --> +<script id="missingPragmaFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +varying vec2 texCoord; +void main() { + float dx = dFdx(texCoord.x); + float dy = dFdy(texCoord.y); + float w = fwidth(texCoord.x); + gl_FragColor = vec4(dx, dy, w, 1.0); +} +</script> + +<!-- Shader to test macro definition --> +<script id="macroFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +void main() { +#ifdef GL_OES_standard_derivatives + gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +#else + // Error expected + #error no GL_OES_standard_derivatives; +#endif +} +</script> + +<!-- Shader with required #extension pragma --> +<script id="testFragmentShader" type="x-shader/x-fragment"> +#extension GL_OES_standard_derivatives : enable +precision mediump float; +varying vec2 texCoord; +void main() { + float dx = dFdx(texCoord.x); + float dy = dFdy(texCoord.y); + float w = fwidth(texCoord.x); + gl_FragColor = vec4(dx, dy, w, 1.0); +} +</script> +<!-- Shader with #extension after other code --> +<script id="testFragmentShaderWithExtensionNotAtTop" type="x-shader/x-fragment"> +precision mediump float; +varying vec2 texCoord; +void main() { +#extension GL_OES_standard_derivatives : enable + float dx = dFdx(texCoord.x); + float dy = dFdy(texCoord.y); + float w = fwidth(texCoord.x); + gl_FragColor = vec4(dx, dy, w, 1.0); +} +</script> +<!-- Shaders to link with test fragment shaders --> +<script id="goodVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +varying vec2 texCoord; +void main() { + texCoord = vPosition.xy; + gl_Position = vPosition; +} +</script> +<!-- Shaders to test output --> +<script id="outputVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +varying vec4 position; +void main() { + position = vPosition; + gl_Position = vPosition; +} +</script> +<script id="outputFragmentShader" type="x-shader/x-fragment"> +#extension GL_OES_standard_derivatives : enable +precision mediump float; +varying vec4 position; +void main() { + float dzdx = dFdx(position.z); + float dzdy = dFdy(position.z); + float fw = fwidth(position.z); + gl_FragColor = vec4(abs(dzdx) * 40.0, abs(dzdy) * 40.0, fw * 40.0, 1.0); +} +</script> + +<script> +"use strict"; +description("This test verifies the functionality of the OES_standard_derivatives extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; + +// Run all tests once. +runAllTests(); + +// Run all tests against with a new context to test for any cache issues. +debug(""); +debug("Testing new context to catch cache errors"); +gl = wtu.create3DContext(); +ext = null; +runAllTests(); + +function runAllTests() { + if (!gl) { + testFailed("WebGL context does not exist"); + } else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runHintTestDisabled(); + runShaderTests(false); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("OES_standard_derivatives"); + if (!ext) { + testPassed("No OES_standard_derivatives support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled OES_standard_derivatives extension"); + + runSupportedTest(true); + + runHintTestEnabled(); + runShaderTests(true); + runOutputTests(); + runUniqueObjectTest(); + + // Run deferred link tests. + runDeferredLinkTests(); + } + } + +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("OES_standard_derivatives") >= 0) { + if (extensionEnabled) { + testPassed("OES_standard_derivatives listed as supported and getExtension succeeded"); + } else { + testFailed("OES_standard_derivatives listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("OES_standard_derivatives not listed as supported but getExtension succeeded"); + } else { + testPassed("OES_standard_derivatives not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runHintTestDisabled() { + debug("Testing FRAGMENT_SHADER_DERIVATIVE_HINT_OES with extension disabled"); + + // Use the constant directly as we don't have the extension + var FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B; + + gl.getParameter(FRAGMENT_SHADER_DERIVATIVE_HINT_OES); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "FRAGMENT_SHADER_DERIVATIVE_HINT_OES should not be queryable if extension is disabled"); + + gl.hint(FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.DONT_CARE); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "hint should not accept FRAGMENT_SHADER_DERIVATIVE_HINT_OES if extension is disabled"); +} + +function runHintTestEnabled() { + debug("Testing FRAGMENT_SHADER_DERIVATIVE_HINT_OES with extension enabled"); + + shouldBe("ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES", "0x8B8B"); + + gl.getParameter(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "FRAGMENT_SHADER_DERIVATIVE_HINT_OES query should succeed if extension is enabled"); + + // Default value is DONT_CARE + if (gl.getParameter(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES) == gl.DONT_CARE) { + testPassed("Default value of FRAGMENT_SHADER_DERIVATIVE_HINT_OES is DONT_CARE"); + } else { + testFailed("Default value of FRAGMENT_SHADER_DERIVATIVE_HINT_OES is not DONT_CARE"); + } + + // Ensure that we can set the target + gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.DONT_CARE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "hint should accept FRAGMENT_SHADER_DERIVATIVE_HINT_OES"); + + // Test all the hint modes + var validModes = ["FASTEST", "NICEST", "DONT_CARE"]; + var anyFailed = false; + for (var n = 0; n < validModes.length; n++) { + var mode = validModes[n]; + gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl[mode]); + if (gl.getParameter(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES) != gl[mode]) { + testFailed("Round-trip of hint()/getParameter() failed on mode " + mode); + anyFailed = true; + } + } + if (!anyFailed) { + testPassed("Round-trip of hint()/getParameter() with all supported modes"); + } +} + +function runShaderTests(extensionEnabled) { + debug(""); + debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled")); + + // Expect the macro shader to succeed ONLY if enabled + { + const macroFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "macroFragmentShader"); + if (extensionEnabled) { + if (macroFragmentProgram) { + // Expected result + testPassed("GL_OES_standard_derivatives defined in shaders when extension is enabled"); + } else { + testFailed("GL_OES_standard_derivatives not defined in shaders when extension is enabled"); + } + } else { + if (macroFragmentProgram) { + testFailed("GL_OES_standard_derivatives defined in shaders when extension is disabled"); + } else { + testPassed("GL_OES_standard_derivatives not defined in shaders when extension disabled"); + } + } + } + + // Always expect the shader missing the #pragma to fail (whether enabled or not) + { + const missingPragmaFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "missingPragmaFragmentShader"); + if (missingPragmaFragmentProgram) { + testFailed("Shader built-ins allowed without #extension pragma"); + } else { + testPassed("Shader built-ins disallowed without #extension pragma"); + } + } + + // Try to compile a shader using the built-ins that should only succeed if enabled + { + const testFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShader"); + if (extensionEnabled) { + if (testFragmentProgram) { + testPassed("Shader built-ins compiled successfully when extension enabled"); + } else { + testFailed("Shader built-ins failed to compile when extension enabled"); + } + } else { + if (testFragmentProgram) { + testFailed("Shader built-ins compiled successfully when extension disabled"); + } else { + testPassed("Shader built-ins failed to compile when extension disabled"); + } + } + } + + // This tests specifically that #extension directives after other code are + // valid, per spec (6.35 GLSL ES #extension directive location). + // + // This test actually has nothing to do with OES_standard_derivatives, but + // is inserted here because this extension is ubiquitous. + // + // This test is not as strict as the spec - it doesn't require that "the + // scope ... is always the whole shader", because implementations (ANGLE + // shader translator) do not actually implement this correctly. The test + // coverage is intentionally left incomplete - in practice, all WebGL + // shaders already work with the current implementation, so there's no + // practical reason to update them to match the spec. Conversely, the + // currently implemented rules are too complex to formalize in spec. + // + // Regression test for https://crbug.com/971660 . + { + const testFragmentProgramWithExtensionNotAtTop = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShaderWithExtensionNotAtTop"); + if (extensionEnabled) { + if (testFragmentProgramWithExtensionNotAtTop) { + testPassed("Shader with #extension after non-preprocessor code: compiled successfully when extension enabled"); + } else { + testFailed("Shader with #extension after non-preprocessor code: failed to compile when extension enabled"); + } + } + } +} + +function runOutputTests() { + // This tests does several draws with various values of z. + // The output of the fragment shader is: + // [dFdx(z), dFdy(z), fwidth(z), 1.0] + // The expected math: (note the conversion to uint8) + // canvas.width = canvas.height = 50 + // dFdx = totalChange.x / canvas.width = 0.5 / 50.0 = 0.01 + // dFdy = totalChange.y / canvas.height = 0.5 / 50.0 = 0.01 + // fw = abs(dFdx + dFdy) = 0.01 + 0.01 = 0.02 + // r = floor(dFdx * 40.0 * 255) = 102 + // g = floor(dFdy * 40.0 * 255) = 102 + // b = floor(fw * 40.0 * 255) = 204 + + var e = 5; // Amount of variance to allow in result pixels - may need to be tweaked higher + + debug("Testing various draws for valid built-in function behavior"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.NICEST); + + var positionLoc = 0; + var texcoordLoc = 1; + var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['vPosition', 'texCoord0'], [0, 1]); + var quadParameters = wtu.setupUnitQuad(gl, positionLoc, texcoordLoc); + + function expectResult(target, message) { + var locations = [ + [ 0.1, 0.1 ], + [ 0.9, 0.1 ], + [ 0.1, 0.9 ], + [ 0.9, 0.9 ], + [ 0.5, 0.5 ] + ]; + for (var n = 0; n < locations.length; n++) { + var loc = locations[n]; + var px = Math.floor(loc[0] * canvas.width); + var py = Math.floor(loc[1] * canvas.height); + wtu.checkCanvasRect(gl, px, py, 1, 1, target, message, 4); + } + }; + + function setupBuffers(tl, tr, bl, br) { + gl.bindBuffer(gl.ARRAY_BUFFER, quadParameters[0]); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 1.0, 1.0, tr, + -1.0, 1.0, tl, + -1.0, -1.0, bl, + 1.0, 1.0, tr, + -1.0, -1.0, bl, + 1.0, -1.0, br]), gl.STATIC_DRAW); + gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0); + }; + + // Draw 1: (no variation) + setupBuffers(0.0, 0.0, 0.0, 0.0); + wtu.clearAndDrawUnitQuad(gl); + expectResult([0, 0, 0, 255], + "Draw 1 (no variation) should pass"); + + // Draw 2: (variation in x) + setupBuffers(1.0, 0.0, 1.0, 0.0); + wtu.clearAndDrawUnitQuad(gl); + expectResult([204, 0, 204, 255], + "Draw 2 (variation in x) should pass"); + + // Draw 3: (variation in y) + setupBuffers(1.0, 1.0, 0.0, 0.0); + wtu.clearAndDrawUnitQuad(gl); + expectResult([0, 204, 204, 255], + "Draw 3 (variation in y) should pass"); + + // Draw 4: (variation in x & y) + setupBuffers(1.0, 0.5, 0.5, 0.0); + wtu.clearAndDrawUnitQuad(gl); + expectResult([102, 102, 204, 255], + "Draw 4 (variation in x & y) should pass"); +} + +function runUniqueObjectTest() +{ + debug("Testing that getExtension() returns the same object each time"); + ext = null; + gl.getExtension("OES_standard_derivatives").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("OES_standard_derivatives").myProperty', '2'); +} + +function runDeferredLinkTests() { + debug(""); + debug("Testing deferred shader compilation tests."); + + // Test for compilation failures that are caused by missing extensions + // do not succeed if extensions are enabled during linking. This would + // only happen for deferred shader compilations. + + // First test if link succeeds with extension enabled. + var glEnabled = wtu.create3DContext(); + var extEnabled = glEnabled.getExtension("OES_standard_derivatives"); + if (!extEnabled) { + testFailed("Deferred link test expects the extension to be supported"); + } + + var vertexShader = wtu.loadShaderFromScript(glEnabled, "goodVertexShader"); + var fragmentShader = wtu.loadShaderFromScript(glEnabled, "macroFragmentShader"); + + if (!vertexShader || !fragmentShader) { + testFailed("Could not create good shaders."); + return; + } + + var program = wtu.setupProgram(glEnabled, [vertexShader, fragmentShader]); + + if (!program) { + testFailed("Compilation with extension enabled failed."); + return; + } + + // Create new context to test link failure without extension enabled. + var glDeferred = wtu.create3DContext(); + + var vertexShader = wtu.loadShaderFromScript(glDeferred, "goodVertexShader", glDeferred.VERTEX_SHADER, undefined, undefined, true); + var fragmentShader = wtu.loadShaderFromScript(glDeferred, "macroFragmentShader", glDeferred.FRAGMENT_SHADER, undefined, undefined, true); + + if (vertexShader == null || fragmentShader == null) { + testFailed("Could not create shaders."); + return; + } + + // Shader compilations should have failed due to extensions not enabled. + glDeferred.getExtension("OES_standard_derivatives"); + var program = wtu.setupProgram(glDeferred, [vertexShader, fragmentShader]); + if (program) { + testFailed("Compilation with extension disabled then linking with extension enabled should have failed."); + return; + } + + testPassed("Compilation with extension disabled then linking with extension enabled."); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-linear.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-linear.html new file mode 100644 index 0000000000..2c8f194e82 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-linear.html @@ -0,0 +1,42 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/oes-texture-float-and-half-float-linear.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +description("Test OES_texture_float_linear, if available."); + +const wtu = WebGLTestUtils; +const gl = wtu.create3DContext(); + +(function() { + if (!wtu.isWebGL2(gl)) { + if (!gl.getExtension("OES_texture_float")) { + testPassed("No OES_texture_float support -- this is legal"); + return; + } + } + testTexLinear(gl, "OES_texture_float_linear", "RGBA32F", "FLOAT"); +})(); + +debug(""); +const successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-canvas.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-canvas.html new file mode 100644 index 0000000000..b681083899 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-canvas.html @@ -0,0 +1,34 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-canvas.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + if (!gl.getExtension("OES_texture_float")) { + testPassed("No OES_texture_float support -- this is legal"); + return false; + } + + testPassed("Successfully enabled OES_texture_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "FLOAT", testPrologue, "../../resources/")()'> +<canvas id="example" width="32" height="32"></canvas> +<div id="description"></div> +<div id="console"></div> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image-data.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image-data.html new file mode 100644 index 0000000000..74ad8d27db --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image-data.html @@ -0,0 +1,35 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-image-data.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + if (!gl.getExtension("OES_texture_float")) { + testPassed("No OES_texture_float support -- this is legal"); + return false; + } + + testPassed("Successfully enabled OES_texture_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "FLOAT", testPrologue, "../../resources/")()'> +<canvas id="texcanvas" width="2" height="2"></canvas> +<canvas id="example" width="2" height="2"></canvas> +<div id="description"></div> +<div id="console"></div> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image.html new file mode 100644 index 0000000000..cff19e5d51 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image.html @@ -0,0 +1,34 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-image.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + if (!gl.getExtension("OES_texture_float")) { + testPassed("No OES_texture_float support -- this is legal"); + return false; + } + + testPassed("Successfully enabled OES_texture_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "FLOAT", testPrologue, "../../resources/")()'> +<canvas id="example" width="32" height="32"></canvas> +<div id="description"></div> +<div id="console"></div> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html new file mode 100644 index 0000000000..3ccbd50f15 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html @@ -0,0 +1,39 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-video.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + if (!gl.getExtension("OES_texture_float")) { + testPassed("No OES_texture_float support -- this is legal"); + return false; + } + + testPassed("Successfully enabled OES_texture_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "FLOAT", testPrologue, "../../resources/")()'> +<canvas id="example" width="32" height="32"></canvas> +<div id="description"></div> +<div id="console"></div> +<video width="640" height="228" id="vid" controls muted> + <source src="../../resources/red-green.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' /> + <source src="../../resources/red-green.webmvp8.webm" type='video/webm; codecs="vp8, vorbis"' /> + <source src="../../resources/red-green.theora.ogv" type='video/ogg; codecs="theora, vorbis"' /> +</video> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html new file mode 100644 index 0000000000..a757cb22ec --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html @@ -0,0 +1,426 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>OES_texture_float/WEBGL_color_buffer_float</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/ext-float-blend.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing floating-point textures --> +<script id="testFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +uniform sampler2D tex; +uniform vec4 subtractor; +varying vec2 texCoord; +void main() +{ + vec4 color = texture2D(tex, texCoord); + if (abs(color.r - subtractor.r) + + abs(color.g - subtractor.g) + + abs(color.b - subtractor.b) + + abs(color.a - subtractor.a) < 8.0) { + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } +} +</script> +<!-- Shaders for testing floating-point render targets --> +<script id="positionVertexShader" type="x-shader/x-vertex"> +attribute vec4 vPosition; +void main() +{ + gl_Position = vPosition; +} +</script> +<script id="floatingPointFragmentShader" type="x-shader/x-fragment"> +void main() +{ + gl_FragColor = vec4(10000.0, 10000.0, 10000.0, 10000.0); +} +</script> +<script> +"use strict"; +description("This test verifies the functionality of the OES_texture_float and WEBGL_color_buffer_float extensions, if available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + var texturedShaders = [ + wtu.simpleTextureVertexShader, + "testFragmentShader" + ]; + var testProgram = + wtu.setupProgram(gl, + texturedShaders, + ['vPosition', 'texCoord0'], + [0, 1]); + var quadParameters = wtu.setupUnitQuad(gl, 0, 1); + + // First verify that allocation of floating-point textures fails if + // the extension has not been enabled yet. + runTextureCreationTest(testProgram, false); + + if (!gl.getExtension("OES_texture_float")) { + testPassed("No OES_texture_float support -- this is legal"); + } else { + testPassed("Successfully enabled OES_texture_float extension"); + // If alpha value is missing from a texture it gets filled to 1 when sampling according to GLES2.0 table 3.12 + runTextureCreationTest(testProgram, true, gl.RGBA, 4, [10000, 10000, 10000, 10000]); + runTextureCreationTest(testProgram, true, gl.RGB, 3, [10000, 10000, 10000, 1]); + runTextureCreationTest(testProgram, true, gl.LUMINANCE, 1, [10000, 10000, 10000, 1]); + runTextureCreationTest(testProgram, true, gl.ALPHA, 1, [0, 0, 0, 10000]); + runTextureCreationTest(testProgram, true, gl.LUMINANCE_ALPHA, 2, [10000, 10000, 10000, 10000]); + + (function() { + debug(""); + var renderable = isRenderable(gl); + var renderableExtName = "WEBGL_color_buffer_float"; + var supported = gl.getSupportedExtensions().includes(renderableExtName); + if (renderable && !supported) { + testFailed("RGBA/FLOAT is color renderable but " + renderableExtName + " not exposed"); + } else if (supported && !renderable) { + testFailed(renderableExtName + " is exposed but RGBA/FLOAT is not color renderable"); + } + if (supported) { + runRenderTargetAndReadbackTest(testProgram, gl.RGBA, 4, [10000, 10000, 10000, 10000], 0, true); + runRenderTargetAndReadbackTest(testProgram, gl.RGB, 3, [10000, 10000, 10000, 1], 0, false); + runRenderTargetAndReadbackTest(testProgram, gl.RGBA, 4, [10000, 10000, 10000, 10000], 1, true); + runRenderTargetAndReadbackTest(testProgram, gl.RGBA, 4, [10000, 10000, 10000, 10000], 0.5, true); + runFramebufferTest(); + + debug(""); + debug("Test float32 blending without EXT_float_blend."); + testExtFloatBlend(gl.RGBA); + + debug(""); + debug("Testing that float32 blending succeeds with EXT_float_blend."); + if (!gl.getExtension("EXT_float_blend")) { + testPassed("No EXT_float_blend support -- this is legal"); + return; + } + testExtFloatBlend(gl.RGBA); + } + })(); + + runUniqueObjectTest(); + } +} + +function isRenderable(gl) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.FLOAT, null); + + var fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); + + return status == gl.FRAMEBUFFER_COMPLETE; +} + +function allocateTexture() +{ + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture parameter setup should succeed"); + return texture; +} + +function checkRenderingResults() +{ + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); +} + +function runTextureCreationTest(testProgram, extensionEnabled, opt_format, opt_numChannels, opt_subtractor) +{ + var format = opt_format || gl.RGBA; + var numberOfChannels = opt_numChannels || 4; + var expectFailure = !extensionEnabled; + var subtractor = opt_subtractor || [10000, 10000, 10000, 10000]; + + debug(""); + debug("testing format: " + wtu.glEnumToString(gl, format) + + " expect:" + (extensionEnabled ? "success" : "failure")); + + var texture = allocateTexture(); + // Generate data. + var width = 2; + var height = 2; + var data = new Float32Array(width * height * numberOfChannels); + for (var ii = 0; ii < data.length; ++ii) { + data[ii] = 10000; + } + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, gl.FLOAT, data); + if (expectFailure) { + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "floating-point texture allocation must be disallowed if OES_texture_float isn't enabled"); + return; + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point texture allocation should succeed if OES_texture_float is enabled"); + } + // Verify that the texture actually works for sampling and contains the expected data. + gl.uniform4fv(gl.getUniformLocation(testProgram, "subtractor"), subtractor); + wtu.clearAndDrawUnitQuad(gl); + checkRenderingResults(); + + // Check that linear fails. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red"); +} + +function arrayToString(arr, size) { + var mySize; + if (!size) + mySize = arr.length; + else + mySize = size; + var out = "["; + for (var ii = 0; ii < mySize; ++ii) { + if (ii > 0) { + out += ", "; + } + out += arr[ii]; + } + return out + "]"; +} + +function runRenderTargetAndReadbackTest(testProgram, format, numberOfChannels, subtractor, texSubImageCover, requireRenderable) +{ + var formatString = wtu.glEnumToString(gl, format); + debug(""); + debug("testing floating-point " + formatString + " render target" + (texSubImageCover > 0 ? " after calling texSubImage" : "")); + + var texture = allocateTexture(); + var width = 2; + var height = 2; + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, gl.FLOAT, null); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point texture allocation should succeed if OES_texture_float is enabled"); + + // Try to use this texture as a render target. + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.bindTexture(gl.TEXTURE_2D, null); + // It is legal for a WebGL implementation exposing the OES_texture_float extension to + // support floating-point textures but not as attachments to framebuffer objects. + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + if (requireRenderable) + testFailed("floating-point " + formatString + " render target not supported"); + else + debug("floating-point " + formatString + " render target not supported -- this is legal"); + return; + } + + if (texSubImageCover > 0) { + // Ensure that replacing the whole texture or a part of it with texSubImage2D doesn't affect renderability + gl.bindTexture(gl.TEXTURE_2D, texture); + var data = new Float32Array(width * height * numberOfChannels * texSubImageCover); + gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height * texSubImageCover, format, gl.FLOAT, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texSubImage2D should succeed if OES_texture_float is enabled"); + gl.bindTexture(gl.TEXTURE_2D, null); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testFailed("render target support changed after calling texSubImage2D"); + return; + } + } + + var renderProgram = + wtu.setupProgram(gl, + ["positionVertexShader", "floatingPointFragmentShader"], + ['vPosition'], + [0]); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering to floating-point texture should succeed"); + + // Now sample from the floating-point texture and verify we got the correct values. + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.useProgram(testProgram); + gl.uniform1i(gl.getUniformLocation(testProgram, "tex"), 0); + gl.uniform4fv(gl.getUniformLocation(testProgram, "subtractor"), subtractor); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering from floating-point texture should succeed"); + checkRenderingResults(); + + // Finally, if the implementation supports floating-point readback, verify it. + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + var implFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT); + var implType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "getParameter of IMPLEMENTATION_COLOR_READ_{FORMAT|TYPE} should succeed"); + if ((implFormat == gl.RGBA || implFormat == gl.RGB) && implType == gl.FLOAT) { + debug("Checking readback of floating-point values"); + var arraySize = (implFormat == gl.RGBA) ? 4 : 3 + var buf = new Float32Array(arraySize); + gl.readPixels(0, 0, 1, 1, implFormat, implType , buf); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels from floating-point renderbuffer should succeed"); + var ok = true; + var tolerance = 8.0; // TODO: factor this out from both this test and the subtractor shader above. + for (var ii = 0; ii < buf.length; ++ii) { + if (Math.abs(buf[ii] - subtractor[ii]) > tolerance) { + ok = false; + break; + } + } + if (ok) { + testPassed("readPixels of float-type data from floating-point renderbuffer succeeded"); + } else { + testFailed("readPixels of float-type data from floating-point renderbuffer failed: expected " + + arrayToString(subtractor, arraySize) + ", got " + arrayToString(buf)); + } + } +} + +function runUniqueObjectTest() +{ + debug(""); + debug("Testing that getExtension() returns the same object each time"); + gl.getExtension("OES_texture_float").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("OES_texture_float").myProperty', '2'); +} + +// Make sure we can call readPixels with the passed in arrayBufferConstructor and that the color +// channels are the ones we expect. If there is a mismatch between the glType and arrayBuffer type, +// fail the test. +function verifyReadPixelsColors(red, green, blue, alpha, alphaRGB, glFormat, glType, arrayBufferConstructor) { + var typeName = wtu.glEnumToString(gl, glType); + + debug(wtu.glEnumToString(gl, glFormat) + " framebuffer with " + typeName + " readback."); + + var arrayBuffer = new arrayBufferConstructor(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels should return NO_ERROR when reading " + typeName + " data."); + + assertMsg(arrayBuffer[0] === red, "Red channel should be " + red + " for " + typeName + " readPixels. Received: " + arrayBuffer[0]); + assertMsg(arrayBuffer[1] === green, "Green channel should be " + green + " for " + typeName + " readPixels. Received: " + arrayBuffer[1]); + assertMsg(arrayBuffer[2] === blue, "Blue channel should be " + blue + " for " + typeName + " readPixels. Received: " + arrayBuffer[2]); + if (glFormat === gl.RGBA) { + assertMsg(arrayBuffer[3] === alpha, "Alpha channel should be " + alpha + " for " + typeName + " readPixels. Received: " + arrayBuffer[3]); + } else if (glFormat === gl.RGB) { + assertMsg(arrayBuffer[3] === alphaRGB, "Alpha channel should be " + alphaRGB + " for " + typeName + " readPixels. Received: " + arrayBuffer[3]); + } + + // Make sure any arrayBuffer types that are not equal to arrayBufferConstructor fail readPixels. + if (arrayBufferConstructor !== Uint8Array) { + arrayBuffer = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "readPixels should return INVALID_OPERATION when reading mismatched types. " + Uint8Array.toString()); + } + if (arrayBufferConstructor !== Float32Array) { + arrayBuffer = new Float32Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "readPixels should return INVALID_OPERATION when reading mismatched types. " + Float32Array.toString()); + } + if (arrayBufferConstructor !== Uint16Array) { + arrayBuffer = new Uint16Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "readPixels should return INVALID_OPERATION when reading mismatched types. " + Uint16Array.toString()); + } +} + +// Verify that float textures attached to frame buffers function correctly with regard to framebuffer +// completness, IMPLEMENTATION_COLOR_READ_FORMAT/TYPE and readPixels +function runFramebufferTest() { + debug(""); + debug("Framebuffer Tests"); + + var texture = allocateTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + + debug("Ensure non-color-renderable formats [LUMINANCE, LUMINANCE_ALPHA, ALPHA] fail."); + var arrayBufferFloatOutput = new Float32Array(4); // 4 color channels + [gl.LUMINANCE, gl.LUMINANCE_ALPHA, gl.ALPHA].forEach(function(badFormat) { + debug(wtu.glEnumToString(gl, badFormat) + " framebuffer"); + + gl.texImage2D(gl.TEXTURE_2D, 0, badFormat, 1, 1, 0, badFormat, gl.FLOAT, null); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + + shouldBeNull("gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT)"); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "IMPLEMENTATION_COLOR_READ_FORMAT should fail for incomplete framebuffers."); + + shouldBeNull("gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE)"); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "IMPLEMENTATION_COLOR_READ_TYPE should fail for incomplete framebuffers."); + + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, arrayBufferFloatOutput); + wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION , "readPixels should fail on incomplete framebuffers."); + debug(""); + }); + + debug("Ensure color renderable formats [RGBA, RGB] succeed."); + var arrayBufferFloatInput = new Float32Array(4); // 4 color channels + arrayBufferFloatInput[0] = 0; + arrayBufferFloatInput[1] = .25; + arrayBufferFloatInput[2] = .50; + arrayBufferFloatInput[3] = .75; + + [gl.RGBA, gl.RGB].forEach(function(goodFormat) { + debug(""); + debug(wtu.glEnumToString(gl, goodFormat) + " framebuffer tests"); + + gl.texImage2D(gl.TEXTURE_2D, 0, goodFormat, 1, 1, 0, goodFormat, gl.FLOAT, arrayBufferFloatInput); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + testPassed("Format is not renderable. This is allowed."); + return; + } + verifyReadPixelsColors( + 0.00, // red + 0.25, // green + 0.50, // blue + 0.75, // alpha + 1.0, // alphaRGB + goodFormat, + gl.FLOAT, + Float32Array); + + var implementationColorReadFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT); + assertMsg(implementationColorReadFormat === gl.RGBA || implementationColorReadFormat === gl.RGB, + "IMPLEMENTATION_COLOR_READ_FORMAT should be color renderable: RGBA or RGB. Received: " + wtu.glEnumToString(gl, implementationColorReadFormat)); + + var implementationColorReadType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE); + assertMsg(implementationColorReadType === gl.UNSIGNED_BYTE || + implementationColorReadType === gl.FLOAT || + "IMPLEMENTATION_COLOR_READ_TYPE must be one of UNSIGNED_BYTE or FLOAT " + + "Received: " + wtu.glEnumToString(gl, implementationColorReadType)); + }); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-linear.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-linear.html new file mode 100644 index 0000000000..459346857e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-linear.html @@ -0,0 +1,46 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/oes-texture-float-and-half-float-linear.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; + +description("Test OES_texture_half_float_linear, if available."); + +const wtu = WebGLTestUtils; +const gl = wtu.create3DContext(); + +(function() { + if (wtu.isWebGL2(gl)) + throw new Error("OES_texture_half_float_linear is core in WebGL 2."); + + const ext = gl.getExtension("OES_texture_half_float"); + if (!ext) { + testPassed("No OES_texture_half_float support -- this is legal"); + return; + } + // Required by the test harness. + gl.HALF_FLOAT_OES = ext.HALF_FLOAT_OES; + testTexLinear(gl, "OES_texture_half_float_linear", undefined, "HALF_FLOAT_OES"); +})(); + +debug(""); +const successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-canvas.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-canvas.html new file mode 100644 index 0000000000..b991c58b19 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-canvas.html @@ -0,0 +1,39 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-canvas.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + var ext = null; + + if (!(ext = gl.getExtension("OES_texture_half_float"))) { + testPassed("No OES_texture_half_float support -- this is legal"); + return false; + } + + // Required by the test harness. + gl.HALF_FLOAT_OES = ext.HALF_FLOAT_OES; + + testPassed("Successfully enabled OES_texture_half_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "HALF_FLOAT_OES", testPrologue, "../../resources/")()'> +<canvas id="example" width="32" height="32"></canvas> +<div id="description"></div> +<div id="console"></div> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image-data.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image-data.html new file mode 100644 index 0000000000..91f3f97b08 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image-data.html @@ -0,0 +1,40 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-image-data.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + var ext = null; + + if (!(ext = gl.getExtension("OES_texture_half_float"))) { + testPassed("No OES_texture_half_float support -- this is legal"); + return false; + } + + // Required by the test harness. + gl.HALF_FLOAT_OES = ext.HALF_FLOAT_OES; + + testPassed("Successfully enabled OES_texture_half_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "HALF_FLOAT_OES", testPrologue, "../../resources/")()'> +<canvas id="texcanvas" width="2" height="2"></canvas> +<canvas id="example" width="2" height="2"></canvas> +<div id="description"></div> +<div id="console"></div> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image.html new file mode 100644 index 0000000000..3f486eac0e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image.html @@ -0,0 +1,39 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-image.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + var ext = null; + + if (!(ext = gl.getExtension("OES_texture_half_float"))) { + testPassed("No OES_texture_half_float support -- this is legal"); + return false; + } + + // Required by the test harness. + gl.HALF_FLOAT_OES = ext.HALF_FLOAT_OES; + + testPassed("Successfully enabled OES_texture_half_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "HALF_FLOAT_OES", testPrologue, "../../resources/")()'> +<canvas id="example" width="32" height="32"></canvas> +<div id="description"></div> +<div id="console"></div> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html new file mode 100644 index 0000000000..719b332113 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html @@ -0,0 +1,44 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-utils.js"></script> +<script src="../../js/tests/tex-image-and-sub-image-2d-with-video.js"></script> +<script> +"use strict"; +function testPrologue(gl) { + var ext = null; + + if (!(ext = gl.getExtension("OES_texture_half_float"))) { + testPassed("No OES_texture_half_float support -- this is legal"); + return false; + } + + // Required by the test harness. + gl.HALF_FLOAT_OES = ext.HALF_FLOAT_OES; + + testPassed("Successfully enabled OES_texture_half_float extension"); + return true; +} +</script> +</head> +<body onload='generateTest("RGBA", "RGBA", "HALF_FLOAT_OES", testPrologue, "../../resources/")()'> +<canvas id="example" width="32" height="32"></canvas> +<div id="description"></div> +<div id="console"></div> +<video width="640" height="228" id="vid" controls muted> + <source src="../../resources/red-green.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' /> + <source src="../../resources/red-green.webmvp8.webm" type='video/webm; codecs="vp8, vorbis"' /> + <source src="../../resources/red-green.theora.ogv" type='video/ogg; codecs="theora, vorbis"' /> +</video> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float.html new file mode 100644 index 0000000000..4d5d81608a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float.html @@ -0,0 +1,474 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL OES_texture_half_float Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<canvas id="canvas2d" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script id="testFragmentShader" type="x-shader/x-fragment"> +precision mediump float; +uniform sampler2D tex; +uniform vec4 subtractor; +varying vec2 texCoord; +void main() +{ + vec4 color = texture2D(tex, texCoord); + if (abs(color.r - subtractor.r) + + abs(color.g - subtractor.g) + + abs(color.b - subtractor.b) + + abs(color.a - subtractor.a) < 8.0) { + gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); + } else { + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } +} +</script> +<!-- Shaders for testing half-floating-point render targets --> +<script id="floatingPointFragmentShader" type="x-shader/x-fragment"> +void main() +{ + gl_FragColor = vec4(10000.0, 10000.0, 10000.0, 10000.0); +} +</script> +<script> +"use strict" +description("This test verifies the functionality of OES_texture_half_float with null/non-null ArrayBufferView"); + +debug(""); +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var colorCanvas = document.getElementById("canvas2d"); +colorCanvas.width = 2; +colorCanvas.height = 2; +var ctx = colorCanvas.getContext("2d"); +ctx.fillStyle = "rgb(255,0,0)"; +ctx.fillRect(0, 0, 2, 2); +var gl = wtu.create3DContext(canvas); +// This constant must be defined in order to run the texture creation test without the extension enabled. +var halfFloatOESEnum = 0x8D61; +var ext = null; + + +if (!gl) { + testFailed("WebGL context does not exists"); +} else { + testPassed("WebGL context exists"); + + // Verify that allocation of texture fails if extension is not enabled + runTextureCreationTest(false); + ext = gl.getExtension("OES_texture_half_float") + if (!ext) { + testPassed("No OES_texture_half_float support. This is legal"); + } else { + testPassed("Successfully enabled OES_texture_half_float extension"); + + var program = wtu.setupTexturedQuad(gl); + + // Check if creation of texture succeed's with various formats and null ArrayBufferView + var formats = [ + { format: gl.RGBA, expected: [255, 0, 0, 255], }, + { format: gl.RGB, expected: [255, 0, 0, 255], }, + { format: gl.LUMINANCE, expected: [255, 255, 255, 255], }, + { format: gl.ALPHA, expected: [ 0, 0, 0, 255], }, + { format: gl.LUMINANCE_ALPHA, expected: [255, 255, 255, 255], }, + ]; + formats.forEach(function(f) { + runTextureCreationTest(true, f.format, null, f.expected); + }); + + // Texture creation should fail when passed with non-null, non-Uint16 ArrayBufferView + formats.forEach(function(f) { + var width = 2; + var height = 2; + var format = f.format; + + // Float32Array + var float32Data = new Float32Array(width * height * getNumberOfChannels(format)); + for (var ii = 0; ii < float32Data.length; ii++) { + float32Data[ii] = 1000; + } + runTextureCreationTest(true, format, float32Data, null); + + // Int16Array + var int16Data = new Int16Array(width * height * getNumberOfChannels(format)); + for (var ii = 0; ii < int16Data.length; ii++) { + int16Data[ii] = 1000; + } + runTextureCreationTest(true, format, int16Data, null); + }); + + // Test that Uint16 encoded half float values can be used as texture data. + + // First test that values in the 0-1 range can be written and read. + var halfFloatOneThird = 0x3555; // Half float 1/3 + var uint16Formats = [ + { format: gl.RGBA, expected: [85, 85, 85, 85], }, + { format: gl.RGB, expected: [85, 85, 85, 255], }, + { format: gl.LUMINANCE, expected: [85, 85, 85, 255], }, + { format: gl.ALPHA, expected: [ 0, 0, 0, 85], }, + { format: gl.LUMINANCE_ALPHA, expected: [85, 85, 85, 85], }, + ]; + + uint16Formats.forEach(function(f) { + var width = 2; + var height = 2; + var format = f.format; + + var uint16Data = new Uint16Array(width * height * getNumberOfChannels(format)); + for (var ii = 0; ii < uint16Data.length; ii++) { + uint16Data[ii] = halfFloatOneThird; + } + runTextureCreationTest(true, format, uint16Data, f.expected); + }); + + // Next check that values outside the 0-1 range can be written. + var halfFloatTenK = 0x70E2; // Half float 10000 + var uint16Formats2 = [ + { format: gl.RGBA, subtractor: [10000, 10000, 10000, 10000], requireRenderable: true}, + { format: gl.RGB, subtractor: [10000, 10000, 10000, 1], requireRenderable: false}, + ]; + + uint16Formats2.forEach(function(f) { + var width = 2; + var height = 2; + var format = f.format; + + var uint16Data = new Uint16Array(width * height * getNumberOfChannels(format)); + for (var ii = 0; ii < uint16Data.length; ii++) { + uint16Data[ii] = halfFloatTenK; + } + runRenderTest(format, f.subtractor, uint16Data, f.requireRenderable); + }); + + (function() { + debug(""); + var renderable = isRenderable(gl, ext); + var renderableExtName = "EXT_color_buffer_half_float"; + var supported = gl.getSupportedExtensions().includes(renderableExtName); + if (renderable && !supported) { + testFailed("RGBA/HALF_FLOAT_OES is color renderable but " + renderableExtName + " not exposed"); + } else if (supported && !renderable) { + testFailed(renderableExtName + " is exposed but RGBA/HALF_FLOAT_OES is not color renderable"); + } + if (supported) { + runRenderTest(gl.RGBA, [10000, 10000, 10000, 10000], null, true); + runRenderTest(gl.RGB, [10000, 10000, 10000, 1], null, false); + runFramebufferTest(); + } + })(); + + // Check of getExtension() returns same object + runUniqueObjectTest(); + } +} + +function isRenderable(gl, ext) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, ext.HALF_FLOAT_OES, null); + + var fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0); + + var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); + + return status == gl.FRAMEBUFFER_COMPLETE; +} + +function getNumberOfChannels(format) +{ + if (format == gl.RGBA) + return 4; + else if (format == gl.RGB) + return 3; + else if (format == gl.LUMINANCE || format == gl.ALPHA) + return 1; + else if (format == gl.LUMINANCE_ALPHA) + return 2; +} + +function allocateTexture() +{ + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture parameter setup should succeed"); + return texture; +} + +function runTextureCreationTest(extensionEnabled, opt_format, opt_data, opt_expected) +{ + var format = opt_format || gl.RGBA; + var data = opt_data || null; + var expectSuccess = true; + + if (!extensionEnabled || !opt_expected) + expectSuccess = false; + debug("Testing texture creation with extension " + (extensionEnabled ? "enabled" : "disabled") + + ", format " + wtu.glEnumToString(gl, format) + ", and data " + (data ? "non-null" : "null") + + ". Expect " + (expectSuccess ? "Success" : "Failure")); + + var texture = allocateTexture(); + var width = 2; + var height = 2; + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, halfFloatOESEnum, data); + if(!extensionEnabled) { + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Half floating point texture must be disallowed if OES_texture_half_float isn't enabled"); + return; + } else if (!opt_expected) { + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Half floating point texture allocation must be disallowed when ArrayBufferView is not-null and not-Uint16"); + return; + } else { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Half floating point texture allocation should succeed if OES_texture_half_float is enabled"); + + if (!data) { + gl.texImage2D(gl.TEXTURE_2D, 0, format, format, halfFloatOESEnum, colorCanvas); + } + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, opt_expected); + // Check that linear fails. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 0, 0, 255], "should be black"); + } + +} + +function checkRenderingResults() +{ + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); +} + +function runRenderTest(format, subtractor, data, requireRenderable) +{ + var formatString = wtu.glEnumToString(gl, format); + + debug(""); + + if (!data) { + debug("Testing half floating point " + formatString + " render target"); + } else { + debug("Testing half floating point " + formatString + " from a Uint16Array"); + } + + var texture = allocateTexture(); + var width = 2; + var height = 2; + + gl.texImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, format, ext.HALF_FLOAT_OES, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Half floating point texture allocation should succeed if OES_texture_half_float is enabled"); + + if (!data) { + // Try to use this texture as render target + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + gl.bindTexture(gl.TEXTURE_2D, null); + + // It is legal for a WebGL implementation exposing the OES_texture_half_float extension to + // support half floating point textures but not as attachments to framebuffer objects. + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + if (requireRenderable) { + testFailed(formatString + " render targets not supported."); + } else { + debug(formatString + " render targets not supported -- this is legal"); + } + return; + } + + var renderProgram = + wtu.setupProgram(gl, + [wtu.simpleVertexShader, "floatingPointFragmentShader"], + ['vPosition'], + [0]); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Rendering to half floating point texture should succeed"); + } + + // Now sample from the half floating-point texture and verify we got the correct values. + var texturedShaders = [ + wtu.simpleTextureVertexShader, + "testFragmentShader" + ]; + var testProgram = + wtu.setupProgram(gl, + [wtu.simpleTextureVertexShader, "testFragmentShader"], + ['vPosition', 'texCoord0'], + [0, 1]); + var quadParameters = wtu.setupUnitQuad(gl, 0, 1); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindTexture(gl.TEXTURE_2D, texture); + gl.useProgram(testProgram); + gl.uniform4fv(gl.getUniformLocation(testProgram, "subtractor"), subtractor); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering from half floating point texture should succeed"); + checkRenderingResults(); +} + +function runUniqueObjectTest() +{ + debug(""); + debug("Testing that getExtension() returns the same object each time"); + ext = null; + gl.getExtension("OES_texture_half_float").myProperty = 2; + webglHarnessCollectGarbage(); + shouldBe('gl.getExtension("OES_texture_half_float").myProperty', '2'); +} + +// Make sure we can call readPixels with the passed in arrayBufferConstructor and that the color +// channels are the ones we expect. If there is a mismatch between the glType and arrayBuffer type, +// fail the test. +function verifyReadPixelsColors(red, green, blue, alpha, alphaRGB, glFormat, glType, arrayBufferConstructor) { + var typeName = wtu.glEnumToString(gl, glType); + + debug(wtu.glEnumToString(gl, glFormat) + " framebuffer with " + typeName + " readback."); + + var arrayBuffer = new arrayBufferConstructor(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels should return NO_ERROR when reading " + typeName + " data."); + + assertMsg(arrayBuffer[0] === red, "Red channel should be " + red + " for " + typeName + " readPixels. Received: " + arrayBuffer[0]); + assertMsg(arrayBuffer[1] === green, "Green channel should be " + green + " for " + typeName + " readPixels. Received: " + arrayBuffer[1]); + assertMsg(arrayBuffer[2] === blue, "Blue channel should be " + blue + " for " + typeName + " readPixels. Received: " + arrayBuffer[2]); + if (glFormat === gl.RGBA) { + assertMsg(arrayBuffer[3] === alpha, "Alpha channel should be " + alpha + " for " + typeName + " readPixels. Received: " + arrayBuffer[3]); + } else if (glFormat === gl.RGB) { + assertMsg(arrayBuffer[3] === alphaRGB, "Alpha channel should be " + alphaRGB + " for " + typeName + " readPixels. Received: " + arrayBuffer[3]); + } + + // Make sure any arrayBuffer types that are not equal to arrayBufferConstructor fail readPixels. + if (arrayBufferConstructor !== Uint8Array) { + arrayBuffer = new Uint8Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "readPixels should return INVALID_OPERATION when reading mismatched types. " + Uint8Array.toString()); + } + if (arrayBufferConstructor !== Float32Array) { + arrayBuffer = new Float32Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "readPixels should return INVALID_OPERATION when reading mismatched types. " + Float32Array.toString()); + } + if (arrayBufferConstructor !== Uint16Array) { + arrayBuffer = new Uint16Array(4); + gl.readPixels(0, 0, 1, 1, gl.RGBA, glType, arrayBuffer); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "readPixels should return INVALID_OPERATION when reading mismatched types. " + Uint16Array.toString()); + } +} + +// Verify that half float textures attached to frame buffers function correctly with regard to framebuffer +// completness, IMPLEMENTATION_COLOR_READ_FORMAT/TYPE and readPixels +function runFramebufferTest() { + debug(""); + debug("Framebuffer Tests"); + + var texture = allocateTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + + debug("Ensure non-color-renderable formats [LUMINANCE, LUMINANCE_ALPHA, ALPHA] fail."); + var arrayBufferFloatOutput = new Float32Array(4); // 4 color channels + [gl.LUMINANCE, gl.LUMINANCE_ALPHA, gl.ALPHA].forEach(function(badFormat) { + debug(wtu.glEnumToString(gl, badFormat) + " framebuffer"); + + gl.texImage2D(gl.TEXTURE_2D, 0, badFormat, 1, 1, 0, badFormat, ext.HALF_FLOAT_OES, null); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT"); + + shouldBeNull("gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT)"); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "IMPLEMENTATION_COLOR_READ_FORMAT should fail for incomplete framebuffers."); + + shouldBeNull("gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE)"); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "IMPLEMENTATION_COLOR_READ_TYPE should fail for incomplete framebuffers."); + + gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, arrayBufferFloatOutput); + wtu.glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION , "readPixels should fail on incomplete framebuffers."); + debug(""); + }); + + debug("Ensure color renderable formats [RGBA, RGB] succeed."); + var arrayBufferHalfFloatInput = new Uint16Array(4); // 4 color channels + arrayBufferHalfFloatInput[0] = 0; // 0 in half float + arrayBufferHalfFloatInput[1] = 0x3400; // 0.25 in half float + arrayBufferHalfFloatInput[2] = 0x3800; // 0.50 in half float + arrayBufferHalfFloatInput[3] = 0x3A00; // 0.75 in half float + + [gl.RGBA, gl.RGB].forEach(function(goodFormat) { + debug(wtu.glEnumToString(gl, goodFormat) + " framebuffer tests"); + debug(""); + + gl.texImage2D(gl.TEXTURE_2D, 0, goodFormat, 1, 1, 0, goodFormat, ext.HALF_FLOAT_OES, arrayBufferHalfFloatInput); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) { + // Per the OES_color_buffer_half_float, RGBA/FLOAT should always succeed for readPixels + verifyReadPixelsColors( + 0.00, // red + 0.25, // green + 0.50, // blue + 0.75, // alpha + 1.0, // alphaRGB + goodFormat, + gl.FLOAT, + Float32Array); + + var implementationColorReadFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT); + assertMsg(implementationColorReadFormat === gl.RGBA || implementationColorReadFormat === gl.RGB, + "IMPLEMENTATION_COLOR_READ_FORMAT should be color renderable: RGBA or RGB. Received: " + wtu.glEnumToString(gl, implementationColorReadFormat)); + + var implementationColorReadType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE); + + // There is nothing in the specifications that keeps the + // implementation color read format and type from being the + // same as the implicitly supported one. For this reason, keep + // gl.FLOAT as one of the valid options. + assertMsg(implementationColorReadType === gl.UNSIGNED_BYTE || + implementationColorReadType === gl.FLOAT || + implementationColorReadType === ext.HALF_FLOAT_OES || + implementationColorReadType === gl.UNSIGNED_SHORT_4_4_4_4 || + implementationColorReadType === gl.UNSIGNED_SHORT_5_5_5_1 || + implementationColorReadType === gl.UNSIGNED_SHORT_5_6_5, + "IMPLEMENTATION_COLOR_READ_TYPE must be one of UNSIGNED_BYTE, UNSIGNED_SHORT_4_4_4_4, UNSIGNED_SHORT_5_5_5_1, UNSIGNED_SHORT_5_6_5, FLOAT, or HALF_FLOAT_OES. " + + "Received: " + wtu.glEnumToString(gl, implementationColorReadType)); + + // Test the RGBA/HALF_FLOAT_OES combination + if (implementationColorReadFormat === gl.RGBA && implementationColorReadType === ext.HALF_FLOAT_OES) { + verifyReadPixelsColors( + 0, // red + 0x3400, // green + 0x3800, // blue + 0x3A00, // alpha + 0x3C00, // alphaRGB + goodFormat, + ext.HALF_FLOAT_OES, + Uint16Array); + } + } + debug(""); + }); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object-bufferData.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object-bufferData.html new file mode 100644 index 0000000000..d89e9bcc1b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object-bufferData.html @@ -0,0 +1,194 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL OES_vertex_array_object_bufferData Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<!-- comment in the script tag below to test through JS emualation of the extension. --> +<!-- +<script src="../../../demos/google/resources/OESVertexArrayObject.js"></script> +--> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script id="vshader" type="x-shader/x-vertex"> +attribute vec4 a_position; +attribute vec4 a_color; +varying vec4 v_color; +void main(void) { + gl_Position = a_position; + v_color = a_color; +} +</script> +<script id="fshader" type="x-shader/x-fragment"> +precision mediump float; +varying vec4 v_color; +void main(void) { + gl_FragColor = v_color; +} +</script> +<script> +"use strict"; +description("This test verifies drawing results when using gl.bufferData with the OES_vertex_array_object extension."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var vao = null; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Setup emulated OESVertexArrayObject if it has been included. + if (window.setupVertexArrayObject) { + debug("using emuated OES_vertex_array_object"); + setupVertexArrayObject(gl); + } + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("OES_vertex_array_object"); + if (!ext) { + testPassed("No OES_vertex_array_object support -- this is legal"); + + } else { + testPassed("Successfully enabled OES_vertex_array_object extension"); + + runBufferDataTest(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + } +} + +/** + * The OES_vertex_array_object extension seems to work incorrectly on some handheld devices, + * namely the Nexus 5, and Nexus 7 (in 2014/01) when using bufferData before binding the VAO. + * The tested OS was Android KitKat 4.4.2, the effects were the same in all tested browsers + * (Chrome, Chrome Beta, Firefox, Firefox Beta), so it is likely a driver bug. + * These devices have the similar Adreno 320 and Adreno 330 GPUs respectively. + * + * The issuse resulted from this sequence of actions in a requestAnimationFrame loop: + * 1. upload some vertex buffers with gl.bufferData (eg. colors) + * 2. bind the VAO + * 3. clear the canvas + * 4. draw (some triangles) to the canvas + * 5. unbind the VAO + * + * This caused the drawn triangles to be drawn with black (0) for most of the frames, with some + * rare frames presenting the correct render results. Interestingly on both devices exactly every + * 64th frame passed (starting with the very first one), the others failed. + * (Because of this, we test multiple frames.) + * When positions were uploaded, seemingly nothing was drawn, that's likely because the + * position buffer was also all 0s. + * + * The issue did not occur: + * - if step 1. and 2. were swapped + * - or if step5 was ommited (probably because that makes step 2 a no-op since the VAO is bound) + */ +function runBufferDataTest() { + debug("Testing draws with bufferData"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + + var testColor = [0, 255, 0, 255]; + var clearColor = [255, 0, 0, 255]; + + // Where the issue occures, this is the sequence of success/failure every time: + // result: success fail fail fail fail ... success fail fail ... + // currentTestCount: 0 1 2 3 4 ... 64 65 66 ... + // So with just 1 test it passes, but 2 tests are enough. Here we use 3. + var numberOfTests = 3; + var currentTestCount = 0; + + var positionLoc = 0; + var colorLoc = 1; + var gridRes = 1; + + var program = wtu.setupSimpleVertexColorProgram(gl, positionLoc, colorLoc); + + var vao0 = ext.createVertexArrayOES(); + ext.bindVertexArrayOES(vao0); + + var buffers = wtu.setupIndexedQuadWithOptions(gl, + { gridRes: gridRes, + positionLocation: positionLoc + }); + + var colorTypedArray = createColorTypedArray(); + + var colorBuffer = gl.createBuffer(gl.ARRAY_BUFFER); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.enableVertexAttribArray(colorLoc); + gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, 0, 0); + + ext.bindVertexArrayOES(null); + + testDrawing(); + + function testDrawing() { + // this order works fine: + // ext.bindVertexArrayOES(vao0); + // uploadColor(); + + // this order doesn't: + uploadColor(); + ext.bindVertexArrayOES(vao0); + + wtu.clearAndDrawIndexedQuad(gl, 1, clearColor); + + ext.bindVertexArrayOES(null); + + //debug("<span>"+currentTestCount+"</span"); + wtu.checkCanvas(gl, testColor, "should be green") + + if (++currentTestCount < numberOfTests) { + testDrawing(); + // wtu.requestAnimFrame(testDrawing); + } else { + // clean up + ext.deleteVertexArrayOES(vao0); + } + } + + function uploadColor() { + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, colorTypedArray, gl.STREAM_DRAW); + } + + function createColorTypedArray() { + var colors = []; + var pOffset = 0; + for (var yy = 0; yy <= gridRes; ++yy) { + for (var xx = 0; xx <= gridRes; ++xx) { + colors[pOffset + 0] = testColor[0]; + colors[pOffset + 1] = testColor[1]; + colors[pOffset + 2] = testColor[2]; + colors[pOffset + 3] = testColor[3]; + pOffset += 4; + } + } + return new Float32Array(colors); + } +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html new file mode 100644 index 0000000000..4d544aeade --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html @@ -0,0 +1,780 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL OES_vertex_array_object Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<!-- comment in the script tag below to test through JS emulation of the extension. --> +<!-- +<script src="../../../demos/google/resources/OESVertexArrayObject.js"></script> +--> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +<script id="vshader" type="x-shader/x-vertex"> +attribute vec4 a_position; +attribute vec4 a_color; +varying vec4 v_color; +void main(void) { + gl_Position = a_position; + v_color = a_color; +} +</script> +<script id="fshader" type="x-shader/x-fragment"> +precision mediump float; +varying vec4 v_color; +void main(void) { + gl_FragColor = v_color; +} +</script> +<script> +"use strict"; +description("This test verifies the functionality of the OES_vertex_array_object extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var vao = null; + +var contextA; +var contextB; +var extA; +var extB; +var vertexArrayA; +var vertexArrayB; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Setup emulated OESVertexArrayObject if it has been included. + if (window.setupVertexArrayObject) { + debug("using emulated OES_vertex_array_object"); + setupVertexArrayObject(gl); + } + + // Run tests with extension disabled + runBindingTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("OES_vertex_array_object"); + if (!ext) { + testPassed("No OES_vertex_array_object support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled OES_vertex_array_object extension"); + + runSupportedTest(true); + runBindingTestEnabled(); + runObjectTest(); + runAttributeTests(); + runAttributeValueTests(); + runDrawTests(); + runUnboundDeleteTests(); + runBoundDeleteTests(); + runArrayBufferBindTests(); + runInvalidContextTests(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + } +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("OES_vertex_array_object") >= 0) { + if (extensionEnabled) { + testPassed("OES_vertex_array_object listed as supported and getExtension succeeded"); + } else { + testFailed("OES_vertex_array_object listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("OES_vertex_array_object not listed as supported but getExtension succeeded"); + } else { + testPassed("OES_vertex_array_object not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runBindingTestDisabled() { + debug(""); + debug("Testing binding enum with extension disabled"); + + // Use the constant directly as we don't have the extension + var VERTEX_ARRAY_BINDING_OES = 0x85B5; + + gl.getParameter(VERTEX_ARRAY_BINDING_OES); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "VERTEX_ARRAY_BINDING_OES should not be queryable if extension is disabled"); +} + +function runBindingTestEnabled() { + debug(""); + debug("Testing binding enum with extension enabled"); + + shouldBe("ext.VERTEX_ARRAY_BINDING_OES", "0x85B5"); + + gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "VERTEX_ARRAY_BINDING_OES query should succeed if extension is enabled"); + + // Default value is null + if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) === null) { + testPassed("Default value of VERTEX_ARRAY_BINDING_OES is null"); + } else { + testFailed("Default value of VERTEX_ARRAY_BINDING_OES is not null"); + } + + debug(""); + debug("Testing binding a VAO"); + var vao0 = ext.createVertexArrayOES(); + var vao1 = ext.createVertexArrayOES(); + shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)"); + ext.bindVertexArrayOES(vao0); + if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) == vao0) { + testPassed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is expected VAO"); + } else { + testFailed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is not expected VAO") + } + ext.bindVertexArrayOES(vao1); + if (gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) == vao1) { + testPassed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is expected VAO"); + } else { + testFailed("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES) is not expected VAO") + } + ext.deleteVertexArrayOES(vao1); + shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)"); + ext.bindVertexArrayOES(vao1); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "binding a deleted vertex array object"); + ext.bindVertexArrayOES(null); + shouldBeNull("gl.getParameter(ext.VERTEX_ARRAY_BINDING_OES)"); + ext.deleteVertexArrayOES(vao1); +} + +function runObjectTest() { + debug(""); + debug("Testing object creation"); + + vao = ext.createVertexArrayOES(); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "createVertexArrayOES should not set an error"); + shouldBeNonNull("vao"); + + // Expect false if never bound + shouldBeFalse("ext.isVertexArrayOES(vao)"); + ext.bindVertexArrayOES(vao); + shouldBeTrue("ext.isVertexArrayOES(vao)"); + ext.bindVertexArrayOES(null); + shouldBeTrue("ext.isVertexArrayOES(vao)"); + + shouldBeFalse("ext.isVertexArrayOES(null)"); + + ext.deleteVertexArrayOES(vao); + vao = null; +} + +function runAttributeTests() { + debug(""); + debug("Testing attributes work across bindings"); + + var states = []; + + var attrCount = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); + for (var n = 0; n < attrCount; n++) { + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); + + var state = {}; + states.push(state); + + var vao = state.vao = ext.createVertexArrayOES(); + ext.bindVertexArrayOES(vao); + + var enableArray = (n % 2 == 0); + if (enableArray) { + gl.enableVertexAttribArray(n); + } else { + gl.disableVertexAttribArray(n); + } + + if (enableArray) { + var buffer = state.buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, 1024, gl.STATIC_DRAW); + + gl.vertexAttribPointer(n, 1 + n % 4, gl.FLOAT, true, n * 4, n * 4); + } + + if (enableArray) { + var elbuffer = state.elbuffer = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elbuffer); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, 1024, gl.STATIC_DRAW); + } + + ext.bindVertexArrayOES(null); + } + + var anyMismatch = false; + for (var n = 0; n < attrCount; n++) { + var state = states[n]; + + ext.bindVertexArrayOES(state.vao); + + var shouldBeEnabled = (n % 2 == 0); + var isEnabled = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_ENABLED); + if (shouldBeEnabled != isEnabled) { + testFailed("VERTEX_ATTRIB_ARRAY_ENABLED not preserved"); + anyMismatch = true; + } + + var buffer = gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING); + if (shouldBeEnabled) { + if (buffer == state.buffer) { + // Matched + if ((gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_SIZE) == 1 + n % 4) && + (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_TYPE) == gl.FLOAT) && + (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_NORMALIZED) == true) && + (gl.getVertexAttrib(n, gl.VERTEX_ATTRIB_ARRAY_STRIDE) == n * 4) && + (gl.getVertexAttribOffset(n, gl.VERTEX_ATTRIB_ARRAY_POINTER) == n * 4)) { + // Matched + } else { + testFailed("VERTEX_ATTRIB_ARRAY_* not preserved"); + anyMismatch = true; + } + } else { + testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved"); + anyMismatch = true; + } + } else { + // GL_CURRENT_VERTEX_ATTRIB is not preserved + if (buffer) { + testFailed("VERTEX_ATTRIB_ARRAY_BUFFER_BINDING not preserved"); + anyMismatch = true; + } + } + + var elbuffer = gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING); + if (shouldBeEnabled) { + if (elbuffer == state.elbuffer) { + // Matched + } else { + testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved"); + anyMismatch = true; + } + } else { + if (elbuffer == null) { + // Matched + } else { + testFailed("ELEMENT_ARRAY_BUFFER_BINDING not preserved"); + anyMismatch = true; + } + } + } + ext.bindVertexArrayOES(null); + if (!anyMismatch) { + testPassed("All attributes preserved across bindings"); + } + + for (var n = 0; n < attrCount; n++) { + var state = states[n]; + ext.deleteVertexArrayOES(state.vao); + } +} + +function runAttributeValueTests() { + debug(""); + debug("Testing that attribute values are not attached to bindings"); + + var v; + var vao0 = ext.createVertexArrayOES(); + var anyFailed = false; + + ext.bindVertexArrayOES(null); + gl.vertexAttrib4f(0, 0, 1, 2, 3); + + v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB); + if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) { + testFailed("Vertex attrib value not round-tripped?"); + anyFailed = true; + } + + ext.bindVertexArrayOES(vao0); + + v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB); + if (!(v[0] == 0 && v[1] == 1 && v[2] == 2 && v[3] == 3)) { + testFailed("Vertex attrib value reset across bindings"); + anyFailed = true; + } + + gl.vertexAttrib4f(0, 4, 5, 6, 7); + ext.bindVertexArrayOES(null); + + v = gl.getVertexAttrib(0, gl.CURRENT_VERTEX_ATTRIB); + if (!(v[0] == 4 && v[1] == 5 && v[2] == 6 && v[3] == 7)) { + testFailed("Vertex attrib value bound to buffer"); + anyFailed = true; + } + + if (!anyFailed) { + testPassed("Vertex attribute values are not attached to bindings") + } + + ext.bindVertexArrayOES(null); + ext.deleteVertexArrayOES(vao0); +} + +function runDrawTests() { + debug(""); + debug("Testing draws with various VAO bindings"); + + canvas.width = 50; canvas.height = 50; + gl.viewport(0, 0, canvas.width, canvas.height); + + var vao0 = ext.createVertexArrayOES(); + var vao1 = ext.createVertexArrayOES(); + var vao2 = ext.createVertexArrayOES(); + + var positionLocation = 0; + var colorLocation = 1; + + var program = wtu.setupSimpleVertexColorProgram(gl, positionLocation, colorLocation); + + function setupQuad(s, colorsInArray) { + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 1.0 * s, 1.0 * s, 0.0, + -1.0 * s, 1.0 * s, 0.0, + -1.0 * s, -1.0 * s, 0.0, + 1.0 * s, 1.0 * s, 0.0, + -1.0 * s, -1.0 * s, 0.0, + 1.0 * s, -1.0 * s, 0.0]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(positionLocation); + gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0); + + // Test switching between VAOs that have different number of enabled arrays + if (colorsInArray) { + var vertexObject = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vertexObject); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ + 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.0, 1.0, + 0.0, 0.0, 0.0, 1.0]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(colorLocation); + gl.vertexAttribPointer(colorLocation, 4, gl.FLOAT, false, 0, 0); + } else { + gl.disableVertexAttribArray(colorLocation); + } + }; + + function verifyDiagonalPixels(s, expectedInside, drawDescription) { + // Tests pixels along a diagonal running from the center of the canvas to the (0, 0) corner. + // Values on the points list indicate relative position along this diagonal. + var points = [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]; + for (var n = 0; n < points.length; n++) { + var expected = points[n] <= s ? expectedInside : 255; + var x = Math.round((1 - points[n]) * canvas.width / 2); + var y = Math.round((1 - points[n]) * canvas.height / 2); + wtu.checkCanvasRect(gl, x, y, 1, 1, [expected, expected, expected, 255], + "Drawing " + drawDescription + " should pass", 2); + } + }; + function verifyDraw(drawDescription, s, colorsInArray) { + wtu.clearAndDrawUnitQuad(gl); + var expectedInside = colorsInArray ? 0 : 128; + verifyDiagonalPixels(s, expectedInside, drawDescription); + }; + + // Setup all bindings + setupQuad(1, true); + ext.bindVertexArrayOES(vao0); + setupQuad(0.5, true); + ext.bindVertexArrayOES(vao1); + setupQuad(0.25, true); + ext.bindVertexArrayOES(vao2); + setupQuad(0.75, false); + + gl.vertexAttrib4f(colorLocation, 0.5, 0.5, 0.5, 1); + + // Verify drawing + ext.bindVertexArrayOES(null); + verifyDraw("with the default VAO", 1, true); + ext.bindVertexArrayOES(vao0); + verifyDraw("with VAO #0", 0.5, true); + ext.bindVertexArrayOES(vao1); + verifyDraw("with VAO #1", 0.25, true); + ext.bindVertexArrayOES(vao2); + verifyDraw("with VAO that has the color array disabled", 0.75, false); + + // Verify bound VAO after delete + ext.bindVertexArrayOES(vao1); + ext.deleteVertexArrayOES(vao0); + verifyDraw("after deleting another VAO", 0.25, true); + ext.deleteVertexArrayOES(vao1); + verifyDraw("after deleting the VAO that was bound", 1, true); + + // Disable global vertex attrib array + gl.disableVertexAttribArray(positionLocation); + gl.disableVertexAttribArray(colorLocation); + + // Check that constant values are treated correctly as not being part of VAO state. + var positionLoc = 0; + var colorLoc = 1; + var gridRes = 1; + wtu.setupIndexedQuad(gl, gridRes, positionLoc); + // Set the vertex color to red. + gl.vertexAttrib4f(colorLoc, 1, 0, 0, 1); + + var vao0 = ext.createVertexArrayOES(); + ext.bindVertexArrayOES(vao0); + var program = wtu.setupSimpleVertexColorProgram(gl, positionLoc, colorLoc); + wtu.setupIndexedQuad(gl, gridRes, positionLoc); + // Set the vertex color to green. + gl.vertexAttrib4f(colorLoc, 0, 1, 0, 1); + wtu.clearAndDrawIndexedQuad(gl, gridRes); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + ext.deleteVertexArrayOES(vao0); + wtu.clearAndDrawIndexedQuad(gl, gridRes); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); +} + +function runUnboundDeleteTests() { + debug(""); + debug("Testing using buffers that are deleted when attached to unbound VAOs"); + + var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["a_position", "a_color"]); + gl.useProgram(program); + + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array([ + 1.0, 1.0, + -1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0]), + gl.STATIC_DRAW); + + var colors = [ + [255, 0, 0, 255], + [ 0, 255, 0, 255], + [ 0, 0, 255, 255], + [ 0, 255, 255, 255] + ]; + var colorBuffers = []; + var elementBuffers = []; + var vaos = []; + for (var ii = 0; ii < colors.length; ++ii) { + var vao = ext.createVertexArrayOES(); + vaos.push(vao); + ext.bindVertexArrayOES(vao); + // Set the position buffer + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + + var elementBuffer = gl.createBuffer(); + elementBuffers.push(elementBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + new Uint8Array([0, 1, 2, 0, 2, 3]), + gl.STATIC_DRAW); + + // Setup the color attrib + var color = colors[ii]; + if (ii < 3) { + var colorBuffer = gl.createBuffer(); + colorBuffers.push(colorBuffer); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array( + [ color[0], color[1], color[2], color[3], + color[0], color[1], color[2], color[3], + color[0], color[1], color[2], color[3], + color[0], color[1], color[2], color[3] + ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0); + } else { + gl.vertexAttrib4f(1, color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255); + } + } + + // delete the color buffers AND the position buffer. + ext.bindVertexArrayOES(null); + for (var ii = 0; ii < colorBuffers.length; ++ii) { + gl.deleteBuffer(colorBuffers[ii]); + gl.deleteBuffer(elementBuffers[ii]); + ext.bindVertexArrayOES(vaos[ii]); + var boundBuffer = gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING); + // The buffers should still be valid at this point, since it was attached to the VAO + if(boundBuffer != colorBuffers[ii]) { + testFailed("buffer removed even though it is still attached to a VAO"); + } + } + + ext.bindVertexArrayOES(null); + gl.deleteBuffer(positionBuffer); + + // Render with the deleted buffers. As they are referenced by VAOs they + // must still be around. + for (var ii = 0; ii < colors.length; ++ii) { + var color = colors[ii]; + ext.bindVertexArrayOES(vaos[ii]); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); + wtu.checkCanvas(gl, color, "should be " + color); + } + + // Clean up. + for (var ii = 0; ii < colorBuffers.length; ++ii) { + ext.deleteVertexArrayOES(vaos[ii]); + } + + for (var ii = 0; ii < colorBuffers.length; ++ii) { + // The buffers should no longer be valid now that the VAOs are deleted + if(gl.isBuffer(colorBuffers[ii])) { + testFailed("buffer not properly cleaned up after VAO deletion"); + } + } +} + +function runBoundDeleteTests() { + debug("Testing using buffers that are deleted when attached to bound VAOs"); + + var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["a_position", "a_color"]); + gl.useProgram(program); + + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array([ + 1.0, 1.0, + -1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0]), + gl.STATIC_DRAW); + + // Setup the color attrib + var colorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array( + [ 255, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 255, 255, + 0, 255, 255, 255 + ]), gl.STATIC_DRAW); + + var vaos = []; + var elementBuffers = []; + for (var ii = 0; ii < 4; ++ii) { + var vao = ext.createVertexArrayOES(); + vaos.push(vao); + ext.bindVertexArrayOES(vao); + + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); + + var elementBuffer = gl.createBuffer(); + elementBuffers.push(elementBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + new Uint8Array([0, 1, 2, 0, 2, 3]), + gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer); + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 4, gl.UNSIGNED_BYTE, true, 0, 0); + } + + // delete the color buffers AND the position buffer, that are bound to the current VAO + for (var ii = 0; ii < vaos.length; ++ii) { + ext.bindVertexArrayOES(vaos[ii]); + + gl.deleteBuffer(colorBuffer); + gl.deleteBuffer(positionBuffer); + + // After the first iteration, deleteBuffer will be a no-op, and will not unbind its matching + // bind points on the now-bound VAO like it did on the first iteration. + var expectRetained = (ii != 0); + var shouldBeStr = (expectRetained ? "retained" : "cleared"); + + var boundPositionBuffer = gl.getVertexAttrib(0, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING); + if (expectRetained != (boundPositionBuffer == positionBuffer)) { + testFailed("Position attrib stored buffer should be " + shouldBeStr + "."); + } + + var boundColorBuffer = gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING); + if (expectRetained != (boundColorBuffer == colorBuffer)) { + testFailed("Color attrib stored buffer should be " + shouldBeStr + "."); + } + + // If retained, everything should still work. If cleared, drawing should now fail. + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); + var expectedError = (expectRetained ? gl.NO_ERROR : gl.INVALID_OPERATION); + wtu.glErrorShouldBe(gl, expectedError, + "Draw call should " + (expectRetained ? "not " : "") + "fail."); + + if (gl.isBuffer(positionBuffer)) { + testFailed("References from unbound VAOs don't keep Position buffer alive."); + } + if (gl.isBuffer(colorBuffer)) { + testFailed("References from unbound VAOs don't keep Color buffer alive"); + } + } +} + +function runArrayBufferBindTests() { + debug(""); + debug("Testing that buffer bindings on VAOs don't affect default VAO ARRAY_BUFFER binding."); + + ext.bindVertexArrayOES(null); + + var program = wtu.setupProgram(gl, ["vshader", "fshader"], ["a_color", "a_position"]); + gl.useProgram(program); + + // create shared element buffer + var elementBuffer = gl.createBuffer(); + // bind to default + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); + gl.bufferData( + gl.ELEMENT_ARRAY_BUFFER, + new Uint8Array([0, 1, 2, 0, 2, 3]), + gl.STATIC_DRAW); + + // first create the buffers for no VAO draw. + var nonVAOColorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, nonVAOColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array( + [ 0, 255, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255, + 0, 255, 0, 255, + ]), gl.STATIC_DRAW); + + // shared position buffer. + var positionBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array([ + 1.0, 1.0, + -1.0, 1.0, + -1.0, -1.0, + 1.0, -1.0]), + gl.STATIC_DRAW); + + // attach position buffer to default + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); + + // now create VAO + var vao = ext.createVertexArrayOES(); + ext.bindVertexArrayOES(vao); + + // attach the position buffer VAO + gl.enableVertexAttribArray(1); + gl.vertexAttribPointer(1, 2, gl.FLOAT, false, 0, 0); + + var vaoColorBuffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vaoColorBuffer); + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array( + [ 255, 0, 0, 255, + 255, 0, 0, 255, + 255, 0, 0, 255, + 255, 0, 0, 255, + ]), gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 4, gl.UNSIGNED_BYTE, true, 0, 0); + + // now set the buffer back to the nonVAOColorBuffer + gl.bindBuffer(gl.ARRAY_BUFFER, nonVAOColorBuffer); + + // bind to VAO + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); + wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red"); + + // unbind VAO + ext.bindVertexArrayOES(null); + + // At this point the nonVAOColorBuffer should be still be bound. + // If the WebGL impl is emulating VAOs it must make sure + // it correctly restores this binding. + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 4, gl.UNSIGNED_BYTE, true, 0, 0); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); +} + +function runInvalidContextTests() { + contextA = wtu.create3DContext(undefined, undefined, 1); + contextB = wtu.create3DContext(undefined, undefined, 1); + extA = contextA.getExtension("OES_vertex_array_object"); + extB = contextB.getExtension("OES_vertex_array_object"); + vertexArrayA = extA.createVertexArrayOES(); + vertexArrayB = extB.createVertexArrayOES(); + + wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "extA.bindVertexArrayOES(vertexArrayA)"); + wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "extA.bindVertexArrayOES(null)"); + wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "extB.bindVertexArrayOES(vertexArrayB)"); + wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "extB.bindVertexArrayOES(null)"); + + debug("") + debug("State queries validate context"); + shouldBeFalse("extA.isVertexArrayOES(vertexArrayB)"); + wtu.glErrorShouldBe(contextA, gl.NO_ERROR, "there should be no errors from invalid request"); + shouldBeFalse("extB.isVertexArrayOES(vertexArrayA)"); + wtu.glErrorShouldBe(contextB, gl.NO_ERROR, "there should be no errors from invalid request"); + shouldBeTrue("extA.isVertexArrayOES(vertexArrayA)"); + wtu.glErrorShouldBe(contextA, gl.NO_ERROR, "there should be no errors from valid request"); + shouldBeTrue("extB.isVertexArrayOES(vertexArrayB)"); + wtu.glErrorShouldBe(contextB, gl.NO_ERROR, "there should be no errors from valid request"); + + debug("") + debug("Deleting an object from another context generates an error"); + wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "extA.deleteVertexArrayOES(vertexArrayB)"); + wtu.shouldGenerateGLError(contextB, contextB.INVALID_OPERATION, "extB.deleteVertexArrayOES(vertexArrayA)"); + + debug("") + debug("Invalid delete operations do not delete"); + shouldBeTrue("extA.isVertexArrayOES(vertexArrayA)"); + shouldBeTrue("extB.isVertexArrayOES(vertexArrayB)"); + + debug("") + debug("Cannot bind VAOs from other contexts"); + wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "extA.bindVertexArrayOES(vertexArrayB)"); + wtu.shouldGenerateGLError(contextB, contextB.INVALID_OPERATION, "extB.bindVertexArrayOES(vertexArrayA)"); + + debug("") + debug("Context checks happen even for deleted objects"); + wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "extA.deleteVertexArrayOES(vertexArrayA)"); + wtu.shouldGenerateGLError(contextB, contextB.NO_ERROR, "extB.deleteVertexArrayOES(vertexArrayB)"); + wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "extA.deleteVertexArrayOES(vertexArrayB)"); + wtu.shouldGenerateGLError(contextB, contextB.INVALID_OPERATION, "extB.deleteVertexArrayOES(vertexArrayA)"); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/s3tc-and-rgtc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/s3tc-and-rgtc.html new file mode 100644 index 0000000000..3b725ffe22 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/s3tc-and-rgtc.html @@ -0,0 +1,1066 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/compressed-texture-utils.js"></script> +<title>WebGL WEBGL_compressed_texture_s3tc and EXT_texture_compression_rgtc Conformance Tests</title> +<style> +img { + border: 1px solid black; + margin-right: 1em; +} + +.testimages br { + clear: both; +} + +.testimages > div { + float: left; + margin: 1em; +} +</style> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_compressed_texture_s3tc extension, if it is available. It also tests the related formats from the EXT_texture_compression_rgtc extension."); + +debug(""); + +// Acceptable interpolation error depends on endpoints: +// 1.0 / 255.0 + 0.03 * max(abs(endpoint0 - endpoint1), abs(endpoint0_p - endpoint1_p)) +// For simplicity, assume the worst case (e0 is 0.0, e1 is 1.0). After conversion to unorm8, it is 9. +const DEFAULT_COLOR_ERROR = 9; + +/* +BC1 (DXT1) block +e0 = [ 0, 255, 0] +e1 = [255, 0, 0] +e0 < e1, so it uses 3-color mode + +local palette + 0: [ 0, 255, 0, 255] + 1: [255, 0, 0, 255] + 2: [128, 128, 0, 255] + 3: [ 0, 0, 0, 255] // for BC1 RGB + 3: [ 0, 0, 0, 0] // for BC1 RGBA +selectors + 3 2 1 0 + 2 2 1 0 + 1 1 1 0 + 0 0 0 0 + +Extending this block with opaque alpha and uploading as BC2 or BC3 +will generate wrong colors because BC2 and BC3 do not have 3-color mode. +*/ +var img_4x4_rgba_dxt1 = new Uint8Array([ + 0xE0, 0x07, 0x00, 0xF8, 0x1B, 0x1A, 0x15, 0x00 +]); + +/* +BC2 (DXT3) block + +Quantized alpha values + 0 1 2 3 + 4 5 6 7 + 8 9 A B + C D E F + +RGB block +e0 = [255, 0, 0] +e1 = [ 0, 255, 0] +BC2 has only 4-color mode + +local palette + 0: [255, 0, 0] + 1: [ 0, 255, 0] + 2: [170, 85, 0] + 3: [ 85, 170, 0] +selectors + 0 1 2 3 + 1 1 2 3 + 2 2 2 3 + 3 3 3 3 +*/ +var img_4x4_rgba_dxt3 = new Uint8Array([ + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + 0x00, 0xF8, 0xE0, 0x07, 0xE4, 0xE5, 0xEA, 0xFF +]); + +/* +BC3 (DXT5) block + +Alpha block (aka DXT5A) +e0 = 255 +e1 = 0 +e0 > e1, so using 6 intermediate points +local palette + 255, 0, 219, 182, 146, 109, 73, 36 +selectors + 0 1 2 3 + 1 2 3 4 + 2 3 4 5 + 3 4 5 6 + +RGB block +e0 = [255, 0, 0] +e1 = [ 0, 255, 0] +BC3 has only 4-color mode + +local palette + 0: [255, 0, 0] + 1: [ 0, 255, 0] + 2: [170, 85, 0] + 3: [ 85, 170, 0] +selectors + 3 2 1 0 + 3 2 1 1 + 3 2 2 2 + 3 3 3 3 +*/ +var img_4x4_rgba_dxt5 = new Uint8Array([ + 0xFF, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x00, 0xF8, 0xE0, 0x07, 0x1B, 0x5B, 0xAB, 0xFF +]); + +// BC4 - just the alpha block from BC3 above, interpreted as the red channel. +// See http://www.reedbeta.com/blog/understanding-bcn-texture-compression-formats/#bc4 +// for format details. +var img_4x4_r_bc4 = new Uint8Array([ + 0xFF, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, +]); + +// BC5 - Two BC3 alpha blocks, interpreted as the red and green channels. +var img_4x4_rg_bc5 = new Uint8Array([ + 0xFF, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x00, 0xFF, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, +]); + +// Signed BC4 - change endpoints to use full -1 to 1 range. +var img_4x4_signed_r_bc4 = new Uint8Array([ + 0x7F, 0x80, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, +]); + +// Signed BC5 - Two BC3 alpha blocks, interpreted as the red and green channels. +var img_4x4_signed_rg_bc5 = new Uint8Array([ + 0x7F, 0x80, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x80, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, +]); + + +/* +8x8 block endpoints use half-intensity values (appear darker than 4x4) +*/ +var img_8x8_rgba_dxt1 = new Uint8Array([ + 0xe0,0x03,0x00,0x78,0x13,0x10,0x15,0x00, + 0x0f,0x00,0xe0,0x7b,0x11,0x10,0x15,0x00, + 0xe0,0x03,0x0f,0x78,0x44,0x45,0x40,0x55, + 0x0f,0x00,0xef,0x03,0x44,0x45,0x40,0x55 +]); +var img_8x8_rgba_dxt3 = new Uint8Array([ + 0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0x78,0xe0,0x03,0x44,0x45,0x40,0x55, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x7b,0x0f,0x00,0x44,0x45,0x40,0x55, + 0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x0f,0x78,0xe0,0x03,0x11,0x10,0x15,0x00, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0x03,0x0f,0x00,0x11,0x10,0x15,0x00 +]); +var img_8x8_rgba_dxt5 = new Uint8Array([ + 0xff,0x69,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x78,0xe0,0x03,0x44,0x45,0x40,0x55, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x7b,0x0f,0x00,0x44,0x45,0x40,0x55, + 0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x0f,0x78,0xe0,0x03,0x11,0x10,0x15,0x00, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xef,0x03,0xef,0x00,0x11,0x10,0x15,0x00 +]); +var img_8x8_r_bc4 = new Uint8Array([ + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, +]); +var img_8x8_rg_bc5 = new Uint8Array([ + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x7F, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, 0x00, 0x7F, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, +]); + +var wtu = WebGLTestUtils; +var ctu = CompressedTextureUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, {antialias: false}); +var program = wtu.setupTexturedQuad(gl); +var ext = null; +var ext_rgtc = {}; +var vao = null; +var validFormats = { + COMPRESSED_RGB_S3TC_DXT1_EXT : 0x83F0, + COMPRESSED_RGBA_S3TC_DXT1_EXT : 0x83F1, + COMPRESSED_RGBA_S3TC_DXT3_EXT : 0x83F2, + COMPRESSED_RGBA_S3TC_DXT5_EXT : 0x83F3, +}; +var name; +var supportedFormats; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc"); + if (!ext) { + testPassed("No WEBGL_compressed_texture_s3tc support -- this is legal"); + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc", false); + } else { + testPassed("Successfully enabled WEBGL_compressed_texture_s3tc extension"); + + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc", true); + runTestExtension(); + } + ext_rgtc = wtu.getExtensionWithKnownPrefixes(gl, "EXT_texture_compression_rgtc"); + if (ext_rgtc) { + ext = ext || {}; + // Make ctu.formatToString work for rgtc enums. + for (const name in ext_rgtc) + ext[name] = ext_rgtc[name]; + runTestRGTC(); + } +} + +function expectedByteLength(width, height, format) { + if (format == validFormats.COMPRESSED_RGBA_S3TC_DXT3_EXT || format == validFormats.COMPRESSED_RGBA_S3TC_DXT5_EXT) { + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16; + } + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; +} + +function getBlockDimensions(format) { + return {width: 4, height: 4}; +} + +function runTestExtension() { + debug(""); + debug("Testing WEBGL_compressed_texture_s3tc"); + + // Test that enum values are listed correctly in supported formats and in the extension object. + ctu.testCompressedFormatsListed(gl, validFormats); + ctu.testCorrectEnumValuesInExt(ext, validFormats); + // Test that texture upload buffer size is validated correctly. + ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions); + + // Test each format + testDXT1_RGB(); + testDXT1_RGBA(); + testDXT3_RGBA(); + testDXT5_RGBA(); + + // Test compressed PBOs with a single format + if (contextVersion >= 2) { + testDXT5_RGBA_PBO(); + } + + // Test TexImage validation on level dimensions combinations. + debug(""); + debug("When level equals 0, width and height must be a multiple of 4."); + debug("When level is larger than 0, this constraint doesn't apply."); + ctu.testTexImageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { level: 0, width: 4, height: 3, expectation: gl.INVALID_OPERATION, message: "0: 4x3" }, + { level: 0, width: 3, height: 4, expectation: gl.INVALID_OPERATION, message: "0: 3x4" }, + { level: 0, width: 2, height: 2, expectation: gl.INVALID_OPERATION, message: "0: 2x2" }, + { level: 0, width: 4, height: 4, expectation: gl.NO_ERROR, message: "0: 4x4" }, + { level: 1, width: 2, height: 2, expectation: gl.NO_ERROR, message: "1: 2x2" }, + { level: 2, width: 1, height: 1, expectation: gl.NO_ERROR, message: "2: 1x1" }, + ]); + + ctu.testTexSubImageDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, 16, 16, + [ + { xoffset: 0, yoffset: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "height is not a multiple of 4" }, + { xoffset: 0, yoffset: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "width is not a multiple of 4" }, + { xoffset: 1, yoffset: 0, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "xoffset is not a multiple of 4" }, + { xoffset: 0, yoffset: 1, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "yoffset is not a multiple of 4" }, + { xoffset: 12, yoffset: 12, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + ]); + + if (contextVersion >= 2) { + debug(""); + debug("Testing NPOT textures"); + ctu.testTexImageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { level: 0, width: 0, height: 0, expectation: gl.NO_ERROR, message: "0: 0x0 is valid" }, + { level: 0, width: 1, height: 1, expectation: gl.INVALID_OPERATION, message: "0: 1x1 is invalid" }, + { level: 0, width: 2, height: 2, expectation: gl.INVALID_OPERATION, message: "0: 2x2 is invalid" }, + { level: 0, width: 3, height: 3, expectation: gl.INVALID_OPERATION, message: "0: 3x3 is invalid" }, + { level: 0, width: 10, height: 10, expectation: gl.INVALID_OPERATION, message: "0: 10x10 is invalid" }, + { level: 0, width: 11, height: 11, expectation: gl.INVALID_OPERATION, message: "0: 11x11 is invalid" }, + { level: 0, width: 11, height: 12, expectation: gl.INVALID_OPERATION, message: "0: 11x12 is invalid" }, + { level: 0, width: 12, height: 11, expectation: gl.INVALID_OPERATION, message: "0: 12x11 is invalid" }, + { level: 0, width: 12, height: 12, expectation: gl.NO_ERROR, message: "0: 12x12 is valid" }, + { level: 1, width: 0, height: 0, expectation: gl.NO_ERROR, message: "1: 0x0, is valid" }, + { level: 1, width: 3, height: 3, expectation: gl.INVALID_OPERATION, message: "1: 3x3, is invalid" }, + { level: 1, width: 5, height: 5, expectation: gl.INVALID_OPERATION, message: "1: 5x5, is invalid" }, + { level: 1, width: 5, height: 6, expectation: gl.INVALID_OPERATION, message: "1: 5x6, is invalid" }, + { level: 1, width: 6, height: 5, expectation: gl.INVALID_OPERATION, message: "1: 6x5, is invalid" }, + { level: 1, width: 6, height: 6, expectation: gl.NO_ERROR, message: "1: 6x6, is valid" }, + { level: 2, width: 0, height: 0, expectation: gl.NO_ERROR, message: "2: 0x0, is valid" }, + { level: 2, width: 3, height: 3, expectation: gl.NO_ERROR, message: "2: 3x3, is valid" }, + { level: 3, width: 1, height: 3, expectation: gl.NO_ERROR, message: "3: 1x3, is valid" }, + { level: 3, width: 1, height: 1, expectation: gl.NO_ERROR, message: "3: 1x1, is valid" }, + ]); + + debug(""); + debug("Testing partial updates"); + ctu.testTexSubImageDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, 12, 12, + [ + { xoffset: 0, yoffset: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "height is not a multiple of 4" }, + { xoffset: 0, yoffset: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "width is not a multiple of 4" }, + { xoffset: 1, yoffset: 0, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "xoffset is not a multiple of 4" }, + { xoffset: 0, yoffset: 1, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "yoffset is not a multiple of 4" }, + { xoffset: 8, yoffset: 8, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + ]); + + debug(""); + debug("Testing immutable NPOT textures"); + ctu.testTexStorageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { width: 12, height: 12, expectation: gl.NO_ERROR, message: "0: 12x12 is valid" }, + { width: 6, height: 6, expectation: gl.NO_ERROR, message: "1: 6x6, is valid" }, + { width: 3, height: 3, expectation: gl.NO_ERROR, message: "2: 3x3, is valid" }, + { width: 1, height: 1, expectation: gl.NO_ERROR, message: "3: 1x1, is valid" }, + ]); + } +} + +function runTestRGTC() { + var tests = [ + { width: 4, + height: 4, + channels: 1, + data: img_4x4_r_bc4, + format: ext_rgtc.COMPRESSED_RED_RGTC1_EXT, + hasAlpha: false, + }, + { width: 4, + height: 4, + channels: 1, + data: img_4x4_signed_r_bc4, + format: ext_rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT, + hasAlpha: false, + }, + { width: 4, + height: 4, + channels: 2, + data: img_4x4_rg_bc5, + format: ext_rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT, + hasAlpha: false, + }, + { width: 4, + height: 4, + channels: 2, + data: img_4x4_signed_rg_bc5, + format: ext_rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, + hasAlpha: false, + error: 18, // Signed, so twice the normal error. + // Experimentally needed by e.g. RTX 3070. + }, + { width: 8, + height: 8, + channels: 2, + data: img_8x8_r_bc4, + format: ext_rgtc.COMPRESSED_RED_RGTC1_EXT, + hasAlpha: false, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_r_bc4, + }, + { width: 8, + height: 8, + channels: 2, + data: img_8x8_rg_bc5, + format: ext_rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT, + hasAlpha: false, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rg_bc5, + }, + ]; + testDXTTextures(tests); +} + +function testDXT1_RGB() { + var tests = [ + { width: 4, + height: 4, + channels: 3, + data: img_4x4_rgba_dxt1, + format: ext.COMPRESSED_RGB_S3TC_DXT1_EXT, + hasAlpha: false, + }, + { width: 8, + height: 8, + channels: 3, + data: img_8x8_rgba_dxt1, + format: ext.COMPRESSED_RGB_S3TC_DXT1_EXT, + hasAlpha: false, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rgba_dxt1 + } + ]; + testDXTTextures(tests); +} + +function testDXT1_RGBA() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_dxt1, + format: ext.COMPRESSED_RGBA_S3TC_DXT1_EXT, + // This is a special case -- the texture is still opaque + // though it's RGBA. + hasAlpha: false, + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_dxt1, + format: ext.COMPRESSED_RGBA_S3TC_DXT1_EXT, + // This is a special case -- the texture is still opaque + // though it's RGBA. + hasAlpha: false, + } + ]; + testDXTTextures(tests); +} + +function testDXT3_RGBA() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_dxt3, + format: ext.COMPRESSED_RGBA_S3TC_DXT3_EXT, + hasAlpha: true, + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_dxt3, + format: ext.COMPRESSED_RGBA_S3TC_DXT3_EXT, + hasAlpha: true, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rgba_dxt3 + } + ]; + testDXTTextures(tests); +} + +function testDXT5_RGBA() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_dxt5, + format: ext.COMPRESSED_RGBA_S3TC_DXT5_EXT, + hasAlpha: true, + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_dxt5, + format: ext.COMPRESSED_RGBA_S3TC_DXT5_EXT, + hasAlpha: true, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rgba_dxt5 + } + ]; + testDXTTextures(tests); +} + +function testDXTTextures(tests) { + debug("<hr/>"); + for (var ii = 0; ii < tests.length; ++ii) { + testDXTTexture(tests[ii], false); + if (contextVersion >= 2) { + debug("<br/>"); + testDXTTexture(tests[ii], true); + } + } +} + +function uncompressDXTBlock( + destBuffer, destX, destY, destWidth, src, srcOffset, format) { + // Decoding routines follow D3D11 functional spec wrt + // endpoints unquantization and interpolation. + // Some hardware may produce slightly different values - it's normal. + + function make565(src, offset) { + return src[offset + 0] + (src[offset + 1] << 8); + } + function make8888From565(c) { + // These values exactly match hw decoder when selectors are 0 or 1. + function replicateBits(v, w) { + return (v << (8 - w)) | (v >> (w + w - 8)); + } + return [ + replicateBits((c >> 11) & 0x1F, 5), + replicateBits((c >> 5) & 0x3F, 6), + replicateBits((c >> 0) & 0x1F, 5), + 255 + ]; + } + function mix(mult, c0, c1, div) { + var r = []; + for (var ii = 0; ii < c0.length; ++ii) { + // For green channel (6 bits), this interpolation exactly matches hw decoders + + // For red and blue channels (5 bits), this interpolation exactly + // matches only some hw decoders and stays within acceptable range for others. + r[ii] = Math.floor((c0[ii] * mult + c1[ii]) / div + 0.5); + } + return r; + } + var isBC45 = ext_rgtc && + (format == ext_rgtc.COMPRESSED_RED_RGTC1_EXT || + format == ext_rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT || + format == ext_rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT || + format == ext_rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT); + let colorOffset = srcOffset; + if (!isBC45) { + var isDXT1 = format == ext.COMPRESSED_RGB_S3TC_DXT1_EXT || + format == ext.COMPRESSED_RGBA_S3TC_DXT1_EXT; + if (!isDXT1) { + colorOffset += 8; + } + var color0 = make565(src, colorOffset + 0); + var color1 = make565(src, colorOffset + 2); + var c0gtc1 = color0 > color1 || !isDXT1; + var rgba0 = make8888From565(color0); + var rgba1 = make8888From565(color1); + var colors = [ + rgba0, + rgba1, + c0gtc1 ? mix(2, rgba0, rgba1, 3) : mix(1, rgba0, rgba1, 2), + c0gtc1 ? mix(2, rgba1, rgba0, 3) : [0, 0, 0, 255] + ]; + } + const isSigned = ext_rgtc && (format == ext_rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT || format == ext_rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT); + const signedSrc = new Int8Array(src); + + // yea I know there is a lot of math in this inner loop. + // so sue me. + for (var yy = 0; yy < 4; ++yy) { + var pixels = src[colorOffset + 4 + yy]; + for (var xx = 0; xx < 4; ++xx) { + var dstOff = ((destY + yy) * destWidth + destX + xx) * 4; + if (!isBC45) { + var code = (pixels >> (xx * 2)) & 0x3; + var srcColor = colors[code]; + } + var alpha; + var rgChannel2 = 0; + let decodeAlpha = (offset) => { + let alpha; + var alpha0 = (isSigned ? signedSrc : src)[offset + 0]; + var alpha1 = (isSigned ? signedSrc : src)[offset + 1]; + var alphaOff = (yy >> 1) * 3 + 2; + var alphaBits = + src[offset + alphaOff + 0] + + src[offset + alphaOff + 1] * 256 + + src[offset + alphaOff + 2] * 65536; + var alphaShift = (yy % 2) * 12 + xx * 3; + var alphaCode = (alphaBits >> alphaShift) & 0x7; + if (alpha0 > alpha1) { + switch (alphaCode) { + case 0: + alpha = alpha0; + break; + case 1: + alpha = alpha1; + break; + default: + alpha = Math.floor(((8 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 7.0 + 0.5); + break; + } + } else { + switch (alphaCode) { + case 0: + alpha = alpha0; + break; + case 1: + alpha = alpha1; + break; + case 6: + alpha = 0; + break; + case 7: + alpha = 255; + break; + default: + alpha = Math.floor(((6 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 5.0 + 0.5); + break; + } + } + return alpha; + } + + switch (format) { + case ext.COMPRESSED_RGB_S3TC_DXT1_EXT: + alpha = 255; + break; + case ext.COMPRESSED_RGBA_S3TC_DXT1_EXT: + alpha = (code == 3 && !c0gtc1) ? 0 : 255; + break; + case ext.COMPRESSED_RGBA_S3TC_DXT3_EXT: + { + var alpha0 = src[srcOffset + yy * 2 + (xx >> 1)]; + var alpha1 = (alpha0 >> ((xx % 2) * 4)) & 0xF; + alpha = alpha1 | (alpha1 << 4); + } + break; + case ext_rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT: + case ext_rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: + rgChannel2 = decodeAlpha(srcOffset + 8); + // FALLTHROUGH + case ext.COMPRESSED_RGBA_S3TC_DXT5_EXT: + case ext_rgtc.COMPRESSED_RED_RGTC1_EXT: + case ext_rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT: + alpha = decodeAlpha(srcOffset); + break; + default: + throw "bad format"; + } + if (isBC45) { + destBuffer[dstOff + 0] = alpha; + destBuffer[dstOff + 1] = rgChannel2; + destBuffer[dstOff + 2] = 0; + destBuffer[dstOff + 3] = 255; + if (isSigned) { + destBuffer[dstOff + 0] = Math.max(0, alpha) * 2; + destBuffer[dstOff + 1] = Math.max(0, rgChannel2) * 2; + } + } else { + destBuffer[dstOff + 0] = srcColor[0]; + destBuffer[dstOff + 1] = srcColor[1]; + destBuffer[dstOff + 2] = srcColor[2]; + destBuffer[dstOff + 3] = alpha; + } + } + } +} + +function getBlockSize(format) { + var isDXT1 = format == ext.COMPRESSED_RGB_S3TC_DXT1_EXT || + format == ext.COMPRESSED_RGBA_S3TC_DXT1_EXT; + var isBC4 = ext_rgtc && (format == ext_rgtc.COMPRESSED_RED_RGTC1_EXT || format == ext_rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT); + return isDXT1 || isBC4 ? 8 : 16; +} + +function uncompressDXT(width, height, data, format) { + if (width % 4 || height % 4) throw "bad width or height"; + + var dest = new Uint8Array(width * height * 4); + var blocksAcross = width / 4; + var blocksDown = height / 4; + var blockSize = getBlockSize(format); + for (var yy = 0; yy < blocksDown; ++yy) { + for (var xx = 0; xx < blocksAcross; ++xx) { + uncompressDXTBlock( + dest, xx * 4, yy * 4, width, data, + (yy * blocksAcross + xx) * blockSize, format); + } + } + return dest; +} + +function uncompressDXTIntoSubRegion(width, height, subX0, subY0, subWidth, subHeight, data, format) +{ + if (width % 4 || height % 4 || subX0 % 4 || subY0 % 4 || subWidth % 4 || subHeight % 4) + throw "bad dimension"; + + var dest = new Uint8Array(width * height * 4); + // Zero-filled DXT1 or BC4/5 texture represents [0, 0, 0, 255] + if (format == ext.COMPRESSED_RGB_S3TC_DXT1_EXT || format == ext.COMPRESSED_RGBA_S3TC_DXT1_EXT || + format == ext.COMPRESSED_RED_RGTC1_EXT || format == ext.COMPRESSED_SIGNED_RED_RGTC1_EXT || + format == ext.COMPRESSED_RED_GREEN_RGTC2_EXT || format == ext.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT) { + for (var i = 3; i < dest.length; i += 4) dest[i] = 255; + } + var blocksAcross = subWidth / 4; + var blocksDown = subHeight / 4; + var blockSize = getBlockSize(format); + for (var yy = 0; yy < blocksDown; ++yy) { + for (var xx = 0; xx < blocksAcross; ++xx) { + uncompressDXTBlock( + dest, subX0 + xx * 4, subY0 + yy * 4, width, data, + (yy * blocksAcross + xx) * blockSize, format); + } + } + return dest; +} + +function copyRect(data, srcX, srcY, dstX, dstY, width, height, stride) { + var bytesPerLine = width * 4; + var srcOffset = srcX * 4 + srcY * stride; + var dstOffset = dstX * 4 + dstY * stride; + for (; height > 0; --height) { + for (var ii = 0; ii < bytesPerLine; ++ii) { + data[dstOffset + ii] = data[srcOffset + ii]; + } + srcOffset += stride; + dstOffset += stride; + } +} + +function testDXTTexture(test, useTexStorage) { + test.error = test.error || DEFAULT_COLOR_ERROR; + + var data = new Uint8Array(test.data); + var width = test.width; + var height = test.height; + var format = test.format; + + var uncompressedData = uncompressDXT(width, height, data, format); + + canvas.width = width; + canvas.height = height; + gl.viewport(0, 0, width, height); + debug("testing " + ctu.formatToString(ext, format) + " " + width + "x" + height + + (useTexStorage ? " via texStorage2D" : " via compressedTexImage2D")); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + if (useTexStorage) { + if (test.subData) { + var uncompressedDataSub = uncompressDXTIntoSubRegion( + width, height, test.subX0, test.subY0, test.subWidth, test.subHeight, test.subData, format); + var tex1 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed texture via texStorage2D"); + gl.compressedTexSubImage2D( + gl.TEXTURE_2D, 0, test.subX0, test.subY0, test.subWidth, test.subHeight, format, test.subData); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture data via compressedTexSubImage2D"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 1"); + compareRect(width, height, test.channels, uncompressedDataSub, "NEAREST", test.error); + + // Clean up and recover + gl.deleteTexture(tex1); + gl.bindTexture(gl.TEXTURE_2D, tex); + } + + gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed texture via texStorage2D"); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + var clearColor = (test.hasAlpha ? [0, 0, 0, 0] : [0, 0, 0, 255]); + wtu.checkCanvas(gl, clearColor, "texture should be initialized to black"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture data via compressedTexSubImage2D"); + } else { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); + } + gl.generateMipmap(gl.TEXTURE_2D); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after clearing generateMipmap error"); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 1"); + compareRect(width, height, test.channels, uncompressedData, "NEAREST", test.error); + // Test again with linear filtering. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 2"); + compareRect(width, height, test.channels, uncompressedData, "LINEAR", test.error); + + if (!useTexStorage) { + // It's not allowed to redefine textures defined via texStorage2D. + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border"); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + + if (width == 4) { + // The width/height of the implied base level must be a multiple of the block size. + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions for level > 0"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 2, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); + } + if (height == 4) { + // The width/height of the implied base level must be a multiple of the block size. + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions for level > 0"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 2, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); + } + } + + // pick a wrong format that uses the same amount of data. + var wrongFormat; + switch (format) { + case ext.COMPRESSED_RGB_S3TC_DXT1_EXT: + wrongFormat = ext.COMPRESSED_RGBA_S3TC_DXT1_EXT; + break; + case ext.COMPRESSED_RGBA_S3TC_DXT1_EXT: + wrongFormat = ext.COMPRESSED_RGB_S3TC_DXT1_EXT; + break; + case ext.COMPRESSED_RGBA_S3TC_DXT3_EXT: + wrongFormat = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT; + break; + case ext.COMPRESSED_RGBA_S3TC_DXT5_EXT: + wrongFormat = ext.COMPRESSED_RGBA_S3TC_DXT3_EXT; + break; + case ext_rgtc.COMPRESSED_RED_RGTC1_EXT: + case ext_rgtc.COMPRESSED_SIGNED_RED_RGTC1_EXT: + wrongFormat = ext_rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT; + break; + case ext_rgtc.COMPRESSED_RED_GREEN_RGTC2_EXT: + case ext_rgtc.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT: + wrongFormat = ext_rgtc.COMPRESSED_RED_RGTC1_EXT; + break; + } + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, wrongFormat, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "format does not match"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 4, 0, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "dimension out of range"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 4, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "dimension out of range"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width + 4, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height + 4, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 4, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 4, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 1, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 2, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 1, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 2, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + + var subData = new Uint8Array(data.buffer, 0, getBlockSize(format)); + + if (width == 8 && height == 8) { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 1, 0, 4, 4, format, subData); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 1, 4, 4, format, subData); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset"); + } + + var stride = width * 4; + for (var yoff = 0; yoff < height; yoff += 4) { + for (var xoff = 0; xoff < width; xoff += 4) { + copyRect(uncompressedData, 0, 0, xoff, yoff, 4, 4, stride); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, xoff, yoff, 4, 4, format, subData); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); + // First test NEAREST filtering. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + wtu.clearAndDrawUnitQuad(gl); + compareRect(width, height, test.channels, uncompressedData, "NEAREST", test.error); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + // Next test LINEAR filtering. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + compareRect(width, height, test.channels, uncompressedData, "LINEAR", test.error); + } + } +} + +function testDXT5_RGBA_PBO() { + debug(""); + debug("testing PBO uploads"); + var width = 8; + var height = 8; + var channels = 4; + var data = img_8x8_rgba_dxt5; + var format = ext.COMPRESSED_RGBA_S3TC_DXT5_EXT; + var uncompressedData = uncompressDXT(width, height, data, format); + + var tex = gl.createTexture(); + + // First, PBO size = image size + var pbo1 = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo1); + gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data, gl.STATIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a PBO"); + + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data.length, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a texture from a PBO"); + + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + compareRect(width, height, channels, uncompressedData, "NEAREST", DEFAULT_COLOR_ERROR); + + // Clear the texture before the next test + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, new Uint8Array(data.length)); + + // Second, image is just a subrange of the PBO + var pbo2 = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo2); + gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data.length*3, gl.STATIC_DRAW); + gl.bufferSubData(gl.PIXEL_UNPACK_BUFFER, data.length, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a PBO subrange"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data.length, data.length); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a texture from a PBO subrange"); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + compareRect(width, height, channels, uncompressedData, "NEAREST", DEFAULT_COLOR_ERROR); +} + +function compareRect(width, height, channels, expectedData, filteringMode, colorError) { + var actual = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, actual); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading back pixels"); + + var div = document.createElement("div"); + div.className = "testimages"; + ctu.insertCaptionedImg(div, "expected", ctu.makeScaledImage(width, height, width, expectedData, true)); + ctu.insertCaptionedImg(div, "actual", ctu.makeScaledImage(width, height, width, actual, true)); + div.appendChild(document.createElement('br')); + document.getElementById("console").appendChild(div); + + var failed = false; + for (var yy = 0; yy < height; ++yy) { + for (var xx = 0; xx < width; ++xx) { + var offset = (yy * width + xx) * 4; + var expected = expectedData.slice(offset, offset + 4); + const was = actual.slice(offset, offset + 4); + + // Compare RGB values + for (var jj = 0; jj < 3; ++jj) { + if (Math.abs(was[jj] - expected[jj]) > colorError) { + failed = true; + testFailed(`RGB at (${xx}, ${yy}) expected: ${expected}` + + ` +/- ${colorError}, was ${was}`); + break; + } + } + + if (channels == 3) { + // BC1 RGB is allowed to be mapped to BC1 RGBA. + // In such a case, 3-color mode black value can be transparent: + // [0, 0, 0, 0] instead of [0, 0, 0, 255]. + + if (actual[offset + 3] != expected[3]) { + // Got non-opaque value for opaque format + + // Check RGB values. Notice, that the condition here + // is more permissive than needed since we don't have + // compressed data at this point. + if (was[0] == 0 && + was[1] == 0 && + was[2] == 0 && + was[3] == 0) { + debug("<b>DXT1 RGB is mapped to DXT1 RGBA</b>"); + } else { + failed = true; + testFailed('Alpha at (' + xx + ', ' + yy + + ') expected: ' + expected[3] + ' was ' + was); + } + } + } else { + // Compare Alpha values + // Acceptable interpolation error depends on endpoints: + // 1.0 / 65535.0 + 0.03 * max(abs(endpoint0 - endpoint1), abs(endpoint0_p - endpoint1_p)) + // For simplicity, assume the worst case (e0 is 0.0, e1 is 1.0). After conversion to unorm8, it is 8. + if (Math.abs(was[3] - expected[3]) > 8) { + failed = true; + testFailed('Alpha at (' + xx + ', ' + yy + + ') expected: ' + expected + ' +/- 8 was ' + was); + } + } + } + } + if (!failed) { + testPassed("texture rendered correctly with " + filteringMode + " filtering"); + } +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-astc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-astc.html new file mode 100644 index 0000000000..d737c3f2a4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-astc.html @@ -0,0 +1,2524 @@ +<!-- +Copyright (c) 2020 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/compressed-texture-utils.js"></script> +<title>WebGL WEBGL_compressed_texture_astc Conformance Tests</title> +<style> +img { + border: 1px solid black; + margin-right: 1em; +} + +.testimages br { + clear: both; +} + +.testimages > div { + float: left; + margin: 1em; +} +</style> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_compressed_texture_astc extension, if it is available."); + +debug(""); + +// Compressed textures generated with ARM's ASTC encoder +// https://github.com/ARM-software/astc-encoder + +// The data below is printed directly from the astc file, +// and the header has been left for clarity reasons. + +// LDR encoded with the following command line: +// 'astcenc -c source.png result.astc {blockSize} -medium' + +// The image used for LDR compression can be found +// at sdk/tests/resources/red-green-hard.png + +var astc_4x4_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x4, 0x4, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x42, 0x2, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x42, 0x2, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x42, 0x2, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x42, 0x2, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x42, 0x2, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0xff, 0x0, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x42, 0x2, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +]); +var astc_5x4_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x5, 0x4, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xc1, 0x2, 0x1, 0x2, 0x1, 0x0, 0x0, 0x80, 0x48, 0x22, 0x89, 0x24, 0x92, 0x48, 0x22, 0x9, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xc1, 0x2, 0x1, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x20, 0x89, 0x24, 0x92, 0x48, 0x22, 0x89, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xc1, 0x2, 0x1, 0x2, 0x1, 0x0, 0x0, 0x80, 0x48, 0x22, 0x89, 0x24, 0x0, 0x40, 0x0, 0x80, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xc1, 0x2, 0x1, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x24, 0x92, 0x48, 0x22, 0x89, +]); +var astc_5x5_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x5, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xf3, 0x0, 0x81, 0xff, 0x7, 0x0, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xf3, 0x0, 0x7f, 0x0, 0xf8, 0x1, 0xe0, 0xff, 0xf1, 0xff, 0xf8, 0x7f, 0x0, 0x0, 0x0, 0x0, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, +]); +var astc_6x5_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x6, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x63, 0x1, 0x81, 0x10, 0x0, 0x0, 0x48, 0x49, 0x29, 0x29, 0x25, 0xa5, 0xa4, 0x94, 0x94, 0x12, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x63, 0x1, 0x81, 0x10, 0x0, 0x0, 0x48, 0x0, 0x28, 0x0, 0x24, 0x0, 0xa4, 0x94, 0x94, 0x92, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x63, 0x1, 0x81, 0x10, 0x0, 0x0, 0x0, 0x48, 0x1, 0x28, 0x1, 0xa4, 0x0, 0x94, 0x0, 0x92, +]); +var astc_6x6_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x6, 0x6, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x4, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x4, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xc3, 0x30, 0xfc, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x4, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xe3, 0xf8, 0xff, 0xff, +]); +var astc_8x5_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x8, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x66, 0x0, 0xc1, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x66, 0x0, 0xc1, 0xff, 0x0, 0x0, 0xf0, 0xff, 0xf0, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x66, 0x0, 0xc1, 0xff, 0x0, 0x0, 0xff, 0xf, 0xff, 0xf, 0xff, 0xf, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x66, 0x0, 0x3f, 0x0, 0x1f, 0x0, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0, 0x3, 0x0, +]); +var astc_8x6_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x8, 0x6, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x70, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x3f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x70, 0x7f, 0x7f, 0xf8, 0x7f, 0x56, 0x3f, 0x72, 0x7f, 0x7f, + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x70, 0x7f, 0x7f, 0x7f, 0x3f, 0x72, 0x78, 0x7f, 0x70, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x50, 0x7f, 0x72, 0xfe, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, +]); +var astc_8x8_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x8, 0x8, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x44, 0x5, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x44, 0x5, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x44, 0x5, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, + 0x44, 0x5, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x3f, +]); +var astc_10x5_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x65, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x65, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x0, 0xfc, 0xf0, 0xc3, 0xff, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x65, 0x1, 0xff, 0x1, 0x0, 0xfe, 0x1, 0x0, 0x0, 0xc0, 0x7, 0x1f, 0x7c, 0xf0, 0xc1, 0x7, +]); +var astc_10x6_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0x6, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xa4, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xa4, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xf0, 0xc3, 0xf, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xa4, 0x1, 0x1, 0xfe, 0xff, 0x1, 0x0, 0x0, 0x0, 0x3e, 0xf8, 0xe0, 0xff, 0xff, 0xff, 0xff, +]); +var astc_10x8_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0x8, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x65, 0x9, 0x69, 0x35, 0x0, 0x8, 0x10, 0x2, 0x0, 0x0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x6, 0x11, 0x69, 0x2d, 0x80, 0x40, 0x2, 0x80, 0x4, 0x0, 0x8, 0x0, 0xff, 0xf, 0xf0, 0xff, + 0x44, 0x5, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +]); +var astc_10x10_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0xa, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xa4, 0xc9, 0xc, 0x3, 0x22, 0x0, 0x8, 0x40, 0x8, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x45, 0x89, 0x7, 0x35, 0x0, 0x40, 0x10, 0x0, 0x20, 0x0, 0x0, 0x1f, 0x7c, 0xf0, 0xc1, 0xff, +]); +var astc_12x10_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xc, 0xa, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x64, 0x8, 0x11, 0x3, 0x22, 0x0, 0x8, 0x40, 0x38, 0xfc, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xe5, 0x10, 0x4a, 0x4d, 0x46, 0x7f, 0x0, 0xc0, 0xf, 0x40, 0xf8, 0x1, 0x39, 0xf1, 0x7, 0x0, +]); +var astc_12x12_argb_ldr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xc, 0xc, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x64, 0xa8, 0x21, 0x3, 0x22, 0x0, 0x8, 0x40, 0x38, 0xfc, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x84, 0xd8, 0xe9, 0x2e, 0x0, 0x0, 0x1, 0x0, 0x80, 0xc0, 0x80, 0x28, 0x2a, 0xab, 0x2a, 0xff, +]); + +// HDR encoded with the following command line: +// 'astcenc -c source.hdr result.astc {blockSize} -medium' + +// The image used for HDR compression can be found +// at sdk/tests/resources/red-green-hard.hdr + +var astc_4x4_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x4, 0x4, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x42, 0x2, 0x1, 0x82, 0x82, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x42, 0x2, 0x81, 0x3, 0x82, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x42, 0x2, 0x81, 0x3, 0x82, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x42, 0x2, 0x83, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, + 0x42, 0x2, 0x83, 0x0, 0x0, 0x41, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff, 0x0, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x42, 0x2, 0x1, 0x82, 0x82, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +]); +var astc_5x4_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x5, 0x4, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xc1, 0x2, 0x1, 0x42, 0x21, 0x0, 0x0, 0x80, 0x48, 0x22, 0x89, 0x24, 0x92, 0x48, 0x22, 0x9, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xc1, 0x2, 0x1, 0x42, 0x21, 0x0, 0x0, 0x0, 0x0, 0x20, 0x89, 0x24, 0x92, 0x48, 0x22, 0x89, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xc1, 0x2, 0x1, 0x42, 0x21, 0x0, 0x0, 0x80, 0x48, 0x22, 0x89, 0x24, 0x0, 0x40, 0x0, 0x80, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xc1, 0x2, 0x1, 0x42, 0x21, 0x0, 0x0, 0x0, 0x0, 0x0, 0x89, 0x24, 0x92, 0x48, 0x22, 0x89, +]); +var astc_5x5_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x5, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xf3, 0x0, 0x81, 0xff, 0x7, 0x0, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xf3, 0x0, 0x81, 0xff, 0x7, 0x0, 0x0, 0x0, 0xe, 0x0, 0x7, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, +]); +var astc_6x5_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x6, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x63, 0x1, 0x81, 0x10, 0x0, 0x0, 0x48, 0x49, 0x29, 0x29, 0x25, 0xa5, 0xa4, 0x94, 0x94, 0x12, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x63, 0x1, 0x81, 0x10, 0x0, 0x0, 0x48, 0x0, 0x28, 0x0, 0x24, 0x0, 0xa4, 0x94, 0x94, 0x92, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x63, 0x1, 0x81, 0x10, 0x0, 0x0, 0x0, 0x48, 0x1, 0x28, 0x1, 0xa4, 0x0, 0x94, 0x0, 0x92, +]); +var astc_6x6_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x6, 0x6, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x4, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x4, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xc3, 0x30, 0xfc, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x4, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0xe3, 0xf8, 0xff, 0xff, +]); +var astc_8x5_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x8, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x66, 0x0, 0xc1, 0xff, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x66, 0x0, 0xc1, 0xff, 0x0, 0x0, 0xf0, 0xff, 0xf0, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x66, 0x0, 0xc1, 0xff, 0x0, 0x0, 0xff, 0xf, 0xff, 0xf, 0xff, 0xf, 0xff, 0xff, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x66, 0x0, 0xc1, 0xff, 0x0, 0x0, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, +]); +var astc_8x6_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x8, 0x6, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x70, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x3f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x70, 0x7f, 0x7f, 0xf8, 0x7f, 0x56, 0x3f, 0x72, 0x7f, 0x7f, + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x70, 0x7f, 0x7f, 0x7f, 0x3f, 0x72, 0x78, 0x7f, 0x70, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x54, 0x1, 0x81, 0x20, 0x0, 0x0, 0x50, 0x7f, 0x72, 0xfe, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, +]); +var astc_8x8_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0x8, 0x8, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x44, 0x5, 0x81, 0x3, 0x82, 0x0, 0x0, 0x0, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0x44, 0x5, 0x81, 0x3, 0x82, 0x0, 0x0, 0x0, 0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x44, 0x5, 0x1, 0x82, 0x82, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, + 0x44, 0x5, 0x1, 0x82, 0x82, 0x0, 0x0, 0x0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x3f, +]); +var astc_10x5_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0x5, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x65, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0x0, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x65, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0xfc, 0xf0, 0xc3, 0xff, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x65, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0xf8, 0xe0, 0x83, 0xf, 0x3e, 0xf8, +]); +var astc_10x6_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0x6, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xa4, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xa4, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xf0, 0xc3, 0xf, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xa4, 0x1, 0x1, 0xfc, 0xfd, 0x1, 0x0, 0x0, 0x0, 0x3e, 0xf8, 0xe0, 0xff, 0xff, 0xff, 0xff, +]); +var astc_10x8_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0x8, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x65, 0x9, 0x69, 0x35, 0x0, 0x8, 0x10, 0x2, 0x0, 0x0, 0xfc, 0xe0, 0x3, 0x0, 0x0, 0x0, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x15, 0x11, 0x69, 0x25, 0x0, 0x84, 0x0, 0x10, 0x0, 0x2, 0x40, 0x88, 0x3f, 0x0, 0x3f, 0x0, + 0x44, 0x5, 0x1, 0x2, 0x2, 0x0, 0x0, 0x0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +]); +var astc_10x10_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xa, 0xa, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0xa4, 0xc9, 0xc, 0x3, 0x22, 0x0, 0x8, 0x40, 0x8, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x45, 0x89, 0x7, 0x35, 0x0, 0x40, 0x0, 0x82, 0x0, 0x0, 0x0, 0xe0, 0x83, 0xf, 0x3e, 0x0, +]); +var astc_12x10_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xc, 0xa, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x64, 0x8, 0x11, 0x3, 0x22, 0x0, 0x8, 0x40, 0x38, 0xfc, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x44, 0x10, 0x4a, 0x49, 0x46, 0xf, 0x1c, 0x3f, 0x0, 0x28, 0xff, 0x1, 0xc0, 0x3f, 0xc, 0x0, +]); +var astc_12x12_argb_hdr = new Uint8Array([ + //0x13, 0xab, 0xa1, 0x5c, 0xc, 0xc, 0x1, 0x10, 0x0, 0x0, 0x10, 0x0, 0x0, 0x1, 0x0, 0x0, HEADER + 0x64, 0xa8, 0x21, 0x3, 0x22, 0x0, 0x8, 0x40, 0x38, 0xfc, 0xc3, 0xff, 0xff, 0xff, 0xff, 0x7f, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0xfc, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, + 0x4, 0xd8, 0xe9, 0x2e, 0x0, 0x8, 0x1, 0x40, 0x80, 0xc0, 0x81, 0x28, 0x2a, 0x0, 0x0, 0x0, +]); + +// Decoded ASTC textures generated with ARM's ASTC encoder +// https://github.com/ARM-software/astc-encoder + +// As the original image is quite simple to compress, +// several decoded images share the same data + +// LDR decoded with the following command line: +// 'astcenc -d source.astc result.tga' +var decoded_4x4To10x6_argb_ldr = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); +var decoded_10x8_argb_ldr = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xdf, 0x20, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); +var decoded_10x10_argb_ldr = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, + 0x20, 0xdf, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x8f, 0x70, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); +var decoded_12x10_argb_ldr = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xdf, 0x20, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x70, 0x8f, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); +var decoded_12x12_argb_ldr = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x10, 0xef, 0x0, 0xff, 0x10, 0xef, 0x0, 0xff, + 0x10, 0xef, 0x0, 0xff, 0x10, 0xef, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x70, 0x8f, 0x0, 0xff, 0x70, 0x8f, 0x0, 0xff, + 0x70, 0x8f, 0x0, 0xff, 0x70, 0x8f, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xcf, 0x30, 0x0, 0xff, 0xcf, 0x30, 0x0, 0xff, + 0xcf, 0x30, 0x0, 0xff, 0xcf, 0x30, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xbf, 0x40, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x60, 0x9f, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); + +var decoded_10x8_argb_ldr_srgb = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xbc, 0x4, 0x0, 0xff, 0x4, 0xbc, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); +var decoded_10x10_argb_ldr_srgb = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x4, 0xbc, 0x0, 0xff, 0x4, 0xbc, 0x0, 0xff, + 0x4, 0xbc, 0x0, 0xff, 0x4, 0xbc, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x46, 0x29, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); +var decoded_12x10_argb_ldr_srgb = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xbc, 0x4, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x29, 0x46, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); +var decoded_12x12_argb_ldr_srgb = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x1, 0xdc, 0x0, 0xff, 0x1, 0xdc, 0x0, 0xff, + 0x1, 0xdc, 0x0, 0xff, 0x1, 0xdc, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x29, 0x46, 0x0, 0xff, 0x29, 0x46, 0x0, 0xff, + 0x29, 0x46, 0x0, 0xff, 0x29, 0x46, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x9f, 0x8, 0x0, 0xff, 0x9f, 0x8, 0x0, 0xff, + 0x9f, 0x8, 0x0, 0xff, 0x9f, 0x8, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x85, 0xd, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x1e, 0x58, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, +]); + +// HDR decoded with the following command line: +// 'astcenc -d source.astc result.tga' + +var decoded_4x4_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfd, 0x0, 0xff, 0x0, 0xfd, 0x0, 0xff, + 0x0, 0xfd, 0x0, 0xff, 0x0, 0xfd, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_5x4_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_5x5_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_6x5_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_6x6_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_8x5_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_8x6_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_8x8_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfd, 0x0, 0xff, 0x0, 0xfd, 0x0, 0xff, + 0x0, 0xfd, 0x0, 0xff, 0x0, 0xfd, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xfd, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); + +var decoded_10x5To10x6_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xfe, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); + +var decoded_10x8_argb_hdr = new Uint8Array([ + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xdf, 0x20, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_10x10_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, + 0x20, 0xdf, 0x0, 0xff, 0x20, 0xdf, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x8f, 0x70, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_12x10_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xdf, 0x20, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x70, 0x8f, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); +var decoded_12x12_argb_hdr = new Uint8Array([ + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0x0, 0xfe, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x10, 0xef, 0x0, 0xff, 0x10, 0xef, 0x0, 0xff, + 0x10, 0xef, 0x0, 0xff, 0x10, 0xef, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0x70, 0x8f, 0x0, 0xff, 0x70, 0x8f, 0x0, 0xff, + 0x70, 0x8f, 0x0, 0xff, 0x70, 0x8f, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xcf, 0x30, 0x0, 0xff, 0xcf, 0x30, 0x0, 0xff, + 0xcf, 0x30, 0x0, 0xff, 0xcf, 0x30, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0xbf, 0x40, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x60, 0x9f, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, 0xff, 0x0, 0x0, 0xff, + 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, 0xfe, 0x0, 0x0, 0xff, +]); + +var wtu = WebGLTestUtils; +var ctu = CompressedTextureUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, {antialias: false}); +var program = wtu.setupTexturedQuad(gl); +var nanToMagentaProgram2D, nanToMagentaProgram2DArray, nanToMagentaProgram3D; +var program2DArray, program3D; + +gl.useProgram(program); + +var extFlag = "WEBGL_compressed_texture_astc"; +var ext = null; +var vao = null; +var validFormats = { + + COMPRESSED_RGBA_ASTC_4x4_KHR : 0x93B0, + COMPRESSED_RGBA_ASTC_5x4_KHR : 0x93B1, + COMPRESSED_RGBA_ASTC_5x5_KHR : 0x93B2, + COMPRESSED_RGBA_ASTC_6x5_KHR : 0x93B3, + COMPRESSED_RGBA_ASTC_6x6_KHR : 0x93B4, + COMPRESSED_RGBA_ASTC_8x5_KHR : 0x93B5, + COMPRESSED_RGBA_ASTC_8x6_KHR : 0x93B6, + COMPRESSED_RGBA_ASTC_8x8_KHR : 0x93B7, + COMPRESSED_RGBA_ASTC_10x5_KHR : 0x93B8, + COMPRESSED_RGBA_ASTC_10x6_KHR : 0x93B9, + COMPRESSED_RGBA_ASTC_10x8_KHR : 0x93BA, + COMPRESSED_RGBA_ASTC_10x10_KHR : 0x93BB, + COMPRESSED_RGBA_ASTC_12x10_KHR : 0x93BC, + COMPRESSED_RGBA_ASTC_12x12_KHR : 0x93BD, + COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR : 0x93D0, + COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR : 0x93D1, + COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR : 0x93D2, + COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR : 0x93D3, + COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR : 0x93D4, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR : 0x93D5, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR : 0x93D6, + COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR : 0x93D7, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR : 0x93D8, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR : 0x93D9, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR : 0x93DA, + COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR : 0x93DB, + COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR : 0x93DC, + COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR : 0x93DD + +}; +var name; + +const errorColor = [255, 0, 255, 255]; +let hasHdr = false; + +var check2DTarget = 1; +var check2DArrayTarget = 1; +var check3DTarget = 1; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // The texture size 3 * 5 * 8 below is divisible by all the different block sizes supported by the extension. + ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 3 * 5 * 8); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, extFlag); + if (!ext) { + testPassed("No WEBGL_compressed_texture_astc support -- this is legal"); + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_astc", false); + } else { + testPassed("Successfully enabled WEBGL_compressed_texture_astc extension"); + + debug(""); + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_astc", true); + runTestExtension(); + } +} + +function runTestExtension() { + debug(""); + debug("Testing " + extFlag); + + // Test that enum values are listed correctly in supported formats and in the extension object. + ctu.testCompressedFormatsListed(gl, validFormats); + ctu.testCorrectEnumValuesInExt(ext, validFormats); + // Test that texture upload buffer size is validated correctly. + ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions); + + if (contextVersion >= 2) { + // For LDR and non-sRGB formats, the error color is magenta *or* all NaNs. To + // make sure we accept this we create a program that transforms an all NaN + // sampling result to magenta. + + nanToMagentaProgram2D = wtu.setupSimpleTextureProgramESSL300(gl, undefined, undefined, + `#version 300 es + precision mediump float; + uniform sampler2D tex; + in vec2 texCoord; + out vec4 out_color; + void main() { + vec4 c = texture(tex, texCoord); + if (all(isnan(c))) { + out_color = vec4(1, 0, 1, 1); + } else { + out_color = c; + } + }` + ); + + nanToMagentaProgram2DArray = wtu.setupSimpleTextureProgramESSL300(gl, undefined, undefined, + `#version 300 es + precision mediump float; + uniform mediump sampler2DArray tex; + in vec2 texCoord; + out vec4 out_color; + void main() { + vec4 c = texture(tex, vec3(texCoord, 0)); + if (all(isnan(c))) { + out_color = vec4(1, 0, 1, 1); + } else { + out_color = c; + } + }` + ); + + nanToMagentaProgram3D = wtu.setupSimpleTextureProgramESSL300(gl, undefined, undefined, + `#version 300 es + precision mediump float; + uniform mediump sampler3D tex; + in vec2 texCoord; + out vec4 out_color; + void main() { + vec4 c = texture(tex, vec3(texCoord, 0)); + if (all(isnan(c))) { + out_color = vec4(1, 0, 1, 1); + } else { + out_color = c; + } + }` + ); + + program2DArray = wtu.setupSimpleTextureProgramESSL300(gl, undefined, undefined, + `#version 300 es + precision mediump float; + uniform mediump sampler2DArray tex; + in vec2 texCoord; + out vec4 out_color; + void main() { + out_color = texture(tex, vec3(texCoord, 0)); + }` + ); + + program3D = wtu.setupSimpleTextureProgramESSL300(gl, undefined, undefined, + `#version 300 es + precision mediump float; + uniform mediump sampler3D tex; + in vec2 texCoord; + out vec4 out_color; + void main() { + out_color = texture(tex, vec3(texCoord, 0)); + }` + ); + } + + // Query supported profiles and test ASTC texture with appropriate formats + var profiles = ext.getSupportedProfiles(); + // Check for HDR support first, since it affects sliced 3D support for LDR + if (profiles.indexOf("hdr") != -1) { + hasHdr = true; + testHDRTextures(); + } else { + testPassed("HDR profile is not supported."); + } + + if (profiles.indexOf("ldr") != -1) { + testLDRTextures(); + } else { + testFailed('LDR profile must be supported.'); + } +} + +function testLDRTextures() { + debug(""); + debug("Testing every LDR texture format compression"); + + var data = []; + var formats = []; + var sRGBformats = []; + var raws = []; + var sRGBraws = []; + + data.push(astc_4x4_argb_ldr, astc_5x4_argb_ldr, astc_5x5_argb_ldr, + astc_6x5_argb_ldr, astc_6x6_argb_ldr, astc_8x5_argb_ldr, + astc_8x6_argb_ldr, astc_8x8_argb_ldr, astc_10x5_argb_ldr, + astc_10x6_argb_ldr, astc_10x8_argb_ldr, astc_10x10_argb_ldr, + astc_12x10_argb_ldr, astc_12x12_argb_ldr); + + formats.push(ext.COMPRESSED_RGBA_ASTC_4x4_KHR, ext.COMPRESSED_RGBA_ASTC_5x4_KHR, + ext.COMPRESSED_RGBA_ASTC_5x5_KHR, ext.COMPRESSED_RGBA_ASTC_6x5_KHR, + ext.COMPRESSED_RGBA_ASTC_6x6_KHR, ext.COMPRESSED_RGBA_ASTC_8x5_KHR, + ext.COMPRESSED_RGBA_ASTC_8x6_KHR, ext.COMPRESSED_RGBA_ASTC_8x8_KHR, + ext.COMPRESSED_RGBA_ASTC_10x5_KHR, ext.COMPRESSED_RGBA_ASTC_10x6_KHR, + ext.COMPRESSED_RGBA_ASTC_10x8_KHR, ext.COMPRESSED_RGBA_ASTC_10x10_KHR, + ext.COMPRESSED_RGBA_ASTC_12x10_KHR, ext.COMPRESSED_RGBA_ASTC_12x12_KHR); + + sRGBformats.push(ext.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, ext.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, + ext.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, ext.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, + ext.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, ext.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, + ext.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, ext.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, + ext.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, ext.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, + ext.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, ext.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, + ext.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, ext.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR); + + // adds decoded image to use in the compare function + // certain block size share the same decoded data + // due to the fact that the test image is quite simple + for (var i = 0; i < 10; ++i) + raws.push(decoded_4x4To10x6_argb_ldr); + raws.push(decoded_10x8_argb_ldr, decoded_10x10_argb_ldr, decoded_12x10_argb_ldr, decoded_12x12_argb_ldr); + + // Given that test images mostly use 0 and 255, sRGB decoding is the same as linear for 4x4 - 10x6 images + for (var i = 0; i < 10; ++i) + sRGBraws.push(decoded_4x4To10x6_argb_ldr); + sRGBraws.push(decoded_10x8_argb_ldr_srgb, decoded_10x10_argb_ldr_srgb, decoded_12x10_argb_ldr_srgb, decoded_12x12_argb_ldr_srgb); + + testASTCTextures(buildTests(data, formats, raws, 'LDR')); + testASTCTextures(buildTests(data, sRGBformats, sRGBraws, 'LDR-sRGB')); +} + +function testHDRTextures() { + debug(""); + debug("Testing every HDR texture format compression"); + + + var data = []; + var formats = []; + var raws = []; + + data.push(astc_4x4_argb_hdr, astc_5x4_argb_hdr, astc_5x5_argb_hdr, + astc_6x5_argb_hdr, astc_6x6_argb_hdr, astc_8x5_argb_hdr, + astc_8x6_argb_hdr, astc_8x8_argb_hdr, astc_10x5_argb_hdr, + astc_10x6_argb_hdr, astc_10x8_argb_hdr, astc_10x10_argb_hdr, + astc_12x10_argb_hdr, astc_12x12_argb_hdr); + + formats.push(ext.COMPRESSED_RGBA_ASTC_4x4_KHR, ext.COMPRESSED_RGBA_ASTC_5x4_KHR, + ext.COMPRESSED_RGBA_ASTC_5x5_KHR, ext.COMPRESSED_RGBA_ASTC_6x5_KHR, + ext.COMPRESSED_RGBA_ASTC_6x6_KHR, ext.COMPRESSED_RGBA_ASTC_8x5_KHR, + ext.COMPRESSED_RGBA_ASTC_8x6_KHR, ext.COMPRESSED_RGBA_ASTC_8x8_KHR, + ext.COMPRESSED_RGBA_ASTC_10x5_KHR, ext.COMPRESSED_RGBA_ASTC_10x6_KHR, + ext.COMPRESSED_RGBA_ASTC_10x8_KHR, ext.COMPRESSED_RGBA_ASTC_10x10_KHR, + ext.COMPRESSED_RGBA_ASTC_12x10_KHR, ext.COMPRESSED_RGBA_ASTC_12x12_KHR); + + // adds decoded image to use in the compare function + // certain block size share the same decoded data + // due to the fact that the test image is quite simple + raws.push(decoded_4x4_argb_hdr, decoded_5x4_argb_hdr, decoded_5x5_argb_hdr, decoded_6x5_argb_hdr, + decoded_6x6_argb_hdr, decoded_8x5_argb_hdr, decoded_8x6_argb_hdr, decoded_8x8_argb_hdr, + decoded_10x5To10x6_argb_hdr, decoded_10x5To10x6_argb_hdr, decoded_10x8_argb_hdr, + decoded_10x10_argb_hdr, decoded_12x10_argb_hdr, decoded_12x12_argb_hdr); + + testASTCTextures(buildTests(data, formats, raws, 'HDR')); +} + +function testASTCTextures(tests) { + debug("<hr/>"); + for (var i = 0; i < tests.length; ++i) { + testASTCTexture(tests[i], contextVersion >= 2); + } +} + +function testASTCTexture(test, useES3) { + var data = test.data; + var width = test.width; + var height = test.height; + var format = test.format; + var raw = test.raw; + + canvas.width = width; + canvas.height = height; + gl.viewport(0, 0, width, height); + debug(""); + debug("testing " + ctu.formatToString(ext, format) + " " + width + "x" + height + " (" + test.mode + ")" + + (useES3 ? " via ES 3.0 entrypoints" : " via compressedTexImage2D")); + debug(""); + + function errorColorMaybeNaN() { + return format >= ext.COMPRESSED_RGBA_ASTC_4x4_KHR && format <= ext.COMPRESSED_RGBA_ASTC_12x12_KHR; + } + + function ensureParameters(target) { + gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + } + + function setupEmptyTexture(target, useStorage) { + var tex = gl.createTexture(); + gl.bindTexture(target, tex); + ensureParameters(target); + + if (target == gl.TEXTURE_2D) { + if (useStorage) { + gl.texStorage2D(target, 1, format, width, height); + } else { + gl.compressedTexImage2D(target, 0, format, width, height, 0, new Uint8Array(data.length)); + } + } else if (target == gl.TEXTURE_2D_ARRAY || target == gl.TEXTURE_3D) { + if (useStorage) { + gl.texStorage3D(target, 1, format, width, height, 1); + } else { + gl.compressedTexImage3D(target, 0, format, width, height, 1, 0, new Uint8Array(data.length)); + } + } + + return tex; // Keep reference to the texture so it could be deleted + } + + function uploadSubData(target) { + function checkResult(target, expectations, dim) { + switch (target) { + case gl.TEXTURE_2D: + wtu.glErrorShouldBe(gl, expectations[0], "uploading compressed 2D texture data via compressedTexSubImage" + dim); + break; + case gl.TEXTURE_2D_ARRAY: + wtu.glErrorShouldBe(gl, expectations[1], "uploading compressed 2D array texture data via compressedTexSubImage" + dim); + break; + case gl.TEXTURE_3D: + wtu.glErrorShouldBe(gl, expectations[2], "uploading compressed 3D texture data via compressedTexSubImage" + dim); + break; + } + } + + gl.compressedTexSubImage2D(target, 0, 0, 0, width, height, format, data); + checkResult(target, [gl.NO_ERROR, gl.INVALID_ENUM, gl.INVALID_ENUM ], "2D"); + + if (useES3) { + gl.compressedTexSubImage3D(target, 0, 0, 0, 0, width, height, 1, format, data); + checkResult(target, [gl.INVALID_ENUM, gl.NO_ERROR, gl.NO_ERROR], "3D"); + } + } + + function setupFilledTexture(target) { + var tex = gl.createTexture(); + gl.bindTexture(target, tex); + ensureParameters(target); + + if (target == gl.TEXTURE_2D) { + gl.compressedTexImage2D(target, 0, format, width, height, 1, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border"); + + gl.compressedTexImage2D(target, 0, format, width, height, 0, data); + } else if (target == gl.TEXTURE_2D_ARRAY || target == gl.TEXTURE_3D) { + gl.compressedTexImage3D(target, 0, format, width, height, 1, 0, data); + } + + return tex; // Keep reference to the texture so it could be deleted + } + + function switchProgram(target) { + // For LDR non-sRGB and HDR formats, we accept all NaNs as a valid result. + // This program transforms all NaNs to the normal error color, magenta. + + // There's no 100% reliable way to check NaN values on ESSL 1.0, + // so error color is verified only on ESSL 3.0. + + if (errorColorMaybeNaN()) { + if (target == gl.TEXTURE_2D) { + gl.useProgram(nanToMagentaProgram2D); + } else if (target == gl.TEXTURE_2D_ARRAY) { + gl.useProgram(nanToMagentaProgram2DArray); + } else if (target == gl.TEXTURE_3D) { + gl.useProgram(nanToMagentaProgram3D); + } + } else { + if (target == gl.TEXTURE_2D) { + gl.useProgram(program); + } else if (target == gl.TEXTURE_2D_ARRAY) { + gl.useProgram(program2DArray); + } else if (target == gl.TEXTURE_3D) { + gl.useProgram(program3D); + } + } + } + + function checkErrorColor() { + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad from empty texture"); + wtu.checkCanvas(gl, errorColor, "texture should be initialized to error color"); + } + + function checkSampling(target) { + // Check that the decoded image is consistent with NEAREST filtering + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing quad with NEAREST filtering"); + compareRect(width, height, test.channels, width, height, raw, data, format, undefined, "NEAREST"); + + // Check that the decoded image is consistent with LINEAR filtering + gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing quad with LINEAR filtering"); + compareRect(width, height, test.channels, width, height, raw, data, format, undefined, "LINEAR"); + + debug(""); + // Mipmaps handling + gl.generateMipmap(target); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture"); + } + + var tex; + + if (useES3) { + // 2D texture + if (check2DTarget) { + switchProgram(gl.TEXTURE_2D); + + // immutable + tex = setupEmptyTexture(gl.TEXTURE_2D, true); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed texture via texStorage2D"); + checkErrorColor(); + uploadSubData(gl.TEXTURE_2D); + checkSampling(gl.TEXTURE_2D) + gl.deleteTexture(tex); + + // mutable empty + tex = setupEmptyTexture(gl.TEXTURE_2D, false); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating empty compressed texture via compressedTexImage2D"); + checkErrorColor(); + uploadSubData(gl.TEXTURE_2D); + checkSampling(gl.TEXTURE_2D) + gl.deleteTexture(tex); + + // mutable filled + tex = setupFilledTexture(gl.TEXTURE_2D); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating filled compressed texture via compressedTexImage2D"); + checkSampling(gl.TEXTURE_2D) + gl.deleteTexture(tex); + } + + // 2D array + if (check2DArrayTarget) { + switchProgram(gl.TEXTURE_2D_ARRAY); + + // immutable + tex = setupEmptyTexture(gl.TEXTURE_2D_ARRAY, true); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed texture array via texStorage3D"); + checkErrorColor(); + uploadSubData(gl.TEXTURE_2D_ARRAY); + checkSampling(gl.TEXTURE_2D_ARRAY) + gl.deleteTexture(tex); + + // mutable empty + tex = setupEmptyTexture(gl.TEXTURE_2D_ARRAY, false); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating empty compressed texture array via compressedTexImage3D"); + checkErrorColor(); + uploadSubData(gl.TEXTURE_2D_ARRAY); + checkSampling(gl.TEXTURE_2D_ARRAY) + gl.deleteTexture(tex); + + // mutable filled + tex = setupFilledTexture(gl.TEXTURE_2D_ARRAY); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating filled compressed texture array via compressedTexImage3D"); + checkSampling(gl.TEXTURE_2D_ARRAY) + gl.deleteTexture(tex); + } + + // 3D texture + if (check3DTarget) { + switchProgram(gl.TEXTURE_3D); + // immutable + tex = setupEmptyTexture(gl.TEXTURE_3D, true); + if (hasHdr) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed sliced 3D texture via texStorage3D"); + checkErrorColor(); + uploadSubData(gl.TEXTURE_3D); + checkSampling(gl.TEXTURE_3D) + } else { + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "allocating compressed sliced 3D texture via texStorage3D"); + } + gl.deleteTexture(tex); + + // mutable empty + tex = setupEmptyTexture(gl.TEXTURE_3D, false); + if (hasHdr) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating empty compressed sliced 3D texture via compressedTexImage3D"); + checkErrorColor(); + uploadSubData(gl.TEXTURE_3D); + checkSampling(gl.TEXTURE_3D) + } else { + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "allocating empty compressed sliced 3D texture via compressedTexImage3D"); + } + gl.deleteTexture(tex); + + // mutable filled + tex = setupFilledTexture(gl.TEXTURE_3D); + if (hasHdr) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating filled compressed sliced 3D texture via compressedTexImage3D"); + checkSampling(gl.TEXTURE_3D) + } else { + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "allocating filled compressed sliced 3D texture via compressedTexImage3D"); + } + gl.deleteTexture(tex); + } + } else { + // 2D texture + if (check2DTarget) { + // mutable empty + tex = setupEmptyTexture(gl.TEXTURE_2D, false); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating empty compressed texture via compressedTexImage2D"); + + // Skip error color check on HDR and non-sRGB formats on WebGL 1.0 + if (!errorColorMaybeNaN()) checkErrorColor(); + uploadSubData(gl.TEXTURE_2D); + checkSampling(gl.TEXTURE_2D) + gl.deleteTexture(tex); + + // mutable filled + tex = setupFilledTexture(gl.TEXTURE_2D); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating filled compressed texture via compressedTexImage2D"); + checkSampling(gl.TEXTURE_2D) + gl.deleteTexture(tex); + } + } +} + +function compareRect( + actualWidth, actualHeight, actualChannels, + dataWidth, dataHeight, expectedData, + testData, testFormat, tolerance, filteringMode) { + + if(typeof(tolerance) == 'undefined') { tolerance = 5; } + var actual = new Uint8Array(actualWidth * actualHeight * 4); + gl.readPixels(0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual); + flipImage(actual, actualWidth, actualHeight); + + var div = document.createElement("div"); + div.className = "testimages"; + ctu.insertCaptionedImg(div, "expected", ctu.makeScaledImage( + actualWidth, actualHeight, dataWidth, expectedData, + actualChannels == 4)); + ctu.insertCaptionedImg(div, "actual", ctu.makeScaledImage( + actualWidth, actualHeight, actualWidth, actual, + actualChannels == 4)); + div.appendChild(document.createElement('br')); + document.getElementById("console").appendChild(div); + + var failed = false; + for (var yy = 0; yy < actualHeight; ++yy) { + for (var xx = 0; xx < actualWidth; ++xx) { + var actualOffset = (yy * actualWidth + xx) * 4; + var expectedOffset = (yy * dataWidth + xx) * 4; + var expected = [ + expectedData[expectedOffset + 0], + expectedData[expectedOffset + 1], + expectedData[expectedOffset + 2], + (actualChannels == 3 ? 255 : expectedData[expectedOffset + 3]) + ]; + for (var jj = 0; jj < 4; ++jj) { + if (Math.abs(actual[actualOffset + jj] - expected[jj]) > tolerance) { + failed = true; + var was = actual[actualOffset + 0].toString(); + for (var j = 1; j < 4; ++j) { + was += "," + actual[actualOffset + j]; + } + testFailed('at (' + xx + ', ' + yy + + ') expected: ' + expected + ' was ' + was); + } + } + } + } + if (!failed) { + testPassed("texture rendered correctly with " + filteringMode + " filtering"); + } +} + +// Builds several tests from two arrays +// data gives each Uint8Array encoded data to use +// formats the associate format to decode the data +// raws gives each decoded Uint8Array data for texture comparison +// mode 'LDR', 'LDR-sRGB', or 'HDR' +function buildTests(data, formats, raws, mode) { + + var tests = []; + for (var i = 0; i < data.length; ++i) { + var test = { + width: 16, + height: 16, + channels: 4, + data: data[i], + format: formats[i], + raw: raws[i], + mode: mode + }; + tests.push(test); + } + + return tests; +} + +function expectedByteLength(w, h, format) { + + if (format == validFormats.COMPRESSED_RGBA_ASTC_4x4_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR) + return Math.floor((w + 3) / 4) * Math.floor((h + 3) / 4) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_5x4_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR) + return Math.floor((w + 4) / 5) * Math.floor((h + 3) / 4) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_5x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR) + return Math.floor((w + 4) / 5) * Math.floor((h + 4) / 5) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_6x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR) + return Math.floor((w + 5) / 6) * Math.floor((h + 4) / 5) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_6x6_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR) + return Math.floor((w + 5) / 6) * Math.floor((h + 5) / 6) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_8x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR) + return Math.floor((w + 7) / 8) * Math.floor((h + 4) / 5) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_8x6_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR) + return Math.floor((w + 7) / 8) * Math.floor((h + 5) / 6) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_8x8_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR) + return Math.floor((w + 7) / 8) * Math.floor((h + 7) / 8) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x5_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR) + return Math.floor((w + 9) / 10) * Math.floor((h + 4) / 5) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x6_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR) + return Math.floor((w + 9) / 10) * Math.floor((h + 5) / 6) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x8_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR) + return Math.floor((w + 9) / 10) * Math.floor((h + 7) / 8) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_10x10_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR) + return Math.floor((w + 9) / 10) * Math.floor((h + 9) / 10) * 16; + else if (format == validFormats.COMPRESSED_RGBA_ASTC_12x10_KHR || format == validFormats.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR) + return Math.floor((w + 11) / 12) * Math.floor((h + 9) / 10) * 16; + + return Math.floor((w + 11) / 12) * Math.floor((h + 11) / 12) * 16; +} + +function getBlockDimensions(format) { + var re = /.*_(\d+)x(\d+)_KHR/; + for (name in validFormats) { + if (validFormats[name] === format) { + var match = name.match(re); + return { + width: parseInt(match[1], 10), + height: parseInt(match[2], 10) + }; + } + } + testFailed('Could not find block dimensions for format ' + ctu.formatToString(ext, format)); + return {width: 4, height: 4}; +} + +// Swaps two cells in an arraybuffer. +// this function is used in the image flipping function +function swapCell(array, i, j) { + var a = array[i]; + array[i] = array[j]; + array[j] = a; +} + +function flipImage(imgBuffer, w, h) { + var halfHeight = h / 2; + + for (var j = 0; j < halfHeight; j++) { + for (var i = 0; i < w; i++) { + var beginByte = (j * w + i) * 4; + var endByte = ((h - j - 1) * w + i) * 4; + + swapCell(imgBuffer, beginByte, endByte); + swapCell(imgBuffer, beginByte + 1, endByte + 1); + swapCell(imgBuffer, beginByte + 2, endByte + 2); + swapCell(imgBuffer, beginByte + 3, endByte + 3); + } + } +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html new file mode 100644 index 0000000000..8a85239ff9 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html @@ -0,0 +1,126 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WEBGL_compressed_texture_etc Conformance Tests</title> +<LINK rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/compressed-texture-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_compressed_texture_etc extension, if it is available."); + +debug(""); + +var validFormats = { + COMPRESSED_R11_EAC : 0x9270, + COMPRESSED_SIGNED_R11_EAC : 0x9271, + COMPRESSED_RG11_EAC : 0x9272, + COMPRESSED_SIGNED_RG11_EAC : 0x9273, + COMPRESSED_RGB8_ETC2 : 0x9274, + COMPRESSED_SRGB8_ETC2 : 0x9275, + COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 : 0x9276, + COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 : 0x9277, + COMPRESSED_RGBA8_ETC2_EAC : 0x9278, + COMPRESSED_SRGB8_ALPHA8_ETC2_EAC : 0x9279 +}; + +function expectedByteLength(width, height, format) { + var blockSizeInBytes = 8; + + var largerBlockFormats = [ + validFormats.COMPRESSED_RG11_EAC, + validFormats.COMPRESSED_SIGNED_RG11_EAC, + validFormats.COMPRESSED_RGBA8_ETC2_EAC, + validFormats.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC]; + + if (largerBlockFormats.indexOf(format) >= 0) { + blockSizeInBytes = 16; + } + + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * blockSizeInBytes; +} + +function getBlockDimensions(format) { + return {width: 4, height: 4}; +} + +var wtu = WebGLTestUtils; +var ctu = CompressedTextureUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var gl = wtu.create3DContext(); +var WEBGL_compressed_texture_etc; + +var formats = null; + +function runTest() { + if (!gl) { + testFailed("context does not exist"); + } else { + testPassed("context exists"); + + ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4); + + WEBGL_compressed_texture_etc = gl.getExtension("WEBGL_compressed_texture_etc"); + + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_etc", WEBGL_compressed_texture_etc !== null); + + var isPositive = WEBGL_compressed_texture_etc !== null; + + if (isPositive) { + // Test that enum values are listed correctly in supported formats and in the extension object. + ctu.testCompressedFormatsListed(gl, validFormats); + ctu.testCorrectEnumValuesInExt(WEBGL_compressed_texture_etc, validFormats); + // Test that texture upload buffer size is validated correctly. + ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + for (var name in validFormats) { + if (validFormats.hasOwnProperty(name)) { + var format = validFormats[name]; + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, " + format + ", 4, 4, 0, new Uint8Array(" + expectedByteLength(4, 4, format) + "))"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, " + format + ", new Uint8Array(" + expectedByteLength(4, 4, format) + "))"); + } + } + } + + var tex2 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex2); + + debug(""); + if (contextVersion >= 2) { + var expectedError = isPositive ? gl.INVALID_OPERATION: [gl.INVALID_ENUM, gl.INVALID_OPERATION]; + // `null` coerces into `0` for the PBO entrypoint, yielding INVALID_OP due to no PBO bound. + wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 0, 0, null)"); + wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, 0, null)"); + wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexImage3D(gl.TEXTURE_2D_ARRAY, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 4, 0, 0, null)"); + wtu.shouldGenerateGLError(gl, expectedError, "gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, 0, null)"); + } else { + shouldThrow("gl.compressedTexImage2D(gl.TEXTURE_2D, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 0, null)"); + shouldThrow("gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, null)"); + shouldThrow("gl.compressedTexImage3D(gl.TEXTURE_2D_ARRAY, 0, validFormats.COMPRESSED_R11_EAC, 4, 4, 4, 0, null)"); + shouldThrow("gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 0, validFormats.COMPRESSED_R11_EAC, null)"); + } + } +} + +runTest(); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc1.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc1.html new file mode 100644 index 0000000000..09a6ed0642 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc1.html @@ -0,0 +1,74 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WEBGL_compressed_texture_etc1 Conformance Tests</title> +<LINK rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/compressed-texture-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_compressed_texture_etc1 extension, if it is available."); + +debug(""); + +var validFormats = { + COMPRESSED_RGB_ETC1_WEBGL: 0x8D64, +}; + +function expectedByteLength(width, height, format) { + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; +} + +function getBlockDimensions(format) { + return {width: 4, height: 4}; +} + +var wtu = WebGLTestUtils; +var ctu = CompressedTextureUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var gl = wtu.create3DContext(); +var WEBGL_compressed_texture_etc1; + +var formats = null; + +function runTest() { + if (!gl) { + testFailed("context does not exist"); + } else { + testPassed("context exists"); + + ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4); + + WEBGL_compressed_texture_etc1 = gl.getExtension("WEBGL_compressed_texture_etc1"); + + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_etc1", WEBGL_compressed_texture_etc1 !== null); + + if (WEBGL_compressed_texture_etc1 !== null) { + // Test that enum values are listed correctly in supported formats and in the extension object. + ctu.testCompressedFormatsListed(gl, validFormats); + ctu.testCorrectEnumValuesInExt(WEBGL_compressed_texture_etc1, validFormats); + // Test that texture upload buffer size is validated correctly. + ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions); + } + } +} + +runTest(); + +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-pvrtc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-pvrtc.html new file mode 100644 index 0000000000..465f44a9cd --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-pvrtc.html @@ -0,0 +1,371 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<title>WebGL WEBGL_compressed_texture_pvrtc Conformance Tests</title> +<style> +img { + border: 1px solid black; + margin-right: 1em; +} +.testimages { +} + +.testimages br { + clear: both; +} + +.testimages > div { + float: left; + margin: 1em; +} +</style> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_compressed_texture_pvrtc extension, if it is available."); + +debug(""); + +var pvrtc_4x4_2bpp = new Uint8Array([ + 0x77, 0x22, 0x77, 0x22, 0xbb, 0x2b, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]); + +var pvrtc_4x4_4bpp = new Uint8Array([ + 0x1b, 0x1b, 0x1b, 0x1b, 0xba, 0x2b, 0x00, 0x80, 0x1b, 0x1b, 0x1b, 0x1b, 0xba, 0x2b, 0x00, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +]); + +var pvrtc_4x4_rgba_decoded = new Uint8Array([ + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xba, 0x44, + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb5, 0x44, + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb5, 0x44, + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb7, 0x44, +]); + +var pvrtc_4x4_rgb_decoded = new Uint8Array([ + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xba, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb5, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb5, 0xff, + 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb7, 0xff, +]); + +var wtu = WebGLTestUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, {antialias: false}); +var program = wtu.setupTexturedQuad(gl); +var ext = null; +var vao = null; +var validFormats = { + COMPRESSED_RGB_PVRTC_4BPPV1_IMG : 0x8C00, + COMPRESSED_RGB_PVRTC_2BPPV1_IMG : 0x8C01, + COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0x8C02, + COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : 0x8C03, +}; +var name; +var supportedFormats; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_pvrtc"); + if (!ext) { + testPassed("No WEBGL_compressed_texture_pvrtc support -- this is legal"); + runSupportedTest(false); + } else { + testPassed("Successfully enabled WEBGL_compressed_texture_pvrtc extension"); + + runSupportedTest(true); + runTestExtension(); + } +} + +function runSupportedTest(extensionEnabled) { + var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_pvrtc"); + if (name !== undefined) { + if (extensionEnabled) { + testPassed("WEBGL_compressed_texture_pvrtc listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_compressed_texture_pvrtc listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_compressed_texture_pvrtc not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_compressed_texture_pvrtc not listed as supported and getExtension failed -- this is legal"); + } + } +} + + +function runTestDisabled() { + debug("Testing binding enum with extension disabled"); + + supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); + shouldBe("supportedFormats", "[]"); +} + +function formatExists(format, supportedFormats) { + for (var ii = 0; ii < supportedFormats.length; ++ii) { + if (format == supportedFormats[ii]) { + testPassed("supported format " + formatToString(format) + " is exists"); + return; + } + } + testFailed("supported format " + formatToString(format) + " does not exist"); +} + +function formatToString(format) { + for (var p in ext) { + if (ext[p] == format) { + return p; + } + } + return "0x" + format.toString(16); +} + +function runTestExtension() { + debug("Testing WEBGL_compressed_texture_pvrtc"); + + // check that all format enums exist. + for (name in validFormats) { + var expected = "0x" + validFormats[name].toString(16); + var actual = "ext['" + name + "']"; + shouldBe(actual, expected); + } + + supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); + // There should be exactly 4 formats for both WebGL 1.0 and WebGL 2.0. + shouldBe("supportedFormats.length", "4"); + + // check that all 4 formats exist + for (var name in validFormats.length) { + formatExists(validFormats[name], supportedFormats); + } + + // Test each format + testPVRTC_RGBA_2BPP(); + testPVRTC_RGB_2BPP(); + testPVRTC_RGBA_4BPP(); + testPVRTC_RGB_4BPP(); +} + +function testPVRTC_RGBA_2BPP() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: pvrtc_4x4_2bpp, + raw: pvrtc_4x4_rgba_decoded, + format: ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG + } + ]; + testPVRTCTextures(tests); +} + +function testPVRTC_RGB_2BPP() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: pvrtc_4x4_2bpp, + raw: pvrtc_4x4_rgb_decoded, + format: ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG + } + ]; + testPVRTCTextures(tests); +} + +function testPVRTC_RGBA_4BPP() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: pvrtc_4x4_4bpp, + raw: pvrtc_4x4_rgba_decoded, + format: ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + } + ]; + testPVRTCTextures(tests); +} + +function testPVRTC_RGB_4BPP() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: pvrtc_4x4_4bpp, + raw: pvrtc_4x4_rgb_decoded, + format: ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG + } + ]; + testPVRTCTextures(tests); +} + +function testPVRTCTextures(tests) { + debug("<hr/>"); + for (var ii = 0; ii < tests.length; ++ii) { + testPVRTCTexture(tests[ii]); + } +} + +function testPVRTCTexture(test) { + var data = new Uint8Array(test.data); + var width = test.width; + var height = test.height; + var format = test.format; + var uncompressedData = test.raw; + + canvas.width = width; + canvas.height = height; + gl.viewport(0, 0, width, height); + debug("testing " + formatToString(format) + " " + width + "x" + height); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); + gl.generateMipmap(gl.TEXTURE_2D); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture"); + wtu.clearAndDrawUnitQuad(gl); + compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "NEAREST"); + // Test again with linear filtering. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "LINEAR"); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border"); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "compressedTexSubImage2D allowed for reloading of complete textures"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 2, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 2, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 2, 0, width - 2, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 2, width, height - 2, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); +} + +function insertImg(element, caption, img) { + var div = document.createElement("div"); + div.appendChild(img); + var label = document.createElement("div"); + label.appendChild(document.createTextNode(caption)); + div.appendChild(label); + element.appendChild(div); +} + +function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) { + var scale = 8; + var c = document.createElement("canvas"); + c.width = imageWidth * scale; + c.height = imageHeight * scale; + var ctx = c.getContext("2d"); + for (var yy = 0; yy < imageHeight; ++yy) { + for (var xx = 0; xx < imageWidth; ++xx) { + var offset = (yy * dataWidth + xx) * 4; + ctx.fillStyle = "rgba(" + + data[offset + 0] + "," + + data[offset + 1] + "," + + data[offset + 2] + "," + + (alpha ? data[offset + 3] / 255 : 1) + ")"; + ctx.fillRect(xx * scale, yy * scale, scale, scale); + } + } + return wtu.makeImageFromCanvas(c); +} +function compareRect( + actualWidth, actualHeight, actualChannels, + dataWidth, dataHeight, expectedData, + testData, testFormat, tolerance, filteringMode) { + if(typeof(tolerance) == 'undefined') { tolerance = 5; } + var actual = new Uint8Array(actualWidth * actualHeight * 4); + gl.readPixels( + 0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual); + + var div = document.createElement("div"); + div.className = "testimages"; + insertImg(div, "expected", makeImage( + actualWidth, actualHeight, dataWidth, expectedData, + actualChannels == 4)); + insertImg(div, "actual", makeImage( + actualWidth, actualHeight, actualWidth, actual, + actualChannels == 4)); + div.appendChild(document.createElement('br')); + document.getElementById("console").appendChild(div); + + var failed = false; + for (var yy = 0; yy < actualHeight; ++yy) { + for (var xx = 0; xx < actualWidth; ++xx) { + var actualOffset = (yy * actualWidth + xx) * 4; + var expectedOffset = (yy * dataWidth + xx) * 4; + var expected = [ + expectedData[expectedOffset + 0], + expectedData[expectedOffset + 1], + expectedData[expectedOffset + 2], + (actualChannels == 3 ? 255 : expectedData[expectedOffset + 3]) + ]; + for (var jj = 0; jj < 4; ++jj) { + if (Math.abs(actual[actualOffset + jj] - expected[jj]) > tolerance) { + failed = true; + var was = actual[actualOffset + 0].toString(); + for (var j = 1; j < 4; ++j) { + was += "," + actual[actualOffset + j]; + } + testFailed('at (' + xx + ', ' + yy + + ') expected: ' + expected + ' was ' + was); + } + } + } + } + if (!failed) { + testPassed("texture rendered correctly with " + filteringMode + " filtering"); + } +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html new file mode 100644 index 0000000000..91fc3f0b1c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html @@ -0,0 +1,912 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/compressed-texture-utils.js"></script> +<title>WebGL WEBGL_compressed_texture_s3tc_srgb Conformance Tests</title> +<style> +img { + border: 1px solid black; + margin-right: 1em; +} + +.testimages br { + clear: both; +} + +.testimages > div { + float: left; + margin: 1em; +} +</style> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> +<div id="console"></div> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_compressed_texture_s3tc_srgb extension, if it is available."); + +debug(""); + +/* +These tests use the same payloads as a non-sRGB version. When running side-by-side, +images from these tests must appear much darker than linear counterparts. +*/ + +/* +BC1 (DXT1) block +e0 = [ 0, 255, 0] +e1 = [255, 0, 0] +e0 < e1, so it uses 3-color mode + +local palette + 0: [ 0, 255, 0, 255] + 1: [255, 0, 0, 255] + 2: [128, 128, 0, 255] + 3: [ 0, 0, 0, 255] // for BC1 RGB + 3: [ 0, 0, 0, 0] // for BC1 RGBA +selectors + 3 2 1 0 + 2 2 1 0 + 1 1 1 0 + 0 0 0 0 + +Extending this block with opaque alpha and uploading as BC2 or BC3 +will generate wrong colors because BC2 and BC3 do not have 3-color mode. +*/ +var img_4x4_rgba_dxt1 = new Uint8Array([ + 0xE0, 0x07, 0x00, 0xF8, 0x1B, 0x1A, 0x15, 0x00 +]); + +/* +BC2 (DXT3) block + +Quantized alpha values + 0 1 2 3 + 4 5 6 7 + 8 9 A B + C D E F + +RGB block +e0 = [255, 0, 0] +e1 = [ 0, 255, 0] +BC2 has only 4-color mode + +local palette + 0: [255, 0, 0] + 1: [ 0, 255, 0] + 2: [170, 85, 0] + 3: [ 85, 170, 0] +selectors + 0 1 2 3 + 1 1 2 3 + 2 2 2 3 + 3 3 3 3 +*/ +var img_4x4_rgba_dxt3 = new Uint8Array([ + 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, + 0x00, 0xF8, 0xE0, 0x07, 0xE4, 0xE5, 0xEA, 0xFF +]); + +/* +BC3 (DXT5) block + +Alpha block (aka DXT5A) +e0 = 255 +e1 = 0 +e0 > e1, so using 6 intermediate points +local palette + 255, 0, 219, 182, 146, 109, 73, 36 +selectors + 0 1 2 3 + 1 2 3 4 + 2 3 4 5 + 3 4 5 6 + +RGB block +e0 = [255, 0, 0] +e1 = [ 0, 255, 0] +BC3 has only 4-color mode + +local palette + 0: [255, 0, 0] + 1: [ 0, 255, 0] + 2: [170, 85, 0] + 3: [ 85, 170, 0] +selectors + 3 2 1 0 + 3 2 1 1 + 3 2 2 2 + 3 3 3 3 +*/ +var img_4x4_rgba_dxt5 = new Uint8Array([ + 0xFF, 0x00, 0x88, 0x16, 0x8D, 0x1A, 0x3B, 0xD6, + 0x00, 0xF8, 0xE0, 0x07, 0x1B, 0x5B, 0xAB, 0xFF +]); + +/* +8x8 block endpoints use half-intensity values (appear darker than 4x4) +*/ +var img_8x8_rgba_dxt1 = new Uint8Array([ + 0xe0,0x03,0x00,0x78,0x13,0x10,0x15,0x00, + 0x0f,0x00,0xe0,0x7b,0x11,0x10,0x15,0x00, + 0xe0,0x03,0x0f,0x78,0x44,0x45,0x40,0x55, + 0x0f,0x00,0xef,0x03,0x44,0x45,0x40,0x55 +]); +var img_8x8_rgba_dxt3 = new Uint8Array([ + 0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0x78,0xe0,0x03,0x44,0x45,0x40,0x55, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0x7b,0x0f,0x00,0x44,0x45,0x40,0x55, + 0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x0f,0x78,0xe0,0x03,0x11,0x10,0x15,0x00, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0x03,0x0f,0x00,0x11,0x10,0x15,0x00 +]); +var img_8x8_rgba_dxt5 = new Uint8Array([ +0xff,0x69,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x78,0xe0,0x03,0x44,0x45,0x40,0x55, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x7b,0x0f,0x00,0x44,0x45,0x40,0x55, + 0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x0f,0x78,0xe0,0x03,0x11,0x10,0x15,0x00, + 0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xef,0x03,0xef,0x00,0x11,0x10,0x15,0x00 +]); + +var wtu = WebGLTestUtils; +var ctu = CompressedTextureUtils; +var contextVersion = wtu.getDefault3DContextVersion(); +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, {antialias: false}); +var program = wtu.setupTexturedQuad(gl); +var ext = null; +var vao = null; +var validFormats = { + COMPRESSED_SRGB_S3TC_DXT1_EXT : 0x8C4C, + COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : 0x8C4D, + COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : 0x8C4E, + COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : 0x8C4F, +}; +var name; +var supportedFormats; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + ctu.testCompressedFormatsUnavailableWhenExtensionDisabled(gl, validFormats, expectedByteLength, 4); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc_srgb"); + if (!ext) { + testPassed("No WEBGL_compressed_texture_s3tc_srgb support -- this is legal"); + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc_srgb", false); + } else { + testPassed("Successfully enabled WEBGL_compressed_texture_s3tc_srgb extension"); + + wtu.runExtensionSupportedTest(gl, "WEBGL_compressed_texture_s3tc_srgb", true); + runTestExtension(); + } +} + +function expectedByteLength(width, height, format) { + if (format == validFormats.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT || format == validFormats.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) { + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16; + } + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; +} + +function getBlockDimensions(format) { + return {width: 4, height: 4}; +} + +function runTestExtension() { + debug(""); + debug("Testing WEBGL_compressed_texture_s3tc_srgb"); + + // Test that enum values are listed correctly in supported formats and in the extension object. + ctu.testCompressedFormatsListed(gl, validFormats); + ctu.testCorrectEnumValuesInExt(ext, validFormats); + // Test that texture upload buffer size is validated correctly. + ctu.testFormatRestrictionsOnBufferSize(gl, validFormats, expectedByteLength, getBlockDimensions); + + // Test each format + testDXT1_SRGB(); + testDXT1_SRGB_ALPHA(); + testDXT3_SRGB_ALPHA(); + testDXT5_SRGB_ALPHA(); + + // Test compressed PBOs with a single format + if (contextVersion >= 2) { + testDXT5_SRGB_ALPHA_PBO(); + } + + // Test TexImage validation on level dimensions combinations. + debug(""); + debug("When level equals 0, width and height must be a multiple of 4."); + debug("When level is larger than 0, this constraint doesn't apply."); + ctu.testTexImageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { level: 0, width: 4, height: 3, expectation: gl.INVALID_OPERATION, message: "0: 4x3" }, + { level: 0, width: 3, height: 4, expectation: gl.INVALID_OPERATION, message: "0: 3x4" }, + { level: 0, width: 2, height: 2, expectation: gl.INVALID_OPERATION, message: "0: 2x2" }, + { level: 0, width: 4, height: 4, expectation: gl.NO_ERROR, message: "0: 4x4" }, + { level: 1, width: 2, height: 2, expectation: gl.NO_ERROR, message: "1: 2x2" }, + { level: 2, width: 1, height: 1, expectation: gl.NO_ERROR, message: "2: 1x1" }, + ]); + + ctu.testTexSubImageDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, 16, 16, + [ + { xoffset: 0, yoffset: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "height is not a multiple of 4" }, + { xoffset: 0, yoffset: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "width is not a multiple of 4" }, + { xoffset: 1, yoffset: 0, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "xoffset is not a multiple of 4" }, + { xoffset: 0, yoffset: 1, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "yoffset is not a multiple of 4" }, + { xoffset: 12, yoffset: 12, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + ]); + + if (contextVersion >= 2) { + debug(""); + debug("Testing NPOT textures"); + ctu.testTexImageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { level: 0, width: 12, height: 12, expectation: gl.NO_ERROR, message: "0: 12x12 is valid" }, + { level: 1, width: 6, height: 6, expectation: gl.NO_ERROR, message: "1: 6x6, is valid" }, + { level: 2, width: 3, height: 3, expectation: gl.NO_ERROR, message: "2: 3x3, is valid" }, + { level: 3, width: 1, height: 1, expectation: gl.NO_ERROR, message: "3: 1x1, is valid" }, + ]); + + debug(""); + debug("Testing partial updates"); + ctu.testTexSubImageDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, 12, 12, + [ + { xoffset: 0, yoffset: 0, width: 4, height: 3, + expectation: gl.INVALID_OPERATION, message: "height is not a multiple of 4" }, + { xoffset: 0, yoffset: 0, width: 3, height: 4, + expectation: gl.INVALID_OPERATION, message: "width is not a multiple of 4" }, + { xoffset: 1, yoffset: 0, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "xoffset is not a multiple of 4" }, + { xoffset: 0, yoffset: 1, width: 4, height: 4, + expectation: gl.INVALID_OPERATION, message: "yoffset is not a multiple of 4" }, + { xoffset: 8, yoffset: 8, width: 4, height: 4, + expectation: gl.NO_ERROR, message: "is valid" }, + ]); + + debug(""); + debug("Testing immutable NPOT textures"); + ctu.testTexStorageLevelDimensions(gl, ext, validFormats, expectedByteLength, getBlockDimensions, + [ + { width: 12, height: 12, expectation: gl.NO_ERROR, message: "0: 12x12 is valid" }, + { width: 6, height: 6, expectation: gl.NO_ERROR, message: "1: 6x6, is valid" }, + { width: 3, height: 3, expectation: gl.NO_ERROR, message: "2: 3x3, is valid" }, + { width: 1, height: 1, expectation: gl.NO_ERROR, message: "3: 1x1, is valid" }, + ]); + } +} + +function testDXT1_SRGB() { + var tests = [ + { width: 4, + height: 4, + channels: 3, + data: img_4x4_rgba_dxt1, + format: ext.COMPRESSED_SRGB_S3TC_DXT1_EXT, + hasAlpha: false, + }, + { width: 8, + height: 8, + channels: 3, + data: img_8x8_rgba_dxt1, + format: ext.COMPRESSED_SRGB_S3TC_DXT1_EXT, + hasAlpha: false, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rgba_dxt1 + } + ]; + testDXTTextures(tests); +} + +function testDXT1_SRGB_ALPHA() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_dxt1, + format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, + // This is a special case -- the texture is still opaque + // though it's RGBA. + hasAlpha: false, + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_dxt1, + format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, + // This is a special case -- the texture is still opaque + // though it's RGBA. + hasAlpha: false, + } + ]; + testDXTTextures(tests); +} + +function testDXT3_SRGB_ALPHA() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_dxt3, + format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, + hasAlpha: true, + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_dxt3, + format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, + hasAlpha: true, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rgba_dxt3 + } + ]; + testDXTTextures(tests); +} + +function testDXT5_SRGB_ALPHA() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_dxt5, + format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, + hasAlpha: true, + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_dxt5, + format: ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, + hasAlpha: true, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rgba_dxt5 + } + ]; + testDXTTextures(tests); +} + +function testDXTTextures(tests) { + debug("<hr/>"); + for (var ii = 0; ii < tests.length; ++ii) { + testDXTTexture(tests[ii], false); + if (contextVersion >= 2) { + debug("<br/>"); + testDXTTexture(tests[ii], true); + } + } +} + +function uncompressDXTBlockSRGB( + destBuffer, destX, destY, destWidth, src, srcOffset, format) { + // Decoding routines follow D3D11 functional spec wrt + // endpoints unquantization and interpolation. + // Some hardware may produce slightly different values - it's normal. + + function make565(src, offset) { + return src[offset + 0] + (src[offset + 1] << 8); + } + function make8888From565(c) { + // These values exactly match hw decoder when selectors are 0 or 1. + function replicateBits(v, w) { + return (v << (8 - w)) | (v >> (w + w - 8)); + } + return [ + replicateBits((c >> 11) & 0x1F, 5), + replicateBits((c >> 5) & 0x3F, 6), + replicateBits((c >> 0) & 0x1F, 5), + 255 + ]; + } + function mix(mult, c0, c1, div) { + var r = []; + for (var ii = 0; ii < c0.length; ++ii) { + // For green channel (6 bits), this interpolation exactly matches hw decoders + + // For red and blue channels (5 bits), this interpolation exactly + // matches only some hw decoders and stays within acceptable range for others. + r[ii] = Math.floor((c0[ii] * mult + c1[ii]) / div + 0.5); + } + return r; + } + var isDXT1 = format == ext.COMPRESSED_SRGB_S3TC_DXT1_EXT || + format == ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + var colorOffset = srcOffset + (isDXT1 ? 0 : 8); + var color0 = make565(src, colorOffset + 0); + var color1 = make565(src, colorOffset + 2); + var c0gtc1 = color0 > color1 || !isDXT1; + var rgba0 = make8888From565(color0); + var rgba1 = make8888From565(color1); + var colors = [ + rgba0, + rgba1, + c0gtc1 ? mix(2, rgba0, rgba1, 3) : mix(1, rgba0, rgba1, 2), + c0gtc1 ? mix(2, rgba1, rgba0, 3) : [0, 0, 0, 255] + ]; + + // yea I know there is a lot of math in this inner loop. + // so sue me. + for (var yy = 0; yy < 4; ++yy) { + var pixels = src[colorOffset + 4 + yy]; + for (var xx = 0; xx < 4; ++xx) { + var dstOff = ((destY + yy) * destWidth + destX + xx) * 4; + var code = (pixels >> (xx * 2)) & 0x3; + var srcColor = colors[code]; + var alpha; + switch (format) { + case ext.COMPRESSED_SRGB_S3TC_DXT1_EXT: + alpha = 255; + break; + case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + alpha = (code == 3 && !c0gtc1) ? 0 : 255; + break; + case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + { + var alpha0 = src[srcOffset + yy * 2 + (xx >> 1)]; + var alpha1 = (alpha0 >> ((xx % 2) * 4)) & 0xF; + alpha = alpha1 | (alpha1 << 4); + } + break; + case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + { + var alpha0 = src[srcOffset + 0]; + var alpha1 = src[srcOffset + 1]; + var alphaOff = (yy >> 1) * 3 + 2; + var alphaBits = + src[srcOffset + alphaOff + 0] + + src[srcOffset + alphaOff + 1] * 256 + + src[srcOffset + alphaOff + 2] * 65536; + var alphaShift = (yy % 2) * 12 + xx * 3; + var alphaCode = (alphaBits >> alphaShift) & 0x7; + if (alpha0 > alpha1) { + switch (alphaCode) { + case 0: + alpha = alpha0; + break; + case 1: + alpha = alpha1; + break; + default: + alpha = Math.floor(((8 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 7.0 + 0.5); + break; + } + } else { + switch (alphaCode) { + case 0: + alpha = alpha0; + break; + case 1: + alpha = alpha1; + break; + case 6: + alpha = 0; + break; + case 7: + alpha = 255; + break; + default: + alpha = Math.floor(((6 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 5.0 + 0.5); + break; + } + } + } + break; + default: + throw "bad format"; + } + destBuffer[dstOff + 0] = sRGBChannelToLinear(srcColor[0]); + destBuffer[dstOff + 1] = sRGBChannelToLinear(srcColor[1]); + destBuffer[dstOff + 2] = sRGBChannelToLinear(srcColor[2]); + destBuffer[dstOff + 3] = alpha; + } + } +} + +function getBlockSize(format) { + var isDXT1 = format == ext.COMPRESSED_SRGB_S3TC_DXT1_EXT || + format == ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + return isDXT1 ? 8 : 16; +} + +function uncompressDXTSRGB(width, height, data, format) { + if (width % 4 || height % 4) throw "bad width or height"; + + var dest = new Uint8Array(width * height * 4); + var blocksAcross = width / 4; + var blocksDown = height / 4; + var blockSize = getBlockSize(format); + for (var yy = 0; yy < blocksDown; ++yy) { + for (var xx = 0; xx < blocksAcross; ++xx) { + uncompressDXTBlockSRGB( + dest, xx * 4, yy * 4, width, data, + (yy * blocksAcross + xx) * blockSize, format); + } + } + return dest; +} + +function uncompressDXTIntoSubRegionSRGB(width, height, subX0, subY0, subWidth, subHeight, data, format) +{ + if (width % 4 || height % 4 || subX0 % 4 || subY0 % 4 || subWidth % 4 || subHeight % 4) + throw "bad dimension"; + + var dest = new Uint8Array(width * height * 4); + // Zero-filled DXT1 texture represents [0, 0, 0, 255] + if (format == ext.COMPRESSED_SRGB_S3TC_DXT1_EXT || format == ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) { + for (var i = 3; i < dest.length; i += 4) dest[i] = 255; + } + var blocksAcross = subWidth / 4; + var blocksDown = subHeight / 4; + var blockSize = getBlockSize(format); + for (var yy = 0; yy < blocksDown; ++yy) { + for (var xx = 0; xx < blocksAcross; ++xx) { + uncompressDXTBlockSRGB( + dest, subX0 + xx * 4, subY0 + yy * 4, width, data, + (yy * blocksAcross + xx) * blockSize, format); + } + } + return dest; +} + +function copyRect(data, srcX, srcY, dstX, dstY, width, height, stride) { + var bytesPerLine = width * 4; + var srcOffset = srcX * 4 + srcY * stride; + var dstOffset = dstX * 4 + dstY * stride; + for (; height > 0; --height) { + for (var ii = 0; ii < bytesPerLine; ++ii) { + data[dstOffset + ii] = data[srcOffset + ii]; + } + srcOffset += stride; + dstOffset += stride; + } +} + +function testDXTTexture(test, useTexStorage) { + var data = new Uint8Array(test.data); + var width = test.width; + var height = test.height; + var format = test.format; + + var uncompressedData = uncompressDXTSRGB(width, height, data, format); + + canvas.width = width; + canvas.height = height; + gl.viewport(0, 0, width, height); + debug("testing " + ctu.formatToString(ext, format) + " " + width + "x" + height + + (useTexStorage ? " via texStorage2D" : " via compressedTexImage2D")); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + if (useTexStorage) { + if (test.subData) { + var uncompressedDataSub = uncompressDXTIntoSubRegionSRGB( + width, height, test.subX0, test.subY0, test.subWidth, test.subHeight, test.subData, format); + var tex1 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + + gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed texture via texStorage2D"); + gl.compressedTexSubImage2D( + gl.TEXTURE_2D, 0, test.subX0, test.subY0, test.subWidth, test.subHeight, format, test.subData); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture data via compressedTexSubImage2D"); + + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 1"); + compareRect(width, height, test.channels, uncompressedDataSub, "NEAREST"); + + // Clean up and recover + gl.deleteTexture(tex1); + gl.bindTexture(gl.TEXTURE_2D, tex); + } + + gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "allocating compressed texture via texStorage2D"); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + var clearColor = (test.hasAlpha ? [0, 0, 0, 0] : [0, 0, 0, 255]); + wtu.checkCanvas(gl, clearColor, "texture should be initialized to black"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture data via compressedTexSubImage2D"); + } else { + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); + } + gl.generateMipmap(gl.TEXTURE_2D); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after clearing generateMipmap error"); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 1"); + compareRect(width, height, test.channels, uncompressedData, "NEAREST"); + // Test again with linear filtering. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad 2"); + compareRect(width, height, test.channels, uncompressedData, "LINEAR"); + + if (!useTexStorage) { + // It's not allowed to redefine textures defined via texStorage2D. + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border"); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + + if (width == 4) { + // The width/height of the implied base level must be a multiple of the block size. + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions for level > 0"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 2, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); + } + if (height == 4) { + // The width/height of the implied base level must be a multiple of the block size. + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions for level > 0"); + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 2, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0"); + } + } + + // pick a wrong format that uses the same amount of data. + var wrongFormat; + switch (format) { + case ext.COMPRESSED_SRGB_S3TC_DXT1_EXT: + wrongFormat = ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + break; + case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + wrongFormat = ext.COMPRESSED_SRGB_S3TC_DXT1_EXT; + break; + case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT: + wrongFormat = ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + break; + case ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + wrongFormat = ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT; + break; + } + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, wrongFormat, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "format does not match"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 4, 0, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "dimension out of range"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 4, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "dimension out of range"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width + 4, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height + 4, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 4, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 4, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions"); + + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 1, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 2, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 1, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 2, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); + + var subData = new Uint8Array(data.buffer, 0, getBlockSize(format)); + + if (width == 8 && height == 8) { + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 1, 0, 4, 4, format, subData); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 1, 4, 4, format, subData); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid offset"); + } + + var stride = width * 4; + for (var yoff = 0; yoff < height; yoff += 4) { + for (var xoff = 0; xoff < width; xoff += 4) { + copyRect(uncompressedData, 0, 0, xoff, yoff, 4, 4, stride); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, xoff, yoff, 4, 4, format, subData); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); + // First test NEAREST filtering. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + wtu.clearAndDrawUnitQuad(gl); + compareRect(width, height, test.channels, uncompressedData, "NEAREST"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + // Next test LINEAR filtering. + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + compareRect(width, height, test.channels, uncompressedData, "LINEAR"); + } + } +} + +function testDXT5_SRGB_ALPHA_PBO() { + debug(""); + debug("testing PBO uploads"); + var width = 8; + var height = 8; + var channels = 4; + var data = img_8x8_rgba_dxt5; + var format = ext.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + var uncompressedData = uncompressDXTSRGB(width, height, data, format); + + var tex = gl.createTexture(); + + // First, PBO size = image size + var pbo1 = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo1); + gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data, gl.STATIC_DRAW); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a PBO"); + + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texStorage2D(gl.TEXTURE_2D, 1, format, width, height); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data.length, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a texture from a PBO"); + + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + compareRect(width, height, channels, uncompressedData, "NEAREST"); + + // Clear the texture before the next test + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, new Uint8Array(data.length)); + + // Second, image is just a subrange of the PBO + var pbo2 = gl.createBuffer(); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, pbo2); + gl.bufferData(gl.PIXEL_UNPACK_BUFFER, data.length*3, gl.STATIC_DRAW); + gl.bufferSubData(gl.PIXEL_UNPACK_BUFFER, data.length, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a PBO subrange"); + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data.length, data.length); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading a texture from a PBO subrange"); + gl.bindBuffer(gl.PIXEL_UNPACK_BUFFER, null); + wtu.clearAndDrawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "drawing unit quad"); + compareRect(width, height, channels, uncompressedData, "NEAREST"); +} + +// See EXT_texture_sRGB, Section 3.8.x, sRGB Texture Color Conversion. +function sRGBChannelToLinear(value) { + value = value / 255; + if (value <= 0.04045) { + value = value / 12.92; + } else { + value = Math.pow((value + 0.055) / 1.055, 2.4); + } + return Math.trunc(value * 255 + 0.5); +} + +function compareRect(width, height, channels, expectedData, filteringMode) { + var actual = new Uint8Array(width * height * 4); + gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, actual); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading back pixels"); + + var div = document.createElement("div"); + div.className = "testimages"; + ctu.insertCaptionedImg(div, "expected", ctu.makeScaledImage(width, height, width, expectedData, true)); + ctu.insertCaptionedImg(div, "actual", ctu.makeScaledImage(width, height, width, actual, true)); + div.appendChild(document.createElement('br')); + document.getElementById("console").appendChild(div); + + var failed = false; + for (var yy = 0; yy < height; ++yy) { + for (var xx = 0; xx < width; ++xx) { + var offset = (yy * width + xx) * 4; + var expected = expectedData.slice(offset, offset + 4); + // Compare RGB values + for (var jj = 0; jj < 3; ++jj) { + // Acceptable interpolation error depends on endpoints: + // 1.0 / 255.0 + 0.03 * max(abs(endpoint0 - endpoint1), abs(endpoint0_p - endpoint1_p)) + // For simplicity, assume the worst case (e0 is 0.0, e1 is 1.0). After conversion to unorm8, it is 9. + if (Math.abs(actual[offset + jj] - expected[jj]) > 9) { + var was = actual[offset + 0].toString(); + for (var j = 1; j < 3; ++j) { + was += "," + actual[offset + j]; + } + failed = true; + testFailed('RGB at (' + xx + ', ' + yy + + ') expected: ' + expected + ' ± 9 was ' + was); + } + } + + if (channels == 3) { + // BC1 RGB is allowed to be mapped to BC1 RGBA. + // In such a case, 3-color mode black value can be transparent: + // [0, 0, 0, 0] instead of [0, 0, 0, 255]. + + if (actual[offset + 3] != expected[3]) { + // Got non-opaque value for opaque format + + // Check RGB values. Notice, that the condition here + // is more permissive than needed since we don't have + // compressed data at this point. + if (actual[offset] == 0 && + actual[offset + 1] == 0 && + actual[offset + 2] == 0 && + actual[offset + 3] == 0) { + debug("<b>DXT1 SRGB is mapped to DXT1 SRGB ALPHA</b>"); + } else { + failed = true; + testFailed('Alpha at (' + xx + ', ' + yy + + ') expected: ' + expected[3] + ' was ' + actual[offset + 3]); + } + } + } else { + // Compare Alpha values + // Acceptable interpolation error depends on endpoints: + // 1.0 / 65535.0 + 0.03 * max(abs(endpoint0 - endpoint1), abs(endpoint0_p - endpoint1_p)) + // For simplicity, assume the worst case (e0 is 0.0, e1 is 1.0). After conversion to unorm8, it is 8. + if (Math.abs(actual[offset + 3] - expected[3]) > 8) { + var was = actual[offset + 3].toString(); + failed = true; + testFailed('Alpha at (' + xx + ', ' + yy + + ') expected: ' + expected + ' ± 8 was ' + was); + } + } + } + } + if (!failed) { + testPassed("texture rendered correctly with " + filteringMode + " filtering"); + } +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-size-limit.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-size-limit.html new file mode 100644 index 0000000000..ab50e64d07 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-size-limit.html @@ -0,0 +1,35 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL compressed texture size limit conformance test</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/webgl-compressed-texture-size-limit.js"></script> +</head> +<body> +<canvas id="example" width="32" height="32" style="width: 40px; height: 40px;"></canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +enableJSTestPreVerboseLogging(); +description("Checks size limit of the webgl compressed textures") + +// ArrayBuffers can be at most 4GB (minus 1 byte), but any allocations larger than 1 GB are unreliable in practice. So limit allocations to 1 GB. +// Textures that are wide in just one dimension can still be used to test max TEXTURE_2D size limit even if we can't allocate space for huge square textures. +// Use a fairly conservative limit for positive test cube map size so OOM is avoided. +runCompressedTextureSizeLimitTest(Math.pow(2, 30), 2048); + +var successfullyParsed = true; +</script> +</body> +</html> + diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-renderer-info.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-renderer-info.html new file mode 100644 index 0000000000..7901225db7 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-renderer-info.html @@ -0,0 +1,104 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WebGL_debug_renderer_info Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 1px; height: 1px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing standard derivatives --> + +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_debug_renderer_info extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("canvas"); +var ext = null; +var vao = null; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("WEBGL_debug_renderer_info"); + if (!ext) { + testPassed("No WEBGL_debug_renderer_info support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled WEBGL_debug_renderer_info extension"); + + runSupportedTest(true); + runTestEnabled(); + } +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("WEBGL_debug_renderer_info") >= 0) { + if (extensionEnabled) { + testPassed("WEBGL_debug_renderer_info listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_debug_renderer_info listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_debug_renderer_info not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_debug_renderer_info not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runTestDisabled() { + debug("Testing enums with extension disabled"); + + // Use the constants directly as we don't have the extension + + var UNMASKED_VENDOR_WEBGL = 0x9245; + gl.getParameter(UNMASKED_VENDOR_WEBGL); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "UNMASKED_VENDOR_WEBGL should not be queryable if extension is disabled"); + + var UNMASKED_RENDERER_WEBGL = 0x9246; + gl.getParameter(UNMASKED_RENDERER_WEBGL); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "UNMASKED_RENDERER_WEBGL should not be queryable if extension is disabled"); +} + +function runTestEnabled() { + debug("Testing enums with extension enabled"); + + shouldBe("ext.UNMASKED_VENDOR_WEBGL", "0x9245"); + gl.getParameter(ext.UNMASKED_VENDOR_WEBGL); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "UNMASKED_VENDOR_WEBGL query should succeed if extension is enable"); + + shouldBe("ext.UNMASKED_RENDERER_WEBGL", "0x9246"); + gl.getParameter(ext.UNMASKED_RENDERER_WEBGL); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "UNMASKED_RENDERER_WEBGL query should succeed if extension is enable"); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-shaders.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-shaders.html new file mode 100644 index 0000000000..9a95736a86 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-shaders.html @@ -0,0 +1,144 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WebGL_debug_shaders Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" style="width: 1px; height: 1px;"> </canvas> +<div id="console"></div> +<!-- Shaders for testing standard derivatives --> + +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_debug_shaders extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("canvas"); +var ext = null; +var shader = null; +var program = null; +var info = null; +var translatedSource; +var newTranslatedSource; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("WEBGL_debug_shaders"); + if (!ext) { + testPassed("No WEBGL_debug_shaders support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled WEBGL_debug_shaders extension"); + + runSupportedTest(true); + runTestEnabled(); + } +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("WEBGL_debug_shaders") >= 0) { + if (extensionEnabled) { + testPassed("WEBGL_debug_shaders listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_debug_shaders listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_debug_shaders not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_debug_shaders not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runTestEnabled() { + debug("Testing function with extension enabled"); + + var shaderInfos = [ + { + source: "void main() { gl_Position = vec4(1.0, 0.0, 0.0, 1.0); }", + type: gl.VERTEX_SHADER + }, + { + source: "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }", + type: gl.FRAGMENT_SHADER + } + ]; + + // Do this twice to test for caching issues. + for (var jj = 0; jj < 2; ++jj) { + debug("pass:" + (jj + 1)); + program = gl.createProgram(); + for (var ii = 0; ii < shaderInfos.length; ++ii) { + info = shaderInfos[ii]; + + shader = gl.createShader(info.type); + + // if no source has been defined or compileShader() has not been called, + // getTranslatedShaderSource() should return an empty string. + shouldBe("ext.getTranslatedShaderSource(shader)", '""'); + gl.shaderSource(shader, info.source); + shouldBe("ext.getTranslatedShaderSource(shader)", '""'); + gl.compileShader(shader); + shouldBeTrue("gl.getShaderParameter(shader, gl.COMPILE_STATUS)"); + translatedSource = ext.getTranslatedShaderSource(shader); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No gl error should occur"); + if (translatedSource && translatedSource.length > 0) { + testPassed("Successfully called getTranslatedShaderSource()"); + } else { + testFailed("Calling getTranslatedShaderSource() failed"); + } + gl.attachShader(program, shader); + } + gl.linkProgram(program); + shouldBeTrue("gl.getProgramParameter(program, gl.LINK_STATUS)"); + } + + // Test changing the source. Make sure we get the correct source each time. + debug("test changing source"); + shader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(shader, "void main() { gl_FragColor = vec4(gl_FragCoord.x, 0.0, 0.0, 1.0); }"); + gl.compileShader(shader); + shouldBeTrue("gl.getShaderParameter(shader, gl.COMPILE_STATUS)"); + shouldThrow("ext.getTranslatedShaderSource(null)"); + translatedSource = ext.getTranslatedShaderSource(shader); + shouldBeTrue('translatedSource && translatedSource.indexOf("gl_FragCoord") >= 0'); + // change the source but don't compile. + gl.shaderSource(shader, "void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }"); + // the source should NOT change. It should be the same as the old source. + newTranslatedSource = ext.getTranslatedShaderSource(shader); + shouldBe('newTranslatedSource', 'translatedSource'); + // now compile. + gl.compileShader(shader); + shouldBeTrue("gl.getShaderParameter(shader, gl.COMPILE_STATUS)"); + // the source should have change. + newTranslatedSource = ext.getTranslatedShaderSource(shader); + shouldNotBe('newTranslatedSource', 'translatedSource'); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-depth-texture.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-depth-texture.html new file mode 100644 index 0000000000..f2318cc1d3 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-depth-texture.html @@ -0,0 +1,377 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<title>WebGL WEBGL_depth_texture Conformance Tests</title> +</head> +<body> +<script id="vshader" type="x-shader/x-vertex"> +attribute vec4 a_position; +void main() +{ + gl_Position = a_position; +} +</script> + +<script id="fshader" type="x-shader/x-fragment"> +precision mediump float; +uniform sampler2D u_texture; +uniform vec2 u_resolution; +void main() +{ + vec2 texcoord = (gl_FragCoord.xy - vec2(0.5)) / (u_resolution - vec2(1.0)); + gl_FragColor = texture2D(u_texture, texcoord); +} +</script> +<div id="description"></div> +<div id="console"></div> +<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_depth_texture extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas, {antialias: false}); +var program = wtu.setupTexturedQuad(gl); +var ext = null; +var vao = null; +var tex; +var name; +var supportedFormats; +var canvas2; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_depth_texture"); + if (!ext) { + testPassed("No WEBGL_depth_texture support -- this is legal"); + runSupportedTest(false); + } else { + testPassed("Successfully enabled WEBGL_depth_texture extension"); + + runSupportedTest(true); + runTestExtension(true); + runTestExtension(false); + } +} + +function runSupportedTest(extensionEnabled) { + var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_depth_texture"); + if (name !== undefined) { + if (extensionEnabled) { + testPassed("WEBGL_depth_texture listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_depth_texture listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_depth_texture not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_depth_texture not listed as supported and getExtension failed -- this is legal"); + } + } +} + + +function runTestDisabled() { + debug("Testing binding enum with extension disabled"); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_VALUE], + 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, 1, 1, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null)'); + wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_VALUE], + 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, 1, 1, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_INT, null)'); +} + + +function dumpIt(gl, res, msg) { + return; // comment out to debug + debug(msg); + var actualPixels = new Uint8Array(res * res * 4); + gl.readPixels(0, 0, res, res, gl.RGBA, gl.UNSIGNED_BYTE, actualPixels); + + for (var yy = 0; yy < res; ++yy) { + var strs = []; + for (var xx = 0; xx < res; ++xx) { + var actual = (yy * res + xx) * 4; + strs.push("(" + actualPixels[actual] + "," + actualPixels[actual+1] + "," + actualPixels[actual + 2] + "," + actualPixels[actual + 3] + ")"); + } + debug(strs.join(" ")); + } +} +function runTestExtension(unpackFlipY) { + debug("Testing WEBGL_depth_texture. UNPACK_FLIP_Y_WEBGL: " + unpackFlipY); + + const res = 2; + const destRes = 4; + + // make canvas for testing. + canvas2 = document.createElement("canvas"); + canvas2.width = res; + canvas2.height = res; + var ctx = canvas2.getContext("2d"); + ctx.fillStyle = "blue"; + ctx.fillRect(0, 0, canvas2.width, canvas2.height); + + var program = wtu.setupProgram(gl, ['vshader', 'fshader'], ['a_position']); + gl.useProgram(program); + gl.uniform2f(gl.getUniformLocation(program, "u_resolution"), destRes, destRes); + + var buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData( + gl.ARRAY_BUFFER, + new Float32Array( + [ 1, 1, 1, + -1, 1, 0, + -1, -1, -1, + 1, 1, 1, + -1, -1, -1, + 1, -1, 0, + ]), + gl.STATIC_DRAW); + gl.enableVertexAttribArray(0); + gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0); + + gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, unpackFlipY); + + var types = [ + {obj: 'gl', attachment: 'DEPTH_ATTACHMENT', format: 'DEPTH_COMPONENT', type: 'UNSIGNED_SHORT', data: 'new Uint16Array(1)', depthBits: "16"}, + {obj: 'gl', attachment: 'DEPTH_ATTACHMENT', format: 'DEPTH_COMPONENT', type: 'UNSIGNED_INT', data: 'new Uint32Array(1)', depthBits: "16"}, + {obj: 'ext', attachment: 'DEPTH_STENCIL_ATTACHMENT', format: 'DEPTH_STENCIL', type: 'UNSIGNED_INT_24_8_WEBGL', data: 'new Uint32Array(1)', depthBits: "24", stencilBits: "8"} + ]; + + for (var ii = 0; ii < types.length; ++ii) { + var typeInfo = types[ii]; + var type = typeInfo.type; + var typeStr = typeInfo.obj + '.' + type; + + debug(""); + debug("testing: " + type); + + // check that cubemaps are not allowed. + var cubeTex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, cubeTex); + var targets = [ + 'TEXTURE_CUBE_MAP_POSITIVE_X', + 'TEXTURE_CUBE_MAP_NEGATIVE_X', + 'TEXTURE_CUBE_MAP_POSITIVE_Y', + 'TEXTURE_CUBE_MAP_NEGATIVE_Y', + 'TEXTURE_CUBE_MAP_POSITIVE_Z', + 'TEXTURE_CUBE_MAP_NEGATIVE_Z' + ]; + for (var tt = 0; tt < targets.length; ++tt) { + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texImage2D(gl.' + targets[ii] + ', 0, gl.' + typeInfo.format + ', 1, 1, 0, gl.' + typeInfo.format + ', ' + typeStr + ', null)'); + } + + // The WebGL_depth_texture extension supports both NEAREST and + // LINEAR filtering for depth textures, even though LINEAR + // doesn't have much meaning, and isn't supported in WebGL + // 2.0. Still, test both. + var filterModes = [ + 'LINEAR', + 'NEAREST' + ]; + + for (var jj = 0; jj < filterModes.length; ++jj) { + debug(''); + debug('testing ' + filterModes[jj] + ' filtering'); + var filterMode = gl[filterModes[jj]]; + + // check 2d textures. + tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterMode); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filterMode); + + // test level > 0 + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texImage2D(gl.TEXTURE_2D, 1, gl.' + typeInfo.format + ', 1, 1, 0, gl.' + typeInfo.format + ', ' + typeStr + ', null)'); + + // test with data + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', 1, 1, 0, gl.' + typeInfo.format + ', ' + typeStr + ', ' + typeInfo.data + ')'); + + // test with canvas + wtu.shouldGenerateGLError(gl, [gl.INVALID_VALUE, gl.INVALID_ENUM, gl.INVALID_OPERATION], 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', gl.' + typeInfo.format + ', ' + typeStr + ', canvas2)'); + + // test copyTexImage2D + wtu.shouldGenerateGLError(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], 'gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', 0, 0, 1, 1, 0)'); + + // test real thing + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.texImage2D(gl.TEXTURE_2D, 0, gl.' + typeInfo.format + ', ' + res + ', ' + res + ', 0, gl.' + typeInfo.format + ', ' + typeStr + ', null)'); + + // test texSubImage2D + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 1, 1, gl.' + typeInfo.format + ', ' + typeStr + ', ' + typeInfo.data + ')'); + + // test copyTexSubImage2D + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1)'); + + // test generateMipmap + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, 'gl.generateMipmap(gl.TEXTURE_2D)'); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl[typeInfo.attachment], gl.TEXTURE_2D, tex, 0); + + // Ensure DEPTH_BITS returns >= 16 bits for UNSIGNED_SHORT and UNSIGNED_INT, >= 24 UNSIGNED_INT_24_8_WEBGL. + // If there is stencil, ensure STENCIL_BITS reports >= 8 for UNSIGNED_INT_24_8_WEBGL. + shouldBeGreaterThanOrEqual('gl.getParameter(gl.DEPTH_BITS)', typeInfo.depthBits); + if (typeInfo.stencilBits === undefined) { + shouldBe('gl.getParameter(gl.STENCIL_BITS)', '0'); + } else { + shouldBeGreaterThanOrEqual('gl.getParameter(gl.STENCIL_BITS)', typeInfo.stencilBits); + } + + // TODO: remove this check if the spec is updated to require these combinations to work. + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) + { + // try adding a color buffer. + var colorTex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, colorTex); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, res, res, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, colorTex, 0); + } + + shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + + // use the default texture to render with while we return to the depth texture. + gl.bindTexture(gl.TEXTURE_2D, null); + + /* Setup 2x2 depth texture: + * 1 0.6 0.8 + * | + * 0 0.2 0.4 + * 0---1 + */ + const d00 = 0.2; + const d01 = 0.4; + const d10 = 0.6; + const d11 = 0.8; + + gl.enable(gl.SCISSOR_TEST); + + gl.scissor(0, 0, 1, 1); + gl.clearDepth(d00); + gl.clear(gl.DEPTH_BUFFER_BIT); + + gl.scissor(1, 0, 1, 1); + gl.clearDepth(d10); + gl.clear(gl.DEPTH_BUFFER_BIT); + + gl.scissor(0, 1, 1, 1); + gl.clearDepth(d01); + gl.clear(gl.DEPTH_BUFFER_BIT); + + gl.scissor(1, 1, 1, 1); + gl.clearDepth(d11); + gl.clear(gl.DEPTH_BUFFER_BIT); + + gl.disable(gl.SCISSOR_TEST); + + // render the depth texture. + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.canvas.width = destRes; + gl.canvas.height = destRes; + gl.viewport(0, 0, destRes, destRes); + gl.bindTexture(gl.TEXTURE_2D, tex); + + gl.disable(gl.DITHER); + gl.enable(gl.DEPTH_TEST); + gl.clearColor(1, 0, 0, 1); + gl.clearDepth(1.0); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + dumpIt(gl, res, "--depth--"); + + var actualPixels = new Uint8Array(destRes * destRes * 4); + gl.readPixels(0, 0, destRes, destRes, gl.RGBA, gl.UNSIGNED_BYTE, actualPixels); + + const eps = 0.002; + + let expectedMin; + let expectedMax; + if (filterMode == gl.NEAREST) { + expectedMin = [ + d00, d00, d10, d10, + d00, d00, d10, d10, + d01, d01, d11, d11, + d01, d01, d11, d11 + ]; + expectedMax = expectedMin.slice(); + + expectedMin = expectedMin.map(x => x - eps); + expectedMax = expectedMax.map(x => x + eps); + } else { + expectedMin = [ + d00-eps, d00, d00, d10-eps, + d00, d00, d00, d10, + d00, d00, d00, d10, + d01-eps, d01, d01, d11-eps, + ]; + expectedMax = [ + d00+eps, d10, d10, d10+eps, + d01, d11, d11, d11, + d01, d11, d11, d11, + d01+eps, d11, d11, d11+eps, + ]; + } + + for (var yy = 0; yy < destRes; ++yy) { + for (var xx = 0; xx < destRes; ++xx) { + const t = xx + destRes*yy; + const was = actualPixels[4*t] / 255.0; // 4bpp + const eMin = expectedMin[t]; + const eMax = expectedMax[t]; + let func = testPassed; + const text = `At ${xx},${yy}, expected within [${eMin},${eMax}], was ${was.toFixed(3)}` + if (was <= eMin || was >= eMax) { + func = testFailed; + } + func(text); + } + } + + // check limitations + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl[typeInfo.attachment], gl.TEXTURE_2D, null, 0); + var badAttachment = typeInfo.attachment == 'DEPTH_ATTACHMENT' ? 'DEPTH_STENCIL_ATTACHMENT' : 'DEPTH_ATTACHMENT'; + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, 'gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.' + badAttachment + ', gl.TEXTURE_2D, tex, 0)'); + shouldNotBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE'); + wtu.shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, 'gl.clear(gl.DEPTH_BUFFER_BIT)'); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + shouldBe('gl.getError()', 'gl.NO_ERROR'); + } + } +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-broadcast-return.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-broadcast-return.html new file mode 100644 index 0000000000..d773ae8621 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-broadcast-return.html @@ -0,0 +1,138 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WEBGL_draw_buffers Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/webgl-draw-buffers-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="64" height="64"> </canvas> +<div id="console"></div> +<script id="fshaderRedWithReturn" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +uniform float u_zero; +void main() { + gl_FragColor = vec4(1,0,0,1); + if (u_zero < 1.0) { + return; + } + gl_FragColor = vec4(0,0,1,1); +} +</script> +<script id="fshaderWithDiscard" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +uniform float u_zero; +void main() { + gl_FragColor = vec4(1,0,0,1); + if (u_zero < 1.0) { + discard; + } + gl_FragColor = vec4(0,0,1,1); +} +</script> +<script> +"use strict"; +description("This test verifies gl_FragColor being broadcasted when using WEBGL_draw_buffers extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var drawBuffersUtils; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("WEBGL_draw_buffers"); + if (!ext) { + testPassed("No WEBGL_draw_buffers support -- this is legal"); + } else { + testPassed("Successfully enabled WEBGL_draw_buffers extension"); + drawBuffersUtils = WebGLDrawBuffersUtils(gl, ext); + runDrawTests(); + } +} + +function runDrawTests() { + debug(""); + var fb = gl.createFramebuffer(); + + var maxUsable = drawBuffersUtils.getMaxUsableColorAttachments(); + var bufs = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + + var width = 64; + var height = 64; + var attachments = []; + for (var ii = 0; ii < maxUsable; ++ii) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + attachments.push({ + texture: tex + }); + } + + debug("test that gl_FragColor broadcasts if extension is enabled in fragment shader and fragment shader main returns in the middle"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + var redProgramWithReturn = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderRedWithReturn"], ["vPosition"], undefined, true); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(redProgramWithReturn); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after draw"); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + debug("test that none of the attachments are written in case the fragment shader discards"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + var programWithDiscard = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderWithDiscard"], ["vPosition"], undefined, true); + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(programWithDiscard); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "after draw"); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 0, 0, 0]); + + gl.bindTexture(gl.TEXTURE_2D, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.deleteFramebuffer(fb); + attachments.forEach(function(attachment) { + gl.deleteTexture(attachment.texture); + }); + gl.deleteProgram(redProgramWithReturn); + gl.deleteProgram(programWithDiscard); +} + +var successfullyParsed = true; + +</script> +<script src="../../js/js-test-post.js"></script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-framebuffer-unsupported.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-framebuffer-unsupported.html new file mode 100644 index 0000000000..5866a3c38b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-framebuffer-unsupported.html @@ -0,0 +1,126 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WEBGL_draw_buffers FRAMEBUFFER_UNSUPPORTED Test</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<div id="console"></div> +<canvas id="canvas" width="2" height="2"> </canvas> + +<script> +"use strict"; +var wtu = WebGLTestUtils; +var gl; +var canvas = document.getElementById("canvas"); +var fb1 = null; +var fb2 = null; + +function checkFramebuffer(expected) { + var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (expected.indexOf(actual) < 0) { + var msg = "checkFramebufferStatus expects ["; + for (var index = 0; index < expected.length; ++index) { + msg += wtu.glEnumToString(gl, expected[index]); + if (index + 1 < expected.length) + msg += ", "; + } + msg += "], was " + wtu.glEnumToString(gl, actual); + testFailed(msg); + } else { + var msg = "checkFramebufferStatus got " + wtu.glEnumToString(gl, actual) + + " as expected"; + testPassed(msg); + } +} + +function testImageAttachedTwoPoints() { + debug(""); + debug("Checking an image is attached to more than one color attachment in a framebuffer."); + + var tex1 = gl.createTexture(); + var tex2 = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex1); + gl.texImage2D(gl.TEXTURE_2D, + 0, // level + gl.RGBA, // internalFormat + 1, // width + 1, // height + 0, // border + gl.RGBA, // format + gl.UNSIGNED_BYTE, // type + new Uint8Array(4)); // data + gl.bindTexture(gl.TEXTURE_2D, tex2); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Texture creation should succeed."); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb1); + gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, tex1, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, tex2, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT2_WEBGL, gl.TEXTURE_2D, tex1, 0); + checkFramebuffer([gl.FRAMEBUFFER_UNSUPPORTED]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + var texCube = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_CUBE_MAP, texCube); + for (var target = gl.TEXTURE_CUBE_MAP_POSITIVE_X; target < gl.TEXTURE_CUBE_MAP_POSITIVE_X + 6; target++) { + gl.texImage2D(target, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4)); + } + gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_CUBE_MAP_POSITIVE_X, texCube, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, texCube, 0); + checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, ext.COLOR_ATTACHMENT2_WEBGL, gl.TEXTURE_CUBE_MAP_POSITIVE_X, texCube, 0); + checkFramebuffer([gl.FRAMEBUFFER_UNSUPPORTED]); + + // Clean up + gl.deleteTexture(tex1); + gl.deleteTexture(tex2); + gl.deleteTexture(texCube); +} + +description("This tests FRAMEBUFFER_UNSUPPORTED."); + +shouldBeNonNull("gl = wtu.create3DContext(undefined, undefined, 1)"); +fb1 = gl.createFramebuffer(); +fb2 = gl.createFramebuffer(); + +var ext = gl.getExtension("WEBGL_draw_buffers"); +if (!ext) { + testPassed("No WEBGL_draw_buffers support -- this is legal"); +} else { + var bufs = [ext.COLOR_ATTACHMENT0_WEBGL, ext.COLOR_ATTACHMENT1_WEBGL, ext.COLOR_ATTACHMENT2_WEBGL]; + gl.bindFramebuffer(gl.FRAMEBUFFER, fb1); + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL successfully"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL successfully"); + + testPassed("Successfully enabled WEBGL_draw_buffers extension"); + testImageAttachedTwoPoints(); + + gl.deleteFramebuffer(fb1); + gl.deleteFramebuffer(fb2); +} + +debug(""); +var successfullyParsed = true; +</script> +<script src="../../js/js-test-post.js"></script> + +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-max-draw-buffers.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-max-draw-buffers.html new file mode 100644 index 0000000000..8181a6a587 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-max-draw-buffers.html @@ -0,0 +1,118 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WEBGL_draw_buffers gl_FragData[gl_MaxDrawBuffers] Conformance Test</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="64" height="64"> </canvas> +<div id="console"></div> +<script id="vshader" type="x-shader/x-vertex"> +attribute vec4 a_position; +void main() { + gl_Position = a_position; +} +</script> +<script id="fshader" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +void main() { + gl_FragData[gl_MaxDrawBuffers] = vec4(0.0); +} +</script> +<script id="fshaderConstantIndex" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +void main() { + gl_FragData[$(gl_MaxDrawBuffers)] = vec4(0.0); +} +</script> +<script id="fshaderTestMaxDrawBuffersValue" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +void main() { + gl_FragColor = ($(gl_MaxDrawBuffers) == gl_MaxDrawBuffers) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); +} +</script> +<script> +"use strict"; +description("This test verifies that compiling the same shader using GL_EXT_draw_buffers twice will have similar results on both rounds."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var maxDrawBuffers; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + ext = gl.getExtension("WEBGL_draw_buffers"); + if (!ext) { + testPassed("No WEBGL_draw_buffers support -- this is legal"); + finishTest(); + } else { + testPassed("Successfully enabled WEBGL_draw_buffers extension"); + maxDrawBuffers = gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL); + runShadersTest(); + finishTest(); + } +} + +function testValueOfMaxDrawBuffers() { + debug("Test the value of gl_MaxDrawBuffers in a shader"); + var fshader = wtu.replaceParams(wtu.getScript("fshaderTestMaxDrawBuffersValue"), {"gl_MaxDrawBuffers": maxDrawBuffers}); + var program = wtu.setupProgram(gl, ["vshader", fshader], ["a_position"], undefined, true); + expectTrue(program != null, "Test program should compile"); + wtu.setupUnitQuad(gl); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green to indicate that gl_MaxDrawBuffers had the right value"); + gl.deleteProgram(program); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runSingleTest(shaders, indexMsg) { + var program = wtu.setupProgram(gl, shaders, ["a_position"], undefined, true); + var programLinkedSuccessfully = (program != null); + expectTrue(!programLinkedSuccessfully, "Program where gl_FragData is indexed by " + indexMsg + " should fail compilation."); + gl.deleteProgram(program); +} + +function runShadersTest() { + debug("MAX_DRAW_BUFFERS_WEBGL is: " + maxDrawBuffers); + + // For reference, use a constant out-of-range parameter to test: + debug("Test indexing gl_FragData with value of MAX_DRAW_BUFFERS_WEBGL"); + var fshader = wtu.replaceParams(wtu.getScript("fshaderConstantIndex"), {"gl_MaxDrawBuffers": maxDrawBuffers}); + runSingleTest(["vshader", fshader], maxDrawBuffers + " (value of MAX_DRAW_BUFFERS_WEBGL)"); + + debug(""); + + debug("Test indexing gl_FragData with gl_MaxDrawBuffers"); + debug("Repeat this test twice as that has revealed a bug."); + for (var i = 0; i < 2; ++i) { + runSingleTest(["vshader", "fshader"], "gl_MaxDrawBuffers"); + } + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + debug(""); + + testValueOfMaxDrawBuffers(); +} +</script> +</body> +</html> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html new file mode 100644 index 0000000000..a33844f6b4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html @@ -0,0 +1,812 @@ +<!-- +Copyright (c) 2019 The Khronos Group Inc. +Use of this source code is governed by an MIT-style license that can be +found in the LICENSE.txt file. +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WEBGL_draw_buffers Conformance Tests</title> +<link rel="stylesheet" href="../../resources/js-test-style.css"/> +<script src="../../js/js-test-pre.js"></script> +<script src="../../js/webgl-test-utils.js"></script> +<script src="../../js/tests/webgl-draw-buffers-utils.js"></script> +</head> +<body> +<div id="description"></div> +<canvas id="canvas" width="64" height="64"> </canvas> +<div id="console"></div> +<script id="fshader" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +uniform vec4 u_colors[$(numDrawingBuffers)]; +void main() { + for (int i = 0; i < $(numDrawingBuffers); ++i) { + gl_FragData[i] = u_colors[i]; + } +} +</script> +<script id="fshaderNoWrite" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +void main() { +} +</script> +<script id="fshaderRed" type="x-shader/x-fragment"> +precision mediump float; +void main() { + gl_FragColor = vec4(1,0,0,1); +} +</script> +<script id="fshaderRedWithExtension" type="x-shader/x-fragment"> +#extension GL_EXT_draw_buffers : require +precision mediump float; +void main() { + gl_FragColor = vec4(1,0,0,1); +} +</script> +<script id="fshaderMacroDisabled" type="x-shader/x-fragment"> +#ifdef GL_EXT_draw_buffers + bad code here +#endif +precision mediump float; +void main() { + gl_FragColor = vec4(0,0,0,0); +} +</script> +<script id="fshaderMacroEnabled" type="x-shader/x-fragment"> +#ifdef GL_EXT_draw_buffers + #if GL_EXT_draw_buffers == 1 + #define CODE + #else + #define CODE this_code_is_bad_it_should_have_compiled + #endif +#else + #define CODE this_code_is_bad_it_should_have_compiled +#endif +CODE +precision mediump float; +void main() { + gl_FragColor = vec4(0,0,0,0); +} +</script> +<script id="fshaderBuiltInConstEnabled" type="x-shader/x-fragment"> +precision mediump float; +void main() { + gl_FragColor = (gl_MaxDrawBuffers == $(numDrawingBuffers)) ? vec4(0,1,0,1) : vec4(1,0,0,1); +} +</script> +<script> +"use strict"; +description("This test verifies the functionality of the WEBGL_draw_buffers extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var programWithMaxDrawBuffersEqualOne = null; +var drawBuffersUtils; +let fb; + +var extensionConstants = [ + { name: "MAX_COLOR_ATTACHMENTS_WEBGL", enum: 0x8CDF, expectedFn: function(v) { return v >= 4; }, passMsg: " should be >= 4"}, + { name: "MAX_DRAW_BUFFERS_WEBGL", enum: 0x8824, expectedFn: function(v) { return v > 0; }, passMsg: " should be > 0"}, + + { name: "COLOR_ATTACHMENT0_WEBGL", enum: 0x8CE0, }, + { name: "COLOR_ATTACHMENT1_WEBGL", enum: 0x8CE1, }, + { name: "COLOR_ATTACHMENT2_WEBGL", enum: 0x8CE2, }, + { name: "COLOR_ATTACHMENT3_WEBGL", enum: 0x8CE3, }, + { name: "COLOR_ATTACHMENT4_WEBGL", enum: 0x8CE4, }, + { name: "COLOR_ATTACHMENT5_WEBGL", enum: 0x8CE5, }, + { name: "COLOR_ATTACHMENT6_WEBGL", enum: 0x8CE6, }, + { name: "COLOR_ATTACHMENT7_WEBGL", enum: 0x8CE7, }, + { name: "COLOR_ATTACHMENT8_WEBGL", enum: 0x8CE8, }, + { name: "COLOR_ATTACHMENT9_WEBGL", enum: 0x8CE9, }, + { name: "COLOR_ATTACHMENT10_WEBGL", enum: 0x8CEA, }, + { name: "COLOR_ATTACHMENT11_WEBGL", enum: 0x8CEB, }, + { name: "COLOR_ATTACHMENT12_WEBGL", enum: 0x8CEC, }, + { name: "COLOR_ATTACHMENT13_WEBGL", enum: 0x8CED, }, + { name: "COLOR_ATTACHMENT14_WEBGL", enum: 0x8CEE, }, + { name: "COLOR_ATTACHMENT15_WEBGL", enum: 0x8CEF, }, + + { name: "DRAW_BUFFER0_WEBGL", enum: 0x8825, }, + { name: "DRAW_BUFFER1_WEBGL", enum: 0x8826, }, + { name: "DRAW_BUFFER2_WEBGL", enum: 0x8827, }, + { name: "DRAW_BUFFER3_WEBGL", enum: 0x8828, }, + { name: "DRAW_BUFFER4_WEBGL", enum: 0x8829, }, + { name: "DRAW_BUFFER5_WEBGL", enum: 0x882A, }, + { name: "DRAW_BUFFER6_WEBGL", enum: 0x882B, }, + { name: "DRAW_BUFFER7_WEBGL", enum: 0x882C, }, + { name: "DRAW_BUFFER8_WEBGL", enum: 0x882D, }, + { name: "DRAW_BUFFER9_WEBGL", enum: 0x882E, }, + { name: "DRAW_BUFFER10_WEBGL", enum: 0x882F, }, + { name: "DRAW_BUFFER11_WEBGL", enum: 0x8830, }, + { name: "DRAW_BUFFER12_WEBGL", enum: 0x8831, }, + { name: "DRAW_BUFFER13_WEBGL", enum: 0x8832, }, + { name: "DRAW_BUFFER14_WEBGL", enum: 0x8833, }, + { name: "DRAW_BUFFER15_WEBGL", enum: 0x8834, }, +]; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runEnumTestDisabled(); + runShadersTestDisabled(); + runAttachmentTestDisabled(); + + debug(""); + + // Query the extension and store globally so shouldBe can access it + ext = gl.getExtension("WEBGL_draw_buffers"); + if (!ext) { + testPassed("No WEBGL_draw_buffers support -- this is legal"); + + runSupportedTest(false); + finishTest(); + } else { + testPassed("Successfully enabled WEBGL_draw_buffers extension"); + + drawBuffersUtils = WebGLDrawBuffersUtils(gl, ext); + runSupportedTest(true); + runEnumTestEnabled(); + runShadersTestEnabled(); + runAttachmentTestEnabled(); + runDrawTests(); + runPreserveTests(); + } +} + +function createExtDrawBuffersProgram(scriptId, sub) { + var fsource = wtu.getScript(scriptId); + fsource = wtu.replaceParams(fsource, sub); + return wtu.setupProgram(gl, [wtu.simpleVertexShader, fsource], ["vPosition"], undefined, true); +} + +function runSupportedTest(extensionEnabled) { + var supported = gl.getSupportedExtensions(); + if (supported.indexOf("WEBGL_draw_buffers") >= 0) { + if (extensionEnabled) { + testPassed("WEBGL_draw_buffers listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_draw_buffers listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_draw_buffers not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_draw_buffers not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runEnumTestDisabled() { + debug(""); + debug("Testing binding enum with extension disabled"); + + // Use the constant directly as we don't have the extension + extensionConstants.forEach(function(c) { + if (c.expectedFn) { + shouldBeNull(`gl.getParameter(${c.enum})`); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, c.name + " should not be queryable if extension is disabled"); + } + }); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runEnumTestEnabled() { + debug(""); + debug("Testing enums with extension enabled"); + + extensionConstants.forEach(function(c) { + shouldBe("ext." + c.name, "0x" + c.enum.toString(16)); + if (c.expectedFn) { + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "before getParameter"); + debug(c.name + ": 0x" + ext[c.name].toString(16)); + expectTrue(c.expectedFn(gl.getParameter(ext[c.name])), "gl.getParameter(ext." + c.name + ")" + c.passMsg); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, c.name + " query should succeed if extension is enabled"); + } + }); + + shouldBeTrue("gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL) >= gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL)"); + + debug("Testing drawBuffersWEBGL with default drawing buffer"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.drawBuffersWEBGL([])"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.drawBuffersWEBGL([gl.NONE, gl.NONE])"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.drawBuffersWEBGL([ext.COLOR_ATTACHMENT0_WEBGL])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.NONE])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.BACK])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + debug("Testing drawBuffers and getParameter with bindFramebuffer, without drawing."); + fb = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.COLOR_ATTACHMENT0"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.NONE"); + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.NONE])"); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.NONE,gl.COLOR_ATTACHMENT0+1])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.COLOR_ATTACHMENT0+1"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "ext.drawBuffersWEBGL([gl.COLOR_ATTACHMENT0,gl.COLOR_ATTACHMENT0+1])"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.COLOR_ATTACHMENT0"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.COLOR_ATTACHMENT0+1"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "gl.deleteFramebuffer(fb)"); + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK"); +} + +function testShaders(tests, sub) { + tests.forEach(function(test) { + var shaders = [wtu.simpleVertexShader, wtu.replaceParams(wtu.getScript(test.fragmentShaderTemplate), sub)]; + var program = wtu.setupProgram(gl, shaders, ["vPosition"], undefined, true); + var programLinkedSuccessfully = (program != null); + var expectedProgramToLinkSuccessfully = (test.expectFailure == true); + expectTrue(programLinkedSuccessfully != expectedProgramToLinkSuccessfully, test.msg); + gl.deleteProgram(program); + }); +} + +function runShadersTestDisabled() { + debug(""); + debug("test shaders disabled"); + + var sub = {numDrawingBuffers: 1}; + testShaders([ + { fragmentShaderTemplate: "fshaderMacroDisabled", + msg: "GL_EXT_draw_buffers should not be defined in GLSL", + }, + { fragmentShaderTemplate: "fshader", + msg: "#extension GL_EXT_draw_buffers should not be allowed in GLSL", + expectFailure: true, + }, + ], sub); + + programWithMaxDrawBuffersEqualOne = createExtDrawBuffersProgram("fshaderBuiltInConstEnabled", sub); + wtu.setupUnitQuad(gl); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runShadersTestEnabled() { + debug(""); + debug("test shaders enabled"); + + var sub = {numDrawingBuffers: gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL)}; + testShaders([ + { fragmentShaderTemplate: "fshaderMacroEnabled", + msg: "GL_EXT_draw_buffers should be defined as 1 in GLSL", + }, + { fragmentShaderTemplate: "fshader", + msg: "fragment shader containing the #extension directive should compile", + }, + ], sub); + + var program = createExtDrawBuffersProgram("fshaderBuiltInConstEnabled", sub); + wtu.setupUnitQuad(gl); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + gl.deleteProgram(program); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + debug(""); + debug("test that gl_MaxDrawBuffers is frozen at link time and enabling the extension won't change it."); + gl.useProgram(programWithMaxDrawBuffersEqualOne); + wtu.clearAndDrawUnitQuad(gl); + wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green"); + gl.deleteProgram(programWithMaxDrawBuffersEqualOne); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function runAttachmentTestDisabled() { + debug(""); + debug("test attachment disabled"); + var tex = gl.createTexture(); + var fb = gl.createFramebuffer(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + 1, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "should not be able to attach to gl.COLOR_ATTACHMENT1"); + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function makeArray(size, value) { + var array = [] + for (var ii = 0; ii < size; ++ii) { + array.push(value); + } + return array; +} + +function runAttachmentTestEnabled() { + debug(""); + debug("test attachment enabled"); + + var maxDrawingBuffers = gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL); + var maxColorAttachments = gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL); + + var tex = gl.createTexture(); + var fb = gl.createFramebuffer(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + maxColorAttachments, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "should not be able to attach pass the max attachment point: gl.COLOR_ATTACHMENT0 + " + maxColorAttachments); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + maxColorAttachments - 1, gl.TEXTURE_2D, tex, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to attach to the max attachment point: gl.COLOR_ATTACHMENT0 + " + (maxColorAttachments - 1)); + ext.drawBuffersWEBGL(makeArray(maxDrawingBuffers, gl.NONE)); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL with array NONE of size " + maxColorAttachments); + var bufs = drawBuffersUtils.makeColorAttachmentArray(maxDrawingBuffers); + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL with array attachments of size " + maxColorAttachments); + bufs[0] = gl.NONE; + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL with mixed array attachments of size " + maxColorAttachments); + if (maxDrawingBuffers > 1) { + bufs[0] = ext.COLOR_ATTACHMENT1_WEBGL; + bufs[1] = ext.COLOR_ATTACHMENT0_WEBGL; + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should not be able to call drawBuffersWEBGL with out of order attachments of size " + maxColorAttachments); + var bufs = drawBuffersUtils.makeColorAttachmentArray(Math.floor(maxDrawingBuffers / 2)); + ext.drawBuffersWEBGL(bufs); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffersWEBGL with short array of attachments of size " + bufs.length); + } + + gl.deleteFramebuffer(fb); + gl.deleteTexture(tex); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); +} + +function makeColorByIndex(index) { + var low = (index - 1) % 15 + 1; + var high = (index - 1) / 15; + + var zeroOrOne = function(v) { + return v ? 1 : 0; + }; + + var oneOrTwo = function(v) { + return v ? 2 : 1; + } + + var makeComponent = function(b0, b1, b2) { + return Math.floor(255 * zeroOrOne(b0) / oneOrTwo(b1) / oneOrTwo(b2)); + }; + return [ + makeComponent(low & (1 << 0), high & (1 << 0), high & (1 << 4)), + makeComponent(low & (1 << 1), high & (1 << 1), high & (1 << 5)), + makeComponent(low & (1 << 2), high & (1 << 2), high & (1 << 6)), + makeComponent(low & (1 << 3), high & (1 << 3), high & (1 << 7)), + ]; +} + +function runDrawTests() { + debug(""); + debug("--------- draw tests -----------"); + var fb = gl.createFramebuffer(); + var fb2 = gl.createFramebuffer(); + var halfFB1 = gl.createFramebuffer(); + var halfFB2 = gl.createFramebuffer(); + var endsFB = gl.createFramebuffer(); + var middleFB = gl.createFramebuffer(); + + var maxDrawingBuffers = gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL); + var maxUsable = drawBuffersUtils.getMaxUsableColorAttachments(); + var half = Math.floor(maxUsable / 2); + var bufs = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + var nones = makeArray(maxUsable, gl.NONE); + + [fb, fb2, halfFB1, halfFB2, endsFB, middleFB].forEach(function(fbo) { + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + }); + + var checkProgram = wtu.setupTexturedQuad(gl); + var redProgram = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderRed"], ["vPosition"]); + var redProgramWithExtension = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderRedWithExtension"], ["vPosition"]); + var drawProgram = createExtDrawBuffersProgram("fshader", {numDrawingBuffers: maxDrawingBuffers}); + var width = 64; + var height = 64; + var attachments = []; + // Makes 6 framebuffers. + // fb and fb2 have all the attachments. + // halfFB1 has the first half of the attachments + // halfFB2 has the second half of the attachments + // endsFB has the first and last attachments + // middleFB has all but the first and last attachments + for (var ii = 0; ii < maxUsable; ++ii) { + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, ii < half ? halfFB1 : halfFB2); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, (ii == 0 || ii == (maxUsable - 1)) ? endsFB : middleFB); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0); + var location = gl.getUniformLocation(drawProgram, "u_colors[" + ii + "]"); + var color = makeColorByIndex(ii + 1); + var floatColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255]; + gl.uniform4fv(location, floatColor); + attachments.push({ + texture: tex, + color: color + }); + } + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + var drawAndCheckAttachments = function(testFB, msg, testFn) { + debug("test clearing " + msg); + + gl.bindFramebuffer(gl.FRAMEBUFFER, testFB); + + attachments.forEach(function(attachment, index) { + debug("attachment: " + index + " = " + wtu.glEnumToString(gl, gl.getParameter(ext.DRAW_BUFFER0_WEBGL + index)) + + ", " + wtu.glEnumToString(gl, gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + index, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE))); + }); + + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + debug("framebuffer not complete"); + debug(""); + return; + } + + // Clear all the attachments + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.clearColor(0, 0, 0, 0); + gl.clear(gl.COLOR_BUFFER_BIT); + //drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + // return [0, 0, 0, 0]; + //}); + //debug("--"); + + // Clear some attachments using testFB + gl.bindFramebuffer(gl.FRAMEBUFFER, testFB); + + gl.clearColor(0, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return testFn(attachment, index) ? [0, 255, 0, 255] : [0, 0, 0, 0]; + }); + + debug("test drawing to " + msg); + + // Draw to some attachments using testFB + gl.useProgram(drawProgram); + gl.bindFramebuffer(gl.FRAMEBUFFER, testFB); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return testFn(attachment, index) ? attachment.color : [0, 0, 0, 0]; + }); + }; + + gl.useProgram(drawProgram); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + ext.drawBuffersWEBGL(bufs); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + + wtu.drawUnitQuad(gl); + + debug("test that each texture got the correct color."); + + drawBuffersUtils.checkAttachmentsForColor(attachments); + + debug("test clearing clears all the textures"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.clearColor(0, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 255, 0, 255]); + + debug("test a fragment shader writing to neither gl_FragColor nor gl_FragData does not touch attachments"); + var noWriteProgram = wtu.setupProgram(gl, [wtu.simpleVertexShader, "fshaderNoWrite"], ["vPosition"]); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no GL error setting up the program"); + if (!noWriteProgram) { + testFailed("Setup a program where fragment shader writes nothing failed"); + } else { + gl.useProgram(noWriteProgram); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Active draw buffers with missing frag outputs."); + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 255, 0, 255]); + gl.deleteProgram(noWriteProgram); + } + + debug("test that NONE draws nothing"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(nones); + gl.useProgram(redProgram); + wtu.clearAndDrawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [0, 255, 0, 255]); + + debug("test that gl_FragColor does not broadcast unless extension is enabled in fragment shader"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.useProgram(redProgram); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Active draw buffers with missing frag outputs."); + gl.colorMask(false, false, false, false); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors when all 4 channels of color mask are disabled."); + gl.colorMask(false, true, false, false); + wtu.drawUnitQuad(gl); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "partially diabled color mask shall have no impact."); + gl.colorMask(true, true, true, true); + + debug("test that gl_FragColor broadcasts if extension is enabled in fragment shader"); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.useProgram(redProgramWithExtension); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + if (maxUsable > 1) { + // First half of color buffers disable. + var bufs1 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + // Second half of color buffers disable. + var bufs2 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + // Color buffers with even indices disabled. + var bufs3 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + // Color buffers with odd indices disabled. + var bufs4 = drawBuffersUtils.makeColorAttachmentArray(maxUsable); + for (var ii = 0; ii < maxUsable; ++ii) { + if (ii < half) { + bufs1[ii] = gl.NONE; + } else { + bufs2[ii] = gl.NONE; + } + if (ii % 2) { + bufs3[ii] = gl.NONE; + } else { + bufs4[ii] = gl.NONE; + } + } + + debug("test setting first half to NONE and clearing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + // We should clear all buffers rather than depending on the previous + // gl_FragColor broadcasts test to succeed and setting the colors. + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + ext.drawBuffersWEBGL(bufs1); + gl.clearColor(0, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? [255, 0, 0, 255] : [0, 255, 0, 255]; + }); + + debug("test setting first half to NONE and drawing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? [255, 0, 0, 255] : attachment.color; + }); + + debug("test setting second half to NONE and clearing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + ext.drawBuffersWEBGL(bufs2); + gl.clearColor(0, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? [0, 0, 255, 255] : [255, 0, 0, 255]; + }); + + debug("test setting second half to NONE and drawing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return index < half ? attachment.color : [255, 0, 0, 255]; + }); + + debug("test setting buffers with even indices to NONE and clearing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + ext.drawBuffersWEBGL(bufs3); + gl.clearColor(1, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return (index % 2) ? [255, 0, 0, 255] : [255, 0, 255, 255]; + }); + + debug("test setting buffers with odd indices to NONE and drawing"); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(0, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(drawProgram); + ext.drawBuffersWEBGL(bufs4); + wtu.drawUnitQuad(gl); + + drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) { + return (index % 2 == 0) ? [0, 0, 0, 255] : attachment.color; + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB1); + ext.drawBuffersWEBGL(bufs); + drawAndCheckAttachments( + halfFB1, "framebuffer that only has first half of attachments", + function(attachment, index) { + return index < half; + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB2); + ext.drawBuffersWEBGL(bufs); + drawAndCheckAttachments( + halfFB2, "framebuffer that only has second half of attachments", + function(attachment, index) { + return index >= half; + }); + + if (maxUsable > 2) { + gl.bindFramebuffer(gl.FRAMEBUFFER, endsFB); + ext.drawBuffersWEBGL(bufs); + drawAndCheckAttachments( + endsFB, "framebuffer that only has first and last attachments", + function(attachment, index) { + return index == 0 || index == (maxUsable - 1); + }); + + gl.bindFramebuffer(gl.FRAMEBUFFER, middleFB); + ext.drawBuffersWEBGL(bufs); + drawAndCheckAttachments( + middleFB, + "framebuffer that has all but the first and last attachments", + function(attachment, index) { + return index != 0 && index != (maxUsable - 1); + }); + } + } + + debug("test switching between fbos does not affect any color attachment contents"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + ext.drawBuffersWEBGL(nones); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + ext.drawBuffersWEBGL(bufs); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + // fb2 still has the NONE draw buffers from before, so this draw should be a no-op. + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + drawBuffersUtils.checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + drawBuffersUtils.checkAttachmentsForColor(attachments); + + debug("test queries"); + debug("check framebuffer with all attachments on"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + for (var ii = 0; ii < maxUsable; ++ii) { + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL + " + ii + ")", "gl.COLOR_ATTACHMENT0 + " + ii); + } + + debug("check framebuffer with all attachments off"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + for (var ii = 0; ii < maxUsable; ++ii) { + shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL + " + ii + ")", "gl.NONE"); + } + + debug("test attachment size mis-match"); + gl.bindTexture(gl.TEXTURE_2D, attachments[0].texture); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width * 2, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + gl.bindFramebuffer(gl.FRAMEBUFFER, fb2); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + + gl.deleteFramebuffer(fb); + gl.deleteFramebuffer(fb2); + gl.deleteFramebuffer(halfFB1); + gl.deleteFramebuffer(halfFB2); + attachments.forEach(function(attachment) { + gl.deleteTexture(attachment.texture); + }); + gl.deleteProgram(checkProgram); + gl.deleteProgram(redProgram); + gl.deleteProgram(redProgramWithExtension); + gl.deleteProgram(drawProgram); +} + +function runPreserveTests() { + debug(""); + debug("--------- preserve tests -----------"); + + debug("Testing that frame buffer is cleared after compositing"); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + gl.clearColor(1, 1, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [255, 255, 0, 255], "should be yellow"); + + // set the draw buffer to NONE + ext.drawBuffersWEBGL([gl.NONE]); + gl.clearColor(1, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + + // make sure the canvas is still clear + wtu.checkCanvas(gl, [255, 255, 0, 255], "should be yellow"); + + wtu.waitForComposite(function() { + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT); + wtu.checkCanvas(gl, [0, 0, 0, 0], "should be clear"); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors"); + + runEndTests(); + }); +} + +function runEndTests() { + // Create new context and verify shader tests with no extension still succeeds. + debug(""); + debug("Testing new context with no extension"); + gl = wtu.create3DContext(); + if (!gl) { + testFailed("New WebGL context does not exist"); + } else { + testPassed("New WebGL context exists"); + runEnumTestDisabled(); + runShadersTestDisabled(); + runAttachmentTestDisabled(); + } + + finishTest(); +} +</script> +</body> +</html> 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> diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-webcodecs-video-frame.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-webcodecs-video-frame.html new file mode 100644 index 0000000000..f0e413e2f6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-webcodecs-video-frame.html @@ -0,0 +1,211 @@ +<!--
+Copyright (c) 2021 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+-->
+
+<!DOCTYPE html>
+<html>
+
+<head>
+ <meta charset="UTF-8">
+ <link rel="stylesheet" href="../../resources/js-test-style.css" />
+ <script src="../../js/js-test-pre.js"></script>
+ <script src="../../js/webgl-test-utils.js"></script>
+ <script src="../../js/tests/out-of-bounds-test.js"></script>
+ <script src="../../../../extensions/proposals/WEBGL_webcodecs_video_frame/webgl_webcodecs_video_frame.js"></script>
+ <style>
+ canvas {
+ padding: 10px;
+ background: gold;
+ }
+
+ button {
+ background-color: #555555;
+ border: none;
+ color: white;
+ padding: 15px 32px;
+ width: 150px;
+ text-align: center;
+ display: block;
+ font-size: 16px;
+ }
+ </style>
+</head>
+
+<body>
+ <canvas id="src" width="640" height="480"></canvas>
+ <canvas id="dst" width="640" height="480"></canvas>
+ <p id="info"></p>
+ <div id="description"></div>
+ <div id="console"></div>
+ <script>
+ "use strict";
+ description("Test of importing Videoframe from Webcodecs to Webgl");
+
+ const kIsRunningTest = true;
+ const kMaxFrame = 10;
+ const kTestPixel = [255, 128, 0, 255];
+ // Sum of pixel difference of R/G/B channel. Use to decide whether a
+ // pixel is matched with another.
+ const codec_string = "vp09.00.51.08.00";
+
+ let wtu = WebGLTestUtils;
+ let cnv = document.getElementById("src");
+ let src_width = cnv.width;
+ let src_height = cnv.height;
+ let src_color = "rgba(" + kTestPixel[0].toString() + "," + kTestPixel[1].toString() + ","
+ + kTestPixel[2].toString() + "," + kTestPixel[3].toString() + ")";
+ let frame_counter = 0;
+ let pixelCompareTolerance = 5;
+
+ function getQueryVariable(variable) {
+ var query = window.location.search.substring(1);
+ var vars = query.split("&");
+ for (var i = 0; i < vars.length; i++) {
+ var pair = vars[i].split("=");
+ if (pair[0] == variable) { return pair[1]; }
+ }
+ return false;
+ }
+
+ let th = parseInt(getQueryVariable('threshold'));
+ if (!isNaN(th))
+ pixelCompareTolerance = th;
+
+ async function startDrawing() {
+ let cnv = document.getElementById("src");
+ var ctx = cnv.getContext('2d', { alpha: false });
+
+ ctx.fillStyle = src_color;
+ let drawOneFrame = function (time) {
+ ctx.fillStyle = src_color;
+ ctx.fillRect(0, 0, src_width, src_height);
+ window.requestAnimationFrame(drawOneFrame);
+ }
+ window.requestAnimationFrame(drawOneFrame);
+ }
+
+ function captureAndEncode(processChunk) {
+ let cnv = document.getElementById("src");
+ let fps = 60;
+ let pending_outputs = 0;
+ let stream = cnv.captureStream(fps);
+ let processor = new MediaStreamTrackProcessor(stream.getVideoTracks()[0]);
+
+ const init = {
+ output: (chunk) => {
+ testPassed("Encode frame successfully.");
+ pending_outputs--;
+ processChunk(chunk);
+ },
+ error: (e) => {
+ testFailed("Failed to encode frame.");
+ finishTest();
+ vtr.stop();
+ }
+ };
+
+ const config = {
+ codec: codec_string,
+ width: cnv.width,
+ height: cnv.height,
+ bitrate: 10e6,
+ framerate: fps,
+ };
+
+ let encoder = new VideoEncoder(init);
+ encoder.configure(config);
+
+ const frame_reader = processor.readable.getReader();
+ frame_reader.read().then(function processFrame({done, value}) {
+ if (done)
+ return;
+
+ if (pending_outputs > 30) {
+ console.log("drop this frame");
+ // Too many frames in flight, encoder is overwhelmed
+ // let's drop this frame.
+ value.close();
+ frame_reader.read().then(processFrame);
+ return;
+ }
+
+ if(frame_counter == kMaxFrame) {
+ frame_reader.releaseLock();
+ processor.readable.cancel();
+ value.close();
+ return;
+ }
+
+ frame_counter++;
+ pending_outputs++;
+ const insert_keyframe = (frame_counter % 150) == 0;
+ encoder.encode(value, { keyFrame: insert_keyframe });
+
+ frame_reader.read().then(processFrame);
+ });
+ }
+
+ function startDecodingAndRendering(cnv, handleFrame) {
+ const init = {
+ output: handleFrame,
+ error: (e) => {
+ testFailed("Failed to decode frame.");
+ finishTest();
+ }
+ };
+
+ const config = {
+ codec: codec_string,
+ codedWidth: cnv.width,
+ codedHeight: cnv.height,
+ acceleration: "deny",
+
+ };
+
+ let decoder = new VideoDecoder(init);
+ decoder.configure(config);
+ return decoder;
+ }
+
+ function isFramePixelMatched(gl, th_per_pixel = pixelCompareTolerance) {
+ WebGLTestUtils.checkCanvasRect(gl, 0, 0, src_width, src_width, kTestPixel, "should be orange", pixelCompareTolerance)
+ }
+
+ function main() {
+ if (!("VideoEncoder" in window)) {
+ testPassed("WebCodecs API is not supported.");
+ finishTest();
+ return;
+ }
+ let cnv = document.getElementById("dst");
+
+ let webgl_webcodecs_test_context = {
+ maxFrameTested: kMaxFrame,
+ displayed_frame: 0,
+ isFramePixelMatched: isFramePixelMatched,
+ testFailed: testFailed,
+ testPassed: testPassed,
+ finishTest: finishTest
+ };
+ setTestMode(webgl_webcodecs_test_context);
+ let handleFrame = requestWebGLVideoFrameHandler(cnv);
+ if (handleFrame === null) {
+ finishTest();
+ return;
+ }
+
+ startDrawing();
+ let decoder = startDecodingAndRendering(cnv, handleFrame);
+ captureAndEncode((chunk) => {
+ decoder.decode(chunk);
+ });
+ }
+
+ document.body.onload = main;
+ </script>
+
+</body>
+
+</html>
\ No newline at end of file |