diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance/extensions')
39 files changed, 11244 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 000000000..8613963d8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt @@ -0,0 +1,39 @@ +--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-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 +--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 +webgl-debug-renderer-info.html +webgl-debug-shaders.html +--min-version 1.0.3 webgl-compressed-texture-atc.html +--min-version 1.0.4 webgl-compressed-texture-etc.html +--min-version 1.0.3 webgl-compressed-texture-pvrtc.html +--min-version 1.0.2 webgl-compressed-texture-s3tc.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-framebuffer-unsupported.html +--min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-max-draw-buffers.html +--min-version 1.0.3 webgl-shared-resources.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 000000000..d632c9d37 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays-out-of-bounds.html @@ -0,0 +1,77 @@ +<!-- + +/* +** Copyright (c) 2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..791326425 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays.html @@ -0,0 +1,652 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +</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(""); + +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"); + + runSupportedTest(true); + + runDivisorTestEnabled(); + runUniqueObjectTest(); + + setupCanvas(); + runOutputTests(); + runDrawArraysWithOffsetTest(); + runVAOInstancingInteractionTest(); + runANGLECorruptionTest(); + } +} + +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); + } +} + +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 + ]); + 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]); + + 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"); +} + +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"); +} + +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); +} + +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 iteration = 0; + var totalIterations = 10; + + 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); + + function cycleAndTest() { + // 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); + if (iteration < totalIterations) { + ++iteration; + setTimeout(cycleAndTest, 0); + } else { + finishTest(); + } + } else { + testFailed("Failed test " + iteration + " of " + totalIterations); + finishTest(); + } + } + + cycleAndTest(); +} +</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 000000000..315dbd742 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-blend-minmax.html @@ -0,0 +1,246 @@ +<!-- + +/* +** Copyright (c) 2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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-disjoint-timer-query.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html new file mode 100644 index 000000000..bb349c4b0 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html @@ -0,0 +1,326 @@ +<!-- +/* +** Copyright (c) 2015 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ +--> + +<!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 { + 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-frag-depth.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-frag-depth.html new file mode 100644 index 000000000..6c71d42e6 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-frag-depth.html @@ -0,0 +1,310 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..ebb53b841 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html @@ -0,0 +1,430 @@ +<!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.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> +"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(); + 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(); + }); +} + +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 000000000..04c30064c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-shader-texture-lod.html @@ -0,0 +1,362 @@ +<!-- + +/* +** Copyright (c) 2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> +<!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-filter-anisotropic.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-filter-anisotropic.html new file mode 100644 index 000000000..e802bf0b1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-filter-anisotropic.html @@ -0,0 +1,190 @@ +<!-- + +/* +** Copyright (c) 2012 Florian Boesch <pyalot@gmail.com>. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +"use strict"; +description("This test verifies the functionality of the EXT_texture_filter_anisotropic extension, if it is available."); + +debug(""); + +var wtu = WebGLTestUtils; +var canvas = document.getElementById("canvas"); +var gl = wtu.create3DContext(canvas); +var ext = null; + +if (!gl) { + testFailed("WebGL context does not exist"); +} else { + testPassed("WebGL context exists"); + + // Run tests with extension disabled + runHintTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "EXT_texture_filter_anisotropic"); + + if (!ext) { + testPassed("No EXT_texture_filter_anisotropic support -- this is legal"); + + runSupportedTest(false); + } else { + testPassed("Successfully enabled EXT_texture_filter_anisotropic extension"); + + runSupportedTest(true); + runHintTestEnabled(); + } +} + +function runSupportedTest(extensionEnabled) { + if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "EXT_texture_filter_anisotropic") !== undefined) { + if (extensionEnabled) { + testPassed("EXT_texture_filter_anisotropic listed as supported and getExtension succeeded"); + } else { + testFailed("EXT_texture_filter_anisotropic listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("EXT_texture_filter_anisotropic not listed as supported but getExtension succeeded"); + } else { + testPassed("EXT_texture_filter_anisotropic not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runHintTestDisabled() { + debug("Testing MAX_TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + + var MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; + gl.getParameter(MAX_TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "MAX_TEXTURE_MAX_ANISOTROPY_EXT should not be queryable if extension is disabled"); + + debug("Testing TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + var TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + gl.getTexParameter(gl.TEXTURE_2D, TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be queryable if extension is disabled"); + + gl.texParameterf(gl.TEXTURE_2D, TEXTURE_MAX_ANISOTROPY_EXT, 1); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be settable if extension is disabled"); + + gl.texParameteri(gl.TEXTURE_2D, TEXTURE_MAX_ANISOTROPY_EXT, 1); + wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "TEXTURE_MAX_ANISOTROPY_EXT should not be settable if extension is disabled"); + + gl.deleteTexture(texture); +} + +function runHintTestEnabled() { + debug("Testing MAX_TEXTURE_MAX_ANISOTROPY_EXT with extension enabled"); + + shouldBe("ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT", "0x84FF"); + + var max_anisotropy = gl.getParameter(ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "MAX_TEXTURE_MAX_ANISOTROPY_EXT query should succeed if extension is enabled"); + + if(max_anisotropy >= 2){ + testPassed("Minimum value of MAX_TEXTURE_MAX_ANISOTROPY_EXT is 2.0"); + } + else{ + testFailed("Minimum value of MAX_TEXTURE_MAX_ANISOTROPY_EXT is 2.0, returned values was: " + max_anisotropy); + } + + // TODO make a texture and verify initial value == 1 and setting to less than 1 is invalid value + + debug("Testing TEXTURE_MAX_ANISOTROPY_EXT with extension disabled"); + shouldBe("ext.TEXTURE_MAX_ANISOTROPY_EXT", "0x84FE"); + + var texture = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, texture); + + var queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "TEXTURE_MAX_ANISOTROPY_EXT query should succeed if extension is enabled"); + + if(queried_value == 1){ + testPassed("Initial value of TEXTURE_MAX_ANISOTROPY_EXT is 1.0"); + } + else{ + testFailed("Initial value of TEXTURE_MAX_ANISOTROPY_EXT should be 1.0, returned value was: " + queried_value); + } + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to < 1 should be an invalid value"); + + gl.texParameteri(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "texParameteri TEXTURE_MAX_ANISOTROPY_EXT set to < 1 should be an invalid value"); + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to >= 2 should succeed"); + + gl.texParameteri(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameteri TEXTURE_MAX_ANISOTROPY_EXT set to >= 2 should succeed"); + + var queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + if(queried_value == max_anisotropy){ + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT matches expecation"); + } + else{ + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT should be: " + max_anisotropy + " , returned value was: " + queried_value); + } + + gl.texParameterf(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT, 1.5); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texParameterf TEXTURE_MAX_ANISOTROPY_EXT set to 1.5 should succeed"); + + queried_value = gl.getTexParameter(gl.TEXTURE_2D, ext.TEXTURE_MAX_ANISOTROPY_EXT); + if(queried_value == 1.5){ + testPassed("Set value of TEXTURE_MAX_ANISOTROPY_EXT matches expecation"); + } + else{ + testFailed("Set value of TEXTURE_MAX_ANISOTROPY_EXT should be: " + 1.5 + " , returned value was: " + queried_value); + } + + + gl.deleteTexture(texture); +} + +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/get-extension.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/get-extension.html new file mode 100644 index 000000000..89e73ee03 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/get-extension.html @@ -0,0 +1,120 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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/oes-element-index-uint.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-element-index-uint.html new file mode 100644 index 000000000..9ee46555d --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-element-index-uint.html @@ -0,0 +1,447 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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(blackList, whiteList) { + 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(blackList, 0); + testList(whiteList, 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 blackList = []; + var whiteList = []; + 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) { + blackList.push(points[n]); + } else { + whiteList.push(points[n]); + } + } + testPixel(blackList, whiteList); + }; + + 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); + shouldBeUndefined('gl.drawElements(gl.TRIANGLES, 3, gl.UNSIGNED_INT, 0)'); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION); + + 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)"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 0)"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "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, gl.INVALID_OPERATION, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 0)"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "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)"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawElements(gl.TRIANGLE_STRIP, 4, gl.UNSIGNED_INT, 0)"); + wtu.shouldGenerateGLError(gl, gl.INVALID_OPERATION, "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-standard-derivatives.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-standard-derivatives.html new file mode 100644 index 000000000..6636b3766 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-standard-derivatives.html @@ -0,0 +1,421 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +<!-- 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 + var 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) + 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() { + // 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 000000000..fdb35d536 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-linear.html @@ -0,0 +1,53 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +<script> +"use strict"; +function testPrologue(gl, extensionTypeName) { + if (!gl.getExtension(extensionTypeName)) { + testPassed("No " + extensionTypeName + " support -- this is legal"); + return false; + } + testPassed("Successfully enabled " + extensionTypeName + " extension"); + return true; +} +</script> +</head> +<body onload='generateTest("OES_texture_float", "OES_texture_float_linear", "FLOAT", testPrologue)()'> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +</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 000000000..36584616a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-canvas.html @@ -0,0 +1,55 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..8ab3300a8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image-data.html @@ -0,0 +1,56 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..ac0293a3e --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image.html @@ -0,0 +1,55 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..03b2d00f1 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html @@ -0,0 +1,60 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> + <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 000000000..4df5e008b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html @@ -0,0 +1,291 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL OES_texture_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> +<!-- 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 extension, if it is 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]); + runRenderTargetAndReadbackTest(testProgram, gl.RGBA, 4, [10000, 10000, 10000, 10000], 0); + runRenderTargetAndReadbackTest(testProgram, gl.RGB, 3, [10000, 10000, 10000, 1], 0); + runRenderTargetAndReadbackTest(testProgram, gl.RGBA, 4, [10000, 10000, 10000, 10000], 1); + runRenderTargetAndReadbackTest(testProgram, gl.RGBA, 4, [10000, 10000, 10000, 10000], 0.5); + runUniqueObjectTest(); + } +} + +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) +{ + 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) { + 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'); +} + + +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 000000000..b9d18f785 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-linear.html @@ -0,0 +1,56 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +<script> +"use strict"; +function testPrologue(gl, extensionTypeName) { + var ext = null; + if (!(ext = gl.getExtension(extensionTypeName))) { + testPassed("No " + extensionTypeName + " support -- this is legal"); + return false; + } + // Required by the test harness. + gl.HALF_FLOAT_OES = ext.HALF_FLOAT_OES; + testPassed("Successfully enabled " + extensionTypeName + " extension"); + return true; +} +</script> +</head> +<body onload='generateTest("OES_texture_half_float", "OES_texture_half_float_linear", "HALF_FLOAT_OES", testPrologue)()'> +<div id="description"></div> +<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas> +<div id="console"></div> +</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 000000000..4fac6b877 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-canvas.html @@ -0,0 +1,60 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..1fca1daaa --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image-data.html @@ -0,0 +1,61 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..075130f38 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image.html @@ -0,0 +1,60 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..ceaca294f --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html @@ -0,0 +1,65 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> + <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 000000000..a676c8ab3 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float.html @@ -0,0 +1,496 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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], }, + { format: gl.RGB, subtractor: [10000, 10000, 10000, 1], }, + ]; + + 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); + }); + + // Check if attaching texture as FBO target succeeds (Not mandatory) + runRenderTest(gl.RGBA, [10000, 10000, 10000, 10000], null); + runRenderTest(gl.RGB, [10000, 10000, 10000, 1], null); + + runFramebufferTest(); + + // Check of getExtension() returns same object + runUniqueObjectTest(); + } +} + +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 getFormatName(format) +{ + if (format == gl.RGBA) + return "RGBA"; + else if (format == gl.RGB) + return "RGB"; + else if (format == gl.LUMINANCE) + return "LUMINANCE"; + else if (format == gl.ALPHA) + return "ALPHA"; + else if (format == gl.LUMINANCE_ALPHA) + return "LUMINANCE_ALPHA"; +} + +function getTypeName(type) { + if (type === gl.UNSIGNED_BYTE) + return "UNSIGNED_BYTE"; + else if (type === ext.HALF_FLOAT_OES) + return "HALF_FLOAT_OES"; + else if (type === gl.UNSIGNED_SHORT_4_4_4_4) + return "UNSIGNED_SHORT_4_4_4_4"; + else if (type === gl.UNSIGNED_SHORT_5_5_5_1) + return "UNSIGNED_SHORT_5_6_5"; + else if (type === gl.FLOAT) + return "FLOAT"; +} + +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 " + getFormatName(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) +{ + 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) { + debug("Half floating point 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 = getTypeName(glType); + + debug(getFormatName(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); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, ext.HALF_FLOAT_OES, null); + + var fbo = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0); + if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { + debug("Half floating point render targets not supported -- this is legal"); + return; + } + 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(getFormatName(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(getFormatName(goodFormat) + " framebuffer tests"); + debug(""); + + gl.texImage2D(gl.TEXTURE_2D, 0, goodFormat, 1, 1, 0, goodFormat, ext.HALF_FLOAT_OES, arrayBufferHalfFloatInput); + shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE"); + + // To avoid GPU idiosyncrasies, dispense with clearing or rendering to the texture. Go straight to readPixels. + + // 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: " + getFormatName(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: " + getTypeName(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 000000000..d9262967a --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object-bufferData.html @@ -0,0 +1,215 @@ +<!-- + +/* +** Copyright (c) 2014 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..4bd2a4fd8 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html @@ -0,0 +1,738 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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; + +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(); + 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); + + // The buffers should not be accessible at this point. Deleted objects that are bound + // in the current context undergo an automatic unbinding + var boundPositionBuffer = gl.getVertexAttrib(0, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING); + if(boundPositionBuffer == positionBuffer) { + testFailed("Position buffer should be automatically unbound when deleted"); + } + var boundColorBuffer = gl.getVertexAttrib(1, gl.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING); + if(boundColorBuffer == colorBuffer) { + testFailed("Color buffer should be automatically unbound when deleted"); + } + + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_BYTE, 0); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Draw call should fail with unbound position and color buffers"); + + var isPositionBuffer = gl.isBuffer(positionBuffer); + var isColorBuffer = gl.isBuffer(colorBuffer); + + if(isPositionBuffer) testFailed("Position buffer should no longer exist after last ref removed"); + if(isColorBuffer) testFailed("Color buffer should no longer exist after last ref removed"); + } +} + +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"); +} + +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-atc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-atc.html new file mode 100644 index 000000000..7bc4a7463 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-atc.html @@ -0,0 +1,431 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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_atc 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_atc extension, if it is available."); + +debug(""); + +// Compressed textures generated with AMD's Compressonator tool +// http://developer.amd.com/resources/archive/archived-tools/gpu-tools-archive/the-compressonator/ +var img_4x4_rgba_raw = new Uint8Array([ + 0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff, + 0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff, + 0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff, +]); +var img_4x4_rgb_atc = new Uint8Array([ + 0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff, +]); +var img_4x4_rgba_atc_explicit = new Uint8Array([ + 0xf6,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff, +]); +var img_4x4_rgba_atc_interpolated = new Uint8Array([ + 0xff,0x6a,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff, +]); +var img_8x8_rgba_raw = new Uint8Array([ + 0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff, + 0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff, + 0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff, + 0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff, + 0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff, + 0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff, + 0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff, + 0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff, + 0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff, + 0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff, + 0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff, + 0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff, + 0xff,0x00,0xff,0x69,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff, + 0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff, +]); +var img_8x8_rgb_atc = new Uint8Array([ + 0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff,0x1f,0x00,0xe0,0xff,0x33,0x30,0x3f,0x00, + 0x1f,0x7c,0xe0,0x07,0x33,0x30,0x3f,0x00,0x1f,0x00,0xff,0x07,0xcc,0xcf,0xc0,0xff, +]); +var img_8x8_rgba_atc_explicit = new Uint8Array([ + 0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1f,0x00,0xe0,0xff,0x33,0x30,0x3f,0x00, + 0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x1f,0x7c,0xe0,0x07,0x33,0x30,0x3f,0x00, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x1f,0x00,0xff,0x07,0xcc,0xcf,0xc0,0xff, +]); +var img_8x8_rgba_atc_interpolated = new Uint8Array([ + 0xff,0x6a,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0x7c,0xe0,0x07,0xcc,0xcf,0xc0,0xff, + 0x00,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0x1f,0x00,0xe0,0xff,0x33,0x30,0x3f,0x00, + 0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x1f,0x7c,0xe0,0x07,0x33,0x30,0x3f,0x00, + 0x00,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0x1f,0x00,0xff,0x07,0xcc,0xcf,0xc0,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_ATC_WEBGL : 0x8C92, + COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL : 0x8C93, + COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL : 0x87EE, +}; +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_atc"); + if (!ext) { + testPassed("No WEBGL_compressed_texture_atc support -- this is legal"); + runSupportedTest(false); + } else { + testPassed("Successfully enabled WEBGL_compressed_texture_atc extension"); + + runSupportedTest(true); + runTestExtension(); + } +} + +function runSupportedTest(extensionEnabled) { + var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_atc"); + if (name !== undefined) { + if (extensionEnabled) { + testPassed("WEBGL_compressed_texture_atc listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_compressed_texture_atc listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_compressed_texture_atc not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_compressed_texture_atc 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_atc"); + + // 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 3 formats for WebGL 1.0 and 13 formats for WebGL 2.0. + if (contextVersion < 2) { + shouldBe("supportedFormats.length", "3"); + } else { + shouldBe("supportedFormats.length", "13"); + } + + // check that all 3 formats exist + for (var name in validFormats.length) { + formatExists(validFormats[name], supportedFormats); + } + + // Test each format + testATC_RGB(); + testATC_RGBA_Explicit(); + testATC_RGBA_Interpolated(); +} + +function testATC_RGB() { + var tests = [ + { width: 4, + height: 4, + channels: 3, + data: img_4x4_rgb_atc, + raw: img_4x4_rgba_raw, + format: ext.COMPRESSED_RGB_ATC_WEBGL + }, + { width: 8, + height: 8, + channels: 3, + data: img_8x8_rgb_atc, + raw: img_8x8_rgba_raw, + format: ext.COMPRESSED_RGB_ATC_WEBGL + } + ]; + testACTTextures(tests); +} + +function testATC_RGBA_Explicit() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_atc_explicit, + raw: img_4x4_rgba_raw, + format: ext.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_atc_explicit, + raw: img_8x8_rgba_raw, + format: ext.COMPRESSED_RGBA_ATC_EXPLICIT_ALPHA_WEBGL + } + ]; + testACTTextures(tests); +} + +function testATC_RGBA_Interpolated() { + var tests = [ + { width: 4, + height: 4, + channels: 4, + data: img_4x4_rgba_atc_interpolated, + raw: img_4x4_rgba_raw, + format: ext.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL + }, + { width: 8, + height: 8, + channels: 4, + data: img_8x8_rgba_atc_interpolated, + raw: img_8x8_rgba_raw, + format: ext.COMPRESSED_RGBA_ATC_INTERPOLATED_ALPHA_WEBGL + } + ]; + testACTTextures(tests); +} + +function testACTTextures(tests) { + debug("<hr/>"); + for (var ii = 0; ii < tests.length; ++ii) { + testACTTexture(tests[ii]); + } +} + +function testACTTexture(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 + 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"); + + gl.compressedTexImage2D(gl.TEXTURE_2D, -1, format, 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "cannot specify negative mip level"); + + // ATC Does not allow use of CompressedTexSubImage + gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed"); +} + +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 (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-etc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html new file mode 100644 index 000000000..56f5552d4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html @@ -0,0 +1,154 @@ +<!-- + +/* +** Copyright (c) 2015-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +</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 COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; +var COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; +var COMPRESSED_RGBA_S3TC_DXT3_EXT = 0x83F2; +var COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; +var COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; +var COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; +var ETC1_RGB8_OES = 0x8D64; +var COMPRESSED_R11_EAC = 0x9270; +var COMPRESSED_SIGNED_R11_EAC = 0x9271; +var COMPRESSED_RG11_EAC = 0x9272; +var COMPRESSED_SIGNED_RG11_EAC = 0x9273; +var COMPRESSED_RGB8_ETC2 = 0x9274; +var COMPRESSED_SRGB8_ETC2 = 0x9275; +var COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9276; +var COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 = 0x9277; +var COMPRESSED_RGBA8_ETC2_EAC = 0x9278; +var COMPRESSED_SRGB8_ALPHA8_ETC2_EAC = 0x9279; + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext(undefined, undefined); +var WEBGL_compressed_texture_etc; + +var formats = null; + +function runTest() { + if (!gl) { + testFailed("context does not exist"); + } else { + testPassed("context exists"); + + var tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + + var haveExt = gl.getSupportedExtensions().indexOf("WEBGL_compressed_texture_etc") >= 0; + WEBGL_compressed_texture_etc = gl.getExtension("WEBGL_compressed_texture_etc"); + + var isPositive = true; + + if (haveExt) { + if (WEBGL_compressed_texture_etc !== null) { + testPassed("WEBGL_compressed_texture_etc listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_compressed_texture_etc listed as supported but getExtension failed"); + return; + } + } else { + if (WEBGL_compressed_texture_etc !== null) { + testFailed("WEBGL_compressed_texture_etc listed as supported but getExtension failed"); + return; + } else { + testPassed("No WEBGL_compressed_texture_etc support -- this is legal"); + isPositive = false; + } + } + + if (isPositive) { + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_R11_EAC", "0x9270"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SIGNED_R11_EAC", "0x9271"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RG11_EAC", "0x9272"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SIGNED_RG11_EAC", "0x9273"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RGB8_ETC2", "0x9274"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SRGB8_ETC2", "0x9275"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", "0x9276"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", "0x9277"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_RGBA8_ETC2_EAC", "0x9278"); + shouldBe("WEBGL_compressed_texture_etc.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", "0x9279"); + } + + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT1_EXT, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_S3TC_DXT5_EXT, 4, 4, 0, new Uint8Array(16))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, ETC1_RGB8_OES, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA_PVRTC_4BPPV1_IMG, 8, 8, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexImage2D(gl.TEXTURE_3D, 0, COMPRESSED_R11_EAC, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, gl.INVALID_ENUM, "gl.compressedTexSubImage2D(gl.TEXTURE_3D, 0, 0, 0, 4, 4, COMPRESSED_R11_EAC, new Uint8Array(8))"); + + var expected = isPositive ? gl.NO_ERROR : gl.INVALID_ENUM; + var expectedSub = isPositive ? gl.NO_ERROR : [gl.INVALID_ENUM, gl.INVALID_OPERATION]; + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_R11_EAC, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, expectedSub, "gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 4, 4, COMPRESSED_R11_EAC, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SIGNED_R11_EAC, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RG11_EAC, 4, 4, 0, new Uint8Array(16))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SIGNED_RG11_EAC, 4, 4, 0, new Uint8Array(16))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB8_ETC2, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SRGB8_ETC2, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, 4, 4, 0, new Uint8Array(8))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_RGBA8_ETC2_EAC, 4, 4, 0, new Uint8Array(16))"); + wtu.shouldGenerateGLError(gl, expected, "gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, 4, 4, 0, new Uint8Array(16))"); + + wtu.shouldGenerateGLError(gl, gl.NO_ERROR, "formats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)"); + shouldBeNonNull("formats"); + shouldBe("formats.length", isPositive ? "10" : "0"); + + debug(""); + shouldThrow("gl.compressedTexImage2D(gl.TEXTURE_2D, 0, COMPRESSED_R11_EAC, 4, 4, 0, null)"); + shouldThrow("gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, COMPRESSED_R11_EAC, null)"); + shouldThrow("gl.compressedTexImage3D(gl.TEXTURE_2D_ARRAY, 0, COMPRESSED_R11_EAC, 4, 4, 4, 0, null)"); + shouldThrow("gl.compressedTexSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0, 0, 0, 0, 0, 0, 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-pvrtc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-pvrtc.html new file mode 100644 index 000000000..8c05b1f5c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-pvrtc.html @@ -0,0 +1,392 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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.drawQuad(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 000000000..4d71440b5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html @@ -0,0 +1,711 @@ +<!-- + +/* +** Copyright (c) 2012-2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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_s3tc_srgb 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_s3tc_srgb extension, if it is available."); + +debug(""); + +// This is the original image from webgl-compressed-texture-s3tc.html but +// scaled to 0x40..0xbf inclusive and alpha changed from 0x69 to 0x7f. +var img_4x4_rgba_raw = new Uint8Array([ + 0xbf,0x40,0x40,0x7f,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff, + 0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff, + 0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff, + 0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff, +]); +var img_4x4_rgb_dxt1 = new Uint8Array([ + 0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55, +]); +var img_4x4_rgba_dxt1 = new Uint8Array([ + 0xa8,0x4d,0x48,0xb2,0x13,0x10,0x15,0x00, +]); +var img_4x4_rgba_dxt3 = new Uint8Array([ + 0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55, +]); +var img_4x4_rgba_dxt5 = new Uint8Array([ + 0xff,0x7f,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55, +]); +var img_8x8_rgba_raw = new Uint8Array([ + 0xdf,0xa0,0xa0,0x7f,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff, + 0x40,0xbf,0x40,0x7f,0x40,0xbf,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff, + 0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0xbf,0x40,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0xbf,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff, + 0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff, + 0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff, + 0xbf,0x40,0xbf,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff, + 0x40,0xbf,0x40,0x7f,0x40,0xbf,0x40,0xff,0x40,0xbf,0x40,0xff,0xbf,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff, + 0xbf,0x40,0xbf,0x7f,0xbf,0x40,0xbf,0xff,0xbf,0x40,0xbf,0xff,0xbf,0x40,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff,0x40,0xbf,0xbf,0xff, +]); +var img_8x8_rgb_dxt1 = new Uint8Array([ + 0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00, +]); +var img_8x8_rgba_dxt1 = new Uint8Array([ + 0xa8,0x4d,0x48,0xb2,0x13,0x10,0x15,0x00,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00, +]); +var img_8x8_rgba_dxt3 = new Uint8Array([ + 0xf7,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00, +]); +var img_8x8_rgba_dxt5 = new Uint8Array([ + 0xff,0x7f,0x01,0x00,0x00,0x00,0x00,0x00,0x08,0xba,0xe8,0x45,0x44,0x45,0x40,0x55,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0xe8,0xbd,0x17,0x42,0x44,0x45,0x40,0x55,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0x17,0xba,0xe8,0x45,0x11,0x10,0x15,0x00,0xff,0xff,0x49,0x92,0x24,0x49,0x92,0x24,0xf7,0x45,0x17,0x42,0x11,0x10,0x15,0x00, +]); + +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_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 + runTestDisabled(); + + // 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"); + runSupportedTest(false); + } else { + testPassed("Successfully enabled WEBGL_compressed_texture_s3tc_srgb extension"); + + runSupportedTest(true); + runTestExtension(); + } +} + +function runSupportedTest(extensionEnabled) { + var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc_srgb"); + if (name !== undefined) { + if (extensionEnabled) { + testPassed("WEBGL_compressed_texture_s3tc_srgb listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_compressed_texture_s3tc_srgb listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_compressed_texture_s3tc_srgb not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_compressed_texture_s3tc_srgb 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_s3tc_srgb"); + + // 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 + testDXT1_SRGB(); + testDXT1_SRGB_ALPHA(); + testDXT3_SRGB_ALPHA(); + testDXT5_SRGB_ALPHA(); +} + +function testDXT1_SRGB() { + var tests = [ + { width: 4, + height: 4, + channels: 3, + data: img_4x4_rgb_dxt1, + format: ext.COMPRESSED_SRGB_S3TC_DXT1_EXT, + hasAlpha: false, + }, + { width: 8, + height: 8, + channels: 3, + data: img_8x8_rgb_dxt1, + format: ext.COMPRESSED_SRGB_S3TC_DXT1_EXT, + hasAlpha: false, + } + ]; + 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, + } + ]; + 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, + } + ]; + 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) { + function make565(src, offset) { + return src[offset + 0] + src[offset + 1] * 256; + } + function make8888From565(c) { + return [ + Math.floor(((c >> 11) & 0x1F) * 255 / 31), + Math.floor(((c >> 5) & 0x3F) * 255 / 63), + Math.floor(((c >> 0) & 0x1F) * 255 / 31), + 255 + ]; + } + function mix(mult, c0, c1, div) { + var r = []; + for (var ii = 0; ii < c0.length; ++ii) { + r[ii] = Math.floor((c0[ii] * mult + c1[ii]) / div); + } + 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 + Math.floor(xx / 2)]; + 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 = Math.floor(yy / 2) * 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 = ((8 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 7; + 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 = ((6 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 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 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 " + formatToString(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) { + 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, width, height, uncompressedData, data, format, "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, width, height, uncompressedData, data, format, "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) { + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid 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) { + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid 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, width, height, uncompressedData, data, format, "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, width, height, uncompressedData, data, format, "LINEAR"); + } + } +} + +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); +} + +// 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( + actualWidth, actualHeight, actualChannels, + dataWidth, dataHeight, expectedData, + testData, testFormat, filteringMode) { + var actual = new Uint8Array(actualWidth * actualHeight * 4); + gl.readPixels( + 0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "reading back pixels"); + + 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); + + // This threshold is required because the values we get back from the + // implementation don't exactly match our javascript implementation. + // This is probably due to allowances in the way sRGB interacts with S3TC. + var threshold = 2; + 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]) > threshold) { + failed = true; + var was = actual.slice(actualOffset, actualOffset + 4); + testFailed('at (' + xx + ', ' + yy + ') expected: ' + + expected + ' ± ' + threshold + '; 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.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc.html new file mode 100644 index 000000000..2f57e0a24 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc.html @@ -0,0 +1,736 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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_s3tc 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_s3tc extension, if it is available."); + +debug(""); + +var img_4x4_rgba_raw = new Uint8Array([ + 0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff, +]); +var img_4x4_rgb_dxt1 = new Uint8Array([ + 0xe0,0x07,0x00,0xf8,0x11,0x10,0x15,0x00, +]); +var img_4x4_rgba_dxt1 = new Uint8Array([ + 0xe0,0x07,0x00,0xf8,0x13,0x10,0x15,0x00, +]); +var img_4x4_rgba_dxt3 = new Uint8Array([ + 0xf6,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55, +]); +var img_4x4_rgba_dxt5 = new Uint8Array([ + 0xf6,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55, +]); +var img_8x8_rgba_raw = new Uint8Array([ + 0xff,0x00,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0x00,0x69,0x00,0xff,0x00,0xff,0x00,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0x00,0xff,0x69,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff, +]); +var img_8x8_rgb_dxt1 = new Uint8Array([ + 0xe0,0x07,0x00,0xf8,0x11,0x10,0x15,0x00,0x1f,0x00,0xe0,0xff,0x11,0x10,0x15,0x00,0xe0,0x07,0x1f,0xf8,0x44,0x45,0x40,0x55,0x1f,0x00,0xff,0x07,0x44,0x45,0x40,0x55, +]); +var img_8x8_rgba_dxt1 = new Uint8Array([ + 0xe0,0x07,0x00,0xf8,0x13,0x13,0x15,0x00,0x1f,0x00,0xe0,0xff,0x11,0x10,0x15,0x00,0xe0,0x07,0x1f,0xf8,0x44,0x45,0x43,0x57,0x1f,0x00,0xff,0x07,0x44,0x45,0x40,0x55, +]); +var img_8x8_rgba_dxt3 = new Uint8Array([ + 0xf6,0xff,0xf6,0xff,0xff,0xff,0xff,0xff,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe0,0xff,0x1f,0x00,0x44,0x45,0x40,0x55,0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xff,0x1f,0xf8,0xe0,0x07,0x11,0x10,0x15,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x1f,0x00,0x11,0x10,0x15,0x00, +]); +var img_8x8_rgba_dxt5 = new Uint8Array([ + 0xff,0x69,0x01,0x10,0x00,0x00,0x00,0x00,0x00,0xf8,0xe0,0x07,0x44,0x45,0x40,0x55,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0xff,0x1f,0x00,0x44,0x45,0x40,0x55,0xff,0x69,0x00,0x00,0x00,0x01,0x10,0x00,0x1f,0xf8,0xe0,0x07,0x11,0x10,0x15,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x07,0x1f,0x00,0x11,0x10,0x15,0x00, +]); + +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_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 + runTestDisabled(); + + // 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"); + runSupportedTest(false); + } else { + testPassed("Successfully enabled WEBGL_compressed_texture_s3tc extension"); + + runSupportedTest(true); + runTestExtension(); + } +} + +function runSupportedTest(extensionEnabled) { + var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_s3tc"); + if (name !== undefined) { + if (extensionEnabled) { + testPassed("WEBGL_compressed_texture_s3tc listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_compressed_texture_s3tc listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_compressed_texture_s3tc not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_compressed_texture_s3tc 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_s3tc"); + + // 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 + testDXT1_RGB(); + testDXT1_RGBA(); + testDXT3_RGBA(); + testDXT5_RGBA(); +} + +function testDXT1_RGB() { + var tests = [ + { width: 4, + height: 4, + channels: 3, + data: img_4x4_rgb_dxt1, + format: ext.COMPRESSED_RGB_S3TC_DXT1_EXT, + hasAlpha: false, + }, + { width: 8, + height: 8, + channels: 3, + data: img_8x8_rgb_dxt1, + format: ext.COMPRESSED_RGB_S3TC_DXT1_EXT, + hasAlpha: false, + subX0: 0, + subY0: 0, + subWidth: 4, + subHeight: 4, + subData: img_4x4_rgb_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. + } + ]; + 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) { + function make565(src, offset) { + return src[offset + 0] + src[offset + 1] * 256; + } + function make8888From565(c) { + return [ + Math.floor(((c >> 11) & 0x1F) * 255 / 31), + Math.floor(((c >> 5) & 0x3F) * 255 / 63), + Math.floor(((c >> 0) & 0x1F) * 255 / 31), + 255 + ]; + } + function mix(mult, c0, c1, div) { + var r = []; + for (var ii = 0; ii < c0.length; ++ii) { + r[ii] = Math.floor((c0[ii] * mult + c1[ii]) / div); + } + return r; + } + var isDXT1 = format == ext.COMPRESSED_RGB_S3TC_DXT1_EXT || + format == ext.COMPRESSED_RGBA_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_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 + Math.floor(xx / 2)]; + var alpha1 = (alpha0 >> ((xx % 2) * 4)) & 0xF; + alpha = alpha1 | (alpha1 << 4); + } + break; + case ext.COMPRESSED_RGBA_S3TC_DXT5_EXT: + { + var alpha0 = src[srcOffset + 0]; + var alpha1 = src[srcOffset + 1]; + var alphaOff = Math.floor(yy / 2) * 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 = ((8 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 7; + 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 = ((6 - alphaCode) * alpha0 + (alphaCode - 1) * alpha1) / 5; + break; + } + } + } + break; + default: + throw "bad format"; + } + 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; + return isDXT1 ? 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); + 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) { + 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 " + formatToString(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"); + + // 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) { + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid 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) { + gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "valid 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; + } + + 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 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, 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 * imageWidth + 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(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"; + insertImg(div, "expected", makeImage(width, height, expectedData, channels == 4)); + insertImg(div, "actual", makeImage(width, height, actual, channels == 4)); + 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[offset + 0], + expectedData[offset + 1], + expectedData[offset + 2], + (channels == 3 ? 255 : expectedData[offset + 3]) + ]; + for (var jj = 0; jj < 4; ++jj) { + if (actual[offset + jj] != expected[jj]) { + failed = true; + var was = actual[offset + 0].toString(); + for (var j = 1; j < 4; ++j) { + was += "," + actual[offset + 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-size-limit.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-size-limit.html new file mode 100644 index 000000000..47574aae4 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-size-limit.html @@ -0,0 +1,246 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +</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") +var canvas; + +function numLevelsFromSize(size) { + var levels = 0; + while ((size >> levels) > 0) { + ++levels; + } + return levels; +} + +// More formats can be added here when more texture compression extensions are enabled in WebGL. +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, +}; + +// format specific restrictions for COMPRESSED_RGB_S3TC_DXT1_EXT and COMPRESSED_RGBA_S3TC_DXT1_EXT +// on the byteLength of the ArrayBufferView, pixels +function func1 (width, height) +{ + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; +} + +// format specific restrictions for COMPRESSED_RGBA_S3TC_DXT3_EXT and COMPRESSED_RGBA_S3TC_DXT5_EXT +// on the byteLength of the ArrayBufferView, pixels +function func2 (width, height) +{ + return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16; +} + +var wtu = WebGLTestUtils; +var gl = wtu.create3DContext("example"); +var tests = [ + // More tests can be added here when more texture compression extensions are enabled in WebGL. + // Level 0 image width and height must be a multiple of the sizeStep. + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGB_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT3_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, + { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT5_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, +]; + +// Note: We expressly only use 2 textures because first a texture will be defined +// using all mip levels of 1 format, then for a moment it will have mixed formats which +// may uncover bugs. +var targets = [ + { target: gl.TEXTURE_2D, + maxSize: gl.getParameter(gl.MAX_TEXTURE_SIZE), + tex: gl.createTexture(), + targets: [gl.TEXTURE_2D] + }, + { target: gl.TEXTURE_CUBE_MAP, + maxSize: gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE), + tex: gl.createTexture(), + targets: [ + gl.TEXTURE_CUBE_MAP_POSITIVE_X, + gl.TEXTURE_CUBE_MAP_NEGATIVE_X, + gl.TEXTURE_CUBE_MAP_POSITIVE_Y, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, + gl.TEXTURE_CUBE_MAP_POSITIVE_Z, + gl.TEXTURE_CUBE_MAP_NEGATIVE_Z + ] + } +]; + +function getSharedArrayBufferSize() { + var sharedArrayBufferSize = 0; + for (var tt = 0; tt < tests.length; ++tt) { + var test = tests[tt]; + for (var trg = 0; trg < targets.length; ++trg) { + var t = targets[trg]; + var bufferSizeNeeded; + if (t.target === gl.TEXTURE_CUBE_MAP) { + var positiveTestSize = Math.min(2048, t.maxSize); + bufferSizeNeeded = test.func(positiveTestSize, positiveTestSize); + } else { + bufferSizeNeeded = test.func(t.maxSize, test.sizeStep); + } + if (bufferSizeNeeded > sharedArrayBufferSize) { + sharedArrayBufferSize = bufferSizeNeeded; + } + bufferSizeNeeded = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); + // ArrayBuffers can be at most 4GB (minus 1 byte) + if (bufferSizeNeeded > sharedArrayBufferSize && bufferSizeNeeded <= 4294967295) { + sharedArrayBufferSize = bufferSizeNeeded; + } + } + } + return sharedArrayBufferSize; +} + +// Share an ArrayBuffer among tests to avoid too many large allocations +var sharedArrayBuffer = new ArrayBuffer(getSharedArrayBufferSize()); + +gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); + +var trg = 0; +var tt = 0; +runNextTest(); + +function runNextTest() { + var t = targets[trg]; + + if (tt == 0) { + var tex = t.tex; + gl.bindTexture(t.target, tex); + + debug(""); + debug("max size for " + wtu.glEnumToString(gl, t.target) + ": " + t.maxSize); + } + + var test = tests[tt]; + testFormatType(t, test); + ++tt; + if (tt == tests.length) { + tt = 0; + ++trg; + if (trg == targets.length) { + finishTest(); + return; + } + } + wtu.waitForComposite(runNextTest); +} + +function testFormatType(t, test) { + var positiveTestSize = t.maxSize; + var positiveTestOtherDimension = test.sizeStep; + if (t.target === gl.TEXTURE_CUBE_MAP) { + // Can't always test the maximum size since that can cause OOM: + positiveTestSize = Math.min(2048, t.maxSize); + // Cube map textures need to be square: + positiveTestOtherDimension = positiveTestSize; + } + var positiveTestLevels = numLevelsFromSize(positiveTestSize); + var numLevels = numLevelsFromSize(t.maxSize); + debug(""); + debug("num levels: " + numLevels + ", levels used in positive test: " + positiveTestLevels); + + debug(""); + + // Query the extension and store globally so shouldBe can access it + var ext = wtu.getExtensionWithKnownPrefixes(gl, test.extension); + if (ext) { + + testPassed("Successfully enabled " + test.extension + " extension"); + + for (var j = 0; j < t.targets.length; ++j) { + var target = t.targets[j]; + debug(""); + debug(wtu.glEnumToString(gl, target)); + + // positive test + var size = positiveTestSize; + var otherDimension = positiveTestOtherDimension; + for (var i = 0; i < positiveTestLevels; i++) { + var pixels = new test.dataType(sharedArrayBuffer, 0, test.func(size, otherDimension)); + gl.compressedTexImage2D(target, i, test.format, size, otherDimension, 0, pixels); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture should generate NO_ERROR." + + "level is " + i + ", size is " + size + "x" + otherDimension); + size /= 2; + otherDimension /= 2; + if (otherDimension < 1) { + otherDimension = 1; + } + } + + var numLevels = numLevelsFromSize(t.maxSize); + + // out of bounds tests + // width and height out of bounds + var dataSize = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); + // this check assumes that each element is 1 byte + if (dataSize > sharedArrayBuffer.byteLength) { + testPassed("Unable to test texture larger than maximum size due to ArrayBuffer size limitations -- this is legal"); + } else { + var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, dataSize); + gl.compressedTexImage2D(target, 0, test.format, t.maxSize + test.sizeStep, t.maxSize + test.sizeStep, 0, pixelsNegativeTest1); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width or height out of bounds: should generate INVALID_VALUE." + + " level is 0, size is " + (t.maxSize + test.sizeStep) + "x" + (t.maxSize + test.sizeStep)); + } + // level out of bounds + var pixelsNegativeTest2 = new test.dataType(sharedArrayBuffer, 0, test.func(256, 256)); + gl.compressedTexImage2D(target, numLevels, test.format, 256, 256, 0, pixelsNegativeTest2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "level out of bounds: should generate INVALID_VALUE." + + " level is " + numLevels + ", size is 256x256"); + //width and height out of bounds for specified level + gl.compressedTexImage2D(target, numLevels - 1, test.format, 256, 256, 0, pixelsNegativeTest2); + wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width or height out of bounds for specified level: should generate INVALID_VALUE." + + " level is " + (numLevels - 1) + ", size is 256x256"); + } + } + else + testPassed("No " + test.extension + " extension support -- this is legal"); +} + +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 000000000..5470d8d08 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-renderer-info.html @@ -0,0 +1,125 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..e54b4e634 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-shaders.html @@ -0,0 +1,165 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..256ff6fd3 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-depth-texture.html @@ -0,0 +1,352 @@ +<!-- + +/* +** Copyright (c) 2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> +<!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 / u_resolution; + 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(); + } +} + +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() { + debug("Testing WEBGL_depth_texture"); + + var res = 8; + + // 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"), res, res); + + 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); + + 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] + ', 1, 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('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); + + // render the z-quad + gl.enable(gl.DEPTH_TEST); + gl.clearColor(1, 0, 0, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + dumpIt(gl, res, "--first--"); + + // render the depth texture. + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.clearColor(0, 0, 1, 1); + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.drawArrays(gl.TRIANGLES, 0, 6); + + var actualPixels = new Uint8Array(res * res * 4); + gl.readPixels(0, 0, res, res, gl.RGBA, gl.UNSIGNED_BYTE, actualPixels); + + dumpIt(gl, res, "--depth--"); + + // Check that each pixel's R value is less than that of the previous pixel + // in either direction. Basically verify we have a gradient. + var success = true; + for (var yy = 0; yy < res; ++yy) { + for (var xx = 0; xx < res; ++xx) { + var actual = (yy * res + xx) * 4; + var left = actual - 4; + var down = actual - res * 4; + + if (xx > 0) { + if (actualPixels[actual] <= actualPixels[left]) { + testFailed("actual(" + actualPixels[actual] + ") < left(" + actualPixels[left] + ")"); + success = false; + } + } + if (yy > 0) { + if (actualPixels[actual] <= actualPixels[down]) { + testFailed("actual(" + actualPixels[actual] + ") < down(" + actualPixels[down] + ")"); + success = false; + } + } + } + } + + // Check that bottom left corner is vastly different thatn top right. + if (actualPixels[(res * res - 1) * 4] - actualPixels[0] < 0xC0) { + testFailed("corners are not different enough"); + success = false; + } + + if (success) { + testPassed("depth texture rendered correctly."); + } + + // 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-framebuffer-unsupported.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-framebuffer-unsupported.html new file mode 100644 index 000000000..3f5c3a089 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-framebuffer-unsupported.html @@ -0,0 +1,147 @@ +<!-- + +/* +** Copyright (c) 2016 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..6c2c5471b --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-max-draw-buffers.html @@ -0,0 +1,139 @@ +<!-- + +/* +** Copyright (c) 2015 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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 000000000..d6f678d4c --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html @@ -0,0 +1,844 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!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> +</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; +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 output = document.getElementById("console"); +var gl = wtu.create3DContext(canvas); +var ext = null; +var programWithMaxDrawBuffersEqualOne = null; + +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"); + + 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, ["vshader", fsource], ["a_position"], 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) { + 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"); +} + +function testShaders(tests, sub) { + tests.forEach(function(test) { + var shaders = [wtu.getScript(test.shaders[0]), wtu.replaceParams(wtu.getScript(test.shaders[1]), sub)]; + var program = wtu.setupProgram(gl, shaders, ["a_position"], 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([ + { shaders: ["vshader", "fshaderMacroDisabled"], + msg: "GL_EXT_draw_buffers should not be defined in GLSL", + }, + { shaders: ["vshader", "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([ + { shaders: ["vshader", "fshaderMacroEnabled"], + msg: "GL_EXT_draw_buffers should be defined as 1 in GLSL", + }, + { shaders: ["vshader", "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 makeColorAttachmentArray(size) { + var array = [] + for (var ii = 0; ii < size; ++ii) { + array.push(gl.COLOR_ATTACHMENT0 + ii); + } + 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 = 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 = 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 maxColorAttachments = gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL); + var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS); + var maxUsable = Math.min(maxDrawingBuffers, maxColorAttachments, maxUniformVectors); + var half = Math.floor(maxUsable / 2); + var bufs = 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, ["vshader", "fshaderRed"], ["a_position"]); + var redProgramWithExtension = wtu.setupProgram(gl, ["vshader", "fshaderRedWithExtension"], ["a_position"]); + 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, + location: location, + 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 checkAttachmentsForColorFn = function(attachments, colorFn) { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.useProgram(checkProgram); + attachments.forEach(function(attachment, index) { + gl.bindTexture(gl.TEXTURE_2D, attachment.texture); + wtu.clearAndDrawUnitQuad(gl); + var expectedColor = colorFn(attachment, index); + var tolerance = 0; + expectedColor.forEach(function(v) { + if (v != 0 && v != 255) { + tolerance = 8; + } + }); + wtu.checkCanvas(gl, expectedColor, "attachment " + index + " should be " + expectedColor.toString(), tolerance); + }); + debug(""); + }; + + var checkAttachmentsForColor = function(attachments, color) { + checkAttachmentsForColorFn(attachments, function(attachment, index) { + return color || attachment.color; + }); + }; + + 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); + //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); + 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); + + 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."); + + 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); + + 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, ["vshader", "fshaderNoWrite"], ["a_position"]); + 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); + + 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); + + 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.drawUnitQuad(gl); + + checkAttachmentsForColorFn(attachments, function(attachment, index) { + return (index == 0) ? [255, 0, 0, 255] : [0, 255, 0, 255]; + }); + + 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); + + checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + if (maxUsable > 1) { + // First half of color buffers disable. + var bufs1 = makeColorAttachmentArray(maxUsable); + // Second half of color buffers disable. + var bufs2 = makeColorAttachmentArray(maxUsable); + // Color buffers with even indices disabled. + var bufs3 = makeColorAttachmentArray(maxUsable); + // Color buffers with odd indices disabled. + var bufs4 = 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); + + 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); + + 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); + 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); + + 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); + + 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); + + 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); + 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); + checkAttachmentsForColor(attachments, [255, 0, 0, 255]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + gl.useProgram(drawProgram); + wtu.drawUnitQuad(gl); + 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-shared-resources.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-shared-resources.html new file mode 100644 index 000000000..92bd09198 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-shared-resources.html @@ -0,0 +1,861 @@ +<!-- + +/* +** Copyright (c) 2013 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +--> + +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>WebGL WEBGL_Shared_Resources 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> +<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 vec4 u_color; +void main() { + gl_FragColor = u_color; +} +</script> +<style> +canvas { + border: 1px solid black; +} +</style> +<canvas id="canvas1" width="64" height="64"> </canvas> +<canvas id="canvas2" width="64" height="64"> </canvas> +<div id="description"></div> +<div id="console"></div> +<script> +"use strict"; +description(); + +var wtu = WebGLTestUtils; +var shouldGenerateGLError = wtu.shouldGenerateGLError; +var canvas1 = document.getElementById("canvas1"); +var gl = wtu.create3DContext(canvas1); +var gl2; +var ext = null; +var ext2; +var ext3; +var buf; +var elementBuf; +var tex; +var tex3; +var rb; +var fb; +var id; +var resource; +var shader; +var program; +var uniformLocation; +var acquiredFlag; +var shaderProgram; // acquired progam (never released) used for shader testing +var programShader; // acquired shader (never released) used for program testing + +if (!gl) { + testFailed("context does not exist"); +} else { + testPassed("context exists"); + + // Run tests with extension disabled + runTestDisabled(); + + // Query the extension and store globally so shouldBe can access it + ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_shared_resources"); + if (!ext) { + testPassed("No WEBGL_shared_resources support -- this is legal"); + runSupportedTest(false); + finishTest(); + } else { + testPassed("Successfully enabled WEBGL_shared_resources extension"); + + runSupportedTest(true); + runTestExtension(); + } +} + +function runSupportedTest(extensionEnabled) { + var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_shared_resources"); + if (name !== undefined) { + if (extensionEnabled) { + testPassed("WEBGL_shared_resources listed as supported and getExtension succeeded"); + } else { + testFailed("WEBGL_shared_resources listed as supported but getExtension failed"); + } + } else { + if (extensionEnabled) { + testFailed("WEBGL_shared_resources not listed as supported but getExtension succeeded"); + } else { + testPassed("WEBGL_shared_resources not listed as supported and getExtension failed -- this is legal"); + } + } +} + +function runTestDisabled() { + // There is no functionality accessable with this extension disabled. +} + +function makeFailCallback(msg) { + return function() { + testFailed(msg); + } +}; + + +function runTestExtension() { + var canvas2 = document.getElementById("canvas2"); + gl2 = wtu.create3DContext(canvas2, { group: ext.group }); + ext2 = wtu.getExtensionWithKnownPrefixes(gl2, "WEBGL_shared_resources"); + + // Runs an array of functions. Expects each function takes a callback + // it will call when finished. + var runSequence = function(steps) { + var stepNdx = 0; + var runNextStep = function() { + if (stepNdx < steps.length) { + steps[stepNdx++](runNextStep); + } + }; + runNextStep(); + }; + + var bufferTests = { + resourceType: "buffer", + + setupFunction: function() { + buf = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buf); + gl.bufferData(gl.ARRAY_BUFFER, 16, gl.STATIC_DRAW); + return buf; + }, + + bindFunction: function(buf) { + gl.bindBuffer(gl.ARRAY_BUFFER, buf); + }, + + implicitBindFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0)"); + }, + + modificationFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.bufferData(gl.ARRAY_BUFFER, 16, gl.STATIC_DRAW)"); + shouldGenerateGLError(gl, expectedError, "gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Uint8Array(4))"); + }, + + queryFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE)"); + }, + }; + + var programTests = { + resourceType: "program", + + setupFunction: function() { + // We need a valid a program with valid shaders to share because the only way to 'bind' + // a program is to call gl.useProgram and you can't call gl.useProgram on an invalid program. + program = wtu.setupProgram(gl, ["vshader", "fshader"]); + programShader = gl.getAttachedShaders(program)[0]; + uniformLocation = gl.getUniformLocation(program, "u_color"); + return program; + }, + + bindFunction: function(program) { + gl.useProgram(program); + }, + + implicitBindFunctions: function(expectedError) { + }, + + modificationFunctions: function(expectedError) { + if (expectedError == gl.NO_ERROR) { + // Need to get a new location because we may have re-linked. + uniformLocation = gl.getUniformLocation(program, 'u_color'); + } + shouldGenerateGLError(gl, expectedError, "gl.uniform4f(uniformLocation, 0, 1, 2, 3)"); + shouldGenerateGLError(gl, expectedError, "gl.detachShader(program, programShader)"); + shouldGenerateGLError(gl, expectedError, "gl.attachShader(program, programShader)"); + shouldGenerateGLError(gl, expectedError, "gl.bindAttribLocation(program, 0, 'foo')"); + shouldGenerateGLError(gl, expectedError, "gl.linkProgram(program)"); + }, + + queryFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.getProgramParameter(program, gl.LINK_STATUS)"); + shouldGenerateGLError(gl, expectedError, "gl.getProgramInfoLog(program)"); + shouldGenerateGLError(gl, expectedError, "gl.getAttachedShaders(program)"); + shouldGenerateGLError(gl, expectedError, "gl.getAttribLocation(program, 'foo')"); + shouldGenerateGLError(gl, expectedError, "gl.getUniformLocation(program, 'foo')"); + shouldGenerateGLError(gl, expectedError, "gl.getActiveAttrib(program, 0)"); + shouldGenerateGLError(gl, expectedError, "gl.getActiveUniform(program, 0)"); + shouldGenerateGLError(gl, expectedError, "gl.getUniform(program, uniformLocation)"); + }, + }; + + var renderbufferTests = { + resourceType: "renderbuffer", + + setupFunction: function() { + rb = gl.createRenderbuffer(); + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16); + fb = gl.createFramebuffer(); + return rb; + }, + + bindFunction: function(rb) { + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + }, + + implicitBindFunctions: function(expectedError) { + gl.bindFramebuffer(gl.FRAMEBUFFER, fb); + shouldGenerateGLError(gl, expectedError, "gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb)"); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + }, + + modificationFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16)"); + }, + + queryFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)"); + }, + }; + + + var shaderTests = { + resourceType: "shader", + + setupFunction: function() { + var shaderSource = "Hello World"; + shader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(shader, shaderSource); + shaderProgram = gl.createProgram(); + gl.attachShader(shaderProgram, shader); + return shader; + }, + + bindFunction: function(shader) { + gl.detachShader(shaderProgram, shader); // you can't attach if a shader is already attached. + gl.attachShader(shaderProgram, shader); + }, + + implicitBindFunctions: function(expectedError) { + }, + + modificationFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.shaderSource(shader, 'foo')"); + shouldGenerateGLError(gl, expectedError, "gl.compileShader(shader)"); + }, + + queryFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.getShaderParameter(shader, gl.COMPILE_STATUS)"); + shouldGenerateGLError(gl, expectedError, "gl.getShaderInfoLog(shader)"); + }, + }; + + var textureTests = { + resourceType: "texture", + + setupFunction: function() { + tex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, tex); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + return tex; + }, + + bindFunction: function(tex) { + gl.bindTexture(gl.TEXTURE_2D, tex); + }, + + implicitBindFunctions: function(expectedError) { + }, + + modificationFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)"); + shouldGenerateGLError(gl, expectedError, "gl.generateMipmap(gl.TEXTURE_2D)"); + shouldGenerateGLError(gl, expectedError, "gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null)"); + shouldGenerateGLError(gl, expectedError, "gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + shouldGenerateGLError(gl, expectedError, "gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 16, 16, 0)"); + shouldGenerateGLError(gl, expectedError, "gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 16, 16)"); + // TODO: Add compressed texture test if extension exists? + }, + + queryFunctions: function(expectedError) { + shouldGenerateGLError(gl, expectedError, "gl.getTexParameter(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S)"); + }, + }; + + var testResourceWithSingleContext = function(info, callback) { + var resourceType = info.resourceType; + + debug("") + debug("test " + resourceType); + var resource = info.setupFunction(); + ext.releaseSharedResource(resource); + + debug(""); + debug("test " + resourceType + " functions can not be called on released " + resourceType); + info.modificationFunctions(gl.INVALID_OPERATION); + info.implicitBindFunctions(gl.INVALID_OPERATION); + info.queryFunctions(gl.INVALID_OPERATION); + + debug(""); + debug("test acquring " + resourceType); + ext.acquireSharedResource(resource, ext.READ_ONLY, function() { + debug(""); + debug("test " + resourceType + " functions can not be called on READ_ONLY acquired " + resourceType + " that has not been bound"); + info.queryFunctions(gl.INVALID_OPERATION); + info.modificationFunctions(gl.INVALID_OPERATION); + + debug(""); + debug("test query " + resourceType + " functions can be called on READ_ONLY acquired " + resourceType + " that has been bound but not " + resourceType + " modification functions"); + info.bindFunction(resource); + info.queryFunctions(gl.NO_ERROR); + info.modificationFunctions(gl.INVALID_OPERATION); + + ext.releaseSharedResource(resource); + ext.acquireSharedResource(resource, ext.EXCLUSIVE, function() { + debug(""); + debug("test " + resourceType + " functions can not be called on EXCLUSIVE acquired " + resourceType + " that has not been bound"); + info.queryFunctions(gl.INVALID_OPERATION); + info.modificationFunctions(gl.INVALID_OPERATION); + + debug(""); + debug("test all " + resourceType + " functions can be called on EXCLUSIVE acquired " + resourceType + " that has been bound."); + info.bindFunction(resource) + info.queryFunctions(gl.NO_ERROR); + info.modificationFunctions(gl.NO_ERROR); + callback(); + }); + }); + }; + + var makeSingleContextResourceTest = function(info) { + return function(callback) { + testResourceWithSingleContext(info, callback); + }; + }; + + var testCommonResourceFeatures = function(info, callback) { + var type = info.resourceType.charAt(0).toUpperCase() + info.resourceType.slice(1); + acquiredFlag = false; + debug(""); + debug("test common features of " + type); + + resource = info.setupFunction(); + info.bindFunction(resource); + + debug("Test a deleted resource can still be acquired."); + var checkAcquireAfterDelete = function() { + debug("check Acquire After Delete"); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)"); +// TODO: fix spec then comment this in shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.bind" + type + "(gl." + target + ", resource)"); // You can't bind a deleted resource + shouldGenerateGLError(gl, gl.NO_ERROR, "ext.releaseSharedResource(resource)"); + callback(); + }; + + var checkDeleteExclusive = function() { + debug("check delete EXLUSIVE"); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)"); + info.bindFunction(resource); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no error deleting exclusively acquired resource"); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.delete" + type + "(resource)"); + ext.releaseSharedResource(resource); + ext.acquireSharedResource(resource, ext.EXCLUSIVE, checkAcquireAfterDelete); + }; + + var checkDeleteReadOnly = function() { + acquiredFlag = true; + debug("check delete READ_ONLY"); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)"); + info.bindFunction(resource); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no error bind read only acquired resource"); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.delete" + type + "(resource)"); // We're READ_ONLY so this should fail + ext.releaseSharedResource(resource); + ext.acquireSharedResource(resource, ext.EXCLUSIVE, checkDeleteExclusive); + }; + + debug("Test you can't have 2 outstanding requests for the same resource."); + ext.releaseSharedResource(resource); + ext.acquireSharedResource(resource, ext.READ_ONLY, checkDeleteReadOnly); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no error from 1st acquire request"); + ext.acquireSharedResource(resource, ext.READ_ONLY, checkDeleteReadOnly); + wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "should be INVALID_OPERATION from 2nd acquire request"); + + debug("Test acquire does not happen immediately on release (must exit current event)"); + shouldBeTrue("acquiredFlag === false"); + }; + + var makeCommonResourceFeatureTest = function(info) { + return function(callback) { + testCommonResourceFeatures(info, callback); + }; + }; + + // acquire multiple resources in multiple contexts. + var acquireMultipleResources = function(extensions, resources, mode, callback) { + var numNeeded = resources.length * extensions.length; + + var checkAcquire = function() { + --numNeeded; + if (numNeeded == 0) { + callback(); + } + }; + + resources.forEach(function(resource) { + extensions.forEach(function(ext) { + ext.acquireSharedResource(resource, mode, checkAcquire); + }); + }); + }; + + // release multiple resources in multiple contexts. + var releaseMultipleResources = function(extensions, resources) { + resources.forEach(function(resource) { + extensions.forEach(function(ext) { + ext.releaseSharedResource(resource); + }); + }); + }; + + var testRendering = function(callback) { + debug(""); + debug("test rendering"); + var positionLocation = 0; + var texcoordLocation = 1; + var program = wtu.setupSimpleTextureProgram(gl, positionLocation, texcoordLocation); + var buffers = wtu.setupUnitQuad(gl, positionLocation, texcoordLocation); + var rb = gl.createRenderbuffer(); + var fb = gl.createFramebuffer(); + + var elementBuf = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuf); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 3, 4, 5]), gl.STATIC_DRAW); + + var width = 16; + var height = 16; + + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height); + + var destTex = gl.createTexture(); + gl.bindTexture(gl.TEXTURE_2D, destTex); + 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.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + // It's not clear if gl.RGBA4 must be framebuffer complete. + var canCheckRenderbuffer = (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, destTex, 0); + wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup"); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE"); + + var tex = gl.createTexture(); + wtu.fillTexture(gl, tex, 1, 1, [0, 255, 0, 255]); + + if (!program) { + testFailed("could not link program"); + callback(); + return; + } + + var releaseAndAcquireResources = function(callback) { + var resources = [buffers[0], buffers[1], tex, program, elementBuf]; + releaseMultipleResources([ext], resources); + acquireMultipleResources([ext, ext2], resources, ext.READ_ONLY, callback); + }; + + var doRenderTest = function(callback) { + debug("Test 2 contexts can render with read only resources."); + + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[1]); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + gl.bindTexture(gl.TEXTURE_2D, tex); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + gl.useProgram(program); + + // Render to canvas1; + debug("render with context 1"); + wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 0, 0, 0]); + wtu.drawUnitQuad(gl); + wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255]); + + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)"); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuf); + gl.clear(gl.COLOR_BUFFER_BIT); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)"); + wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255]); + + // Render to canvas2; + debug("render with context 2"); + gl2.useProgram(program); + + gl2.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + gl2.enableVertexAttribArray(positionLocation); + gl2.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0); + gl2.bindBuffer(gl.ARRAY_BUFFER, buffers[1]); + gl2.enableVertexAttribArray(texcoordLocation); + gl2.vertexAttribPointer(texcoordLocation, 2, gl.FLOAT, false, 0, 0); + + gl2.bindTexture(gl.TEXTURE_2D, tex); + + wtu.checkCanvas(gl2, [0, 0, 0, 0]); + wtu.drawUnitQuad(gl2); + wtu.checkCanvas(gl2, [0, 255, 0, 255]); + + shouldGenerateGLError(gl2, gl2.INVALID_OPERATION, "gl2.drawElements(gl2.TRIANGLES, 6, gl2.UNSIGNED_SHORT, 0)"); + gl2.bindBuffer(gl2.ELEMENT_ARRAY_BUFFER, elementBuf); + gl2.clear(gl2.COLOR_BUFFER_BIT); + shouldGenerateGLError(gl2, gl2.NO_ERROR, "gl2.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0)"); + wtu.checkCanvas(gl2, [0, 255, 0, 255]); + + debug("Test you can't render to a framebuffer with a released texture"); + ext.releaseSharedResource(destTex); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)"); + + debug("Test you can't read from a framebuffer with a released texture"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + + ext.acquireSharedResource(destTex, ext.READ_ONLY, callback); + }; + + var checkReadOnlyTextureOnFramebuffer = function(callback) { + debug(""); + debug("test READ_ONLY texture attachment"); + debug("Test we fail of we haven't bound again"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, destTex); + gl.activeTexture(gl.TEXTURE0); + debug("Test we fail to draw because we're read only."); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)"); + + debug("Test we can read a READ_ONLY texture."); + shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE"); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + + checkRenderbuffer(callback); + }; + + var checkRenderbuffer = function(callback) { + if (canCheckRenderbuffer) { + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + wtu.drawUnitQuad(gl); + wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255]); + + debug("Test you can't render to a framebuffer with a released renderbuffer"); + ext.releaseSharedResource(rb); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)"); + + debug("Test you can't read from a framebuffer with a released renderbuffer"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + } + + ext.acquireSharedResource(rb, ext.READ_ONLY, callback); + }; + + var checkReadOnlyRenderbufferOnFramebuffer = function(callback) { + if (canCheckRenderbuffer) { + debug(""); + debug("test READ_ONLY renderbuffer attachment"); + debug("Test we fail of we haven't bound again"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + + gl.bindRenderbuffer(gl.RENDERBUFFER, rb); + debug("Test we fail to draw because we're read only."); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.clear(gl.COLOR_BUFFER_BIT)"); + shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + shouldBeTrue("gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE"); + + debug("Test we can read a READ_ONLY renderbuffer."); + shouldBeTrue("gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE"); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + } + + ext.releaseSharedResource(rb); + ext.acquireSharedResource(rb, ext.READ_ONLY, callback); + }; + + var checkRenderbufferBindsOnAttach = function(callback) { + if (canCheckRenderbuffer) { + debug(""); + debug("Test we fail of we haven't bound again"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + + debug("Test attaching a renderbuffer marks it as bound"); + gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); + + debug("Test we can read a READ_ONLY renderbuffer."); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + } + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, destTex, 0); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + ext.releaseSharedResource(destTex); + ext.acquireSharedResource(destTex, ext.READ_ONLY, callback); + }; + + var checkTextureBindsOnAttach = function(callback) { + debug(""); + debug("Test we fail of we haven't bound again"); + shouldGenerateGLError(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + + debug("Test attaching a texture marks it as bound"); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, destTex, 0); + + debug("Test we can read a READ_ONLY texture."); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4))"); + + callback(); + }; + + var checkCanNotRenderWithReleasedProgram = function(callback) { + debug(""); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + + ext.releaseSharedResource(program); + + debug("Test we can't draw with a released program."); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + + ext.acquireSharedResource(program, ext.EXCLUSIVE, callback); + ext2.releaseSharedResource(program); + }; + + var checkCanNotRenderWithReleasedBuffer = function(callback) { + debug(""); + gl.useProgram(program); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + + ext.releaseSharedResource(buffers[0]); + + debug("Test we can't draw with a released buffer."); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + + ext.acquireSharedResource(buffers[0], ext.READ_ONLY, callback); + }; + + var checkCanNotRenderWithReleasedTexture = function(callback) { + debug(""); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers[0]); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + + ext.releaseSharedResource(tex); + + debug("Test we can't draw with a released texture."); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + + ext.acquireSharedResource(tex, ext.READ_ONLY, callback); + }; + + var checkCanRenderWithReleasedShader = function(callback) { + gl.bindTexture(gl.TEXTURE_2D, tex); + var shaders = gl.getAttachedShaders(program); + ext.releaseSharedResource(shaders[0]); + + debug(""); + debug("Test we can draw with a released shader."); + shouldGenerateGLError(gl, gl.NO_ERROR, "gl.drawArrays(gl.TRIANGLES, 0, 6)"); + callback(); + }; + + runSequence( + [ + releaseAndAcquireResources, + doRenderTest, + checkReadOnlyTextureOnFramebuffer, + checkReadOnlyRenderbufferOnFramebuffer, + checkRenderbufferBindsOnAttach, + checkTextureBindsOnAttach, + checkCanNotRenderWithReleasedProgram, + checkCanNotRenderWithReleasedBuffer, + checkCanNotRenderWithReleasedTexture, + checkCanRenderWithReleasedShader, + callback, + ]); + }; + + var testMisc = function(callback) { + debug(""); + debug("Test you can't release a framebuffer"); + // TODO: It's not clear what should happen here to me. + //shouldThrow("ext.releaseSharedResource(fb)", "TypeError"); + + debug("") + debug("Test you can compare sharegroups"); + var gl3 = wtu.create3DContext(); + ext3 = wtu.getExtensionWithKnownPrefixes(gl3, "WEBGL_shared_resources"); + // TODO: comment in once this comparison works. + //shouldBeTrue("ext.group == ext2.group"); + shouldBeTrue("ext.group != ext3.group"); + + debug("Test you can't use resources from another different group."); + tex3 = gl3.createTexture(); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.releaseSharedResource(tex3)"); + shouldGenerateGLError(gl, gl.INVALID_OPERATION, "ext.acquireSharedResource(tex3, ext.READ_ONLY, makeFailCallback('should not be able to acquire resource from other context'))"); + + var failTest = function() { + testFailed("cancelled callback was called"); + }; + + var tex = gl.createTexture(); + debug("test releasing from the wrong context. Should be a no-op"); + shouldGenerateGLError(gl, gl.NO_ERROR, "ext2.releaseSharedResource(tex)"); + + id = ext2.acquireSharedResource(tex, ext.READ_ONLY, failTest); + debug("test cancelling a request for which an event has not been posted"); + ext2.cancelAcquireSharedResource(id); + + debug("test cancelling a request for which an event has already been posted"); + ext.releaseSharedResource(tex); + id = ext.acquireSharedResource(tex, ext.READ_ONLY, failTest); + ext.cancelAcquireSharedResource(id); + + debug("test cancelling on the wrong context's extension is ignored"); + id = ext2.acquireSharedResource(tex, ext.READ_ONLY, callback); + shouldGenerateGLError(gl, gl.NO_ERROR, 'ext.cancelAcquireSharedResource(id)'); + }; + + var testLostContext = function(callback) { + var WEBGL_lose_context = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_lose_context"); + if (!WEBGL_lose_context) { + callback(); + return; + } + + var tex = gl.createTexture(); + var tex2 = gl.createTexture(); + + var setupAcquire = function(callback) { + var callbacksNeeded = 3; + var waitForContextLostAndAcquire = function(e) { + if (e && e.preventDefault) { + e.preventDefault(); // allow context restore. + } + --callbacksNeeded; + if (callbacksNeeded == 0) { + callback(); + } + return false; + }; + + debug(""); + debug("Test extension still functions during context lost."); + acquireMultipleResources([ext2], [tex, tex2], ext2.READ_ONLY, waitForContextLostAndAcquire); + canvas1.addEventListener("webglcontextlost", waitForContextLostAndAcquire, false); + canvas2.addEventListener("webglcontextlost", waitForContextLostAndAcquire, false); + // Release one before context lost + ext.releaseSharedResource(tex); + WEBGL_lose_context.loseContext(); + // Release one after context lost + ext.releaseSharedResource(tex2); + + shouldBeTrue('gl.isContextLost()'); + shouldBeTrue('gl2.isContextLost()'); + }; + + var checkAcquireExt2 = function(callback) { + testPassed("was able to acquire resources during context lost"); + acquireMultipleResources([ext], [tex, tex2], ext.READ_ONLY, callback); + }; + + var checkAcquireExt = function(callback) { + testPassed("was able to request acquire resources during context lost"); + canvas1.addEventListener("webglcontextrestored", callback, false); + WEBGL_lose_context.restoreContext(); + }; + + var passTest = function(callback) { + testPassed("extension works during lost context"); + callback(); + }; + + runSequence( + [ + setupAcquire, + checkAcquireExt2, + checkAcquireExt, + passTest, + callback, + ]); + }; + + runSequence( + [ + makeCommonResourceFeatureTest(bufferTests), + makeCommonResourceFeatureTest(programTests), + makeCommonResourceFeatureTest(shaderTests), + makeCommonResourceFeatureTest(renderbufferTests), + makeCommonResourceFeatureTest(textureTests), + makeSingleContextResourceTest(bufferTests), + makeSingleContextResourceTest(programTests), + makeSingleContextResourceTest(renderbufferTests), + makeSingleContextResourceTest(shaderTests), + makeSingleContextResourceTest(textureTests), + testRendering, + testMisc, + testLostContext, + finishTest, + ]); + +} +var successfullyParsed = true; +</script> +</body> +</html> + |