summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance/extensions
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/conformance/extensions')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt39
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays-out-of-bounds.html77
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/angle-instanced-arrays.html652
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-blend-minmax.html246
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html326
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-frag-depth.html310
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html430
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-shader-texture-lod.html362
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-filter-anisotropic.html190
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/get-extension.html120
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-element-index-uint.html447
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-standard-derivatives.html421
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-linear.html53
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-canvas.html55
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image-data.html56
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-image.html55
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html60
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html291
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-linear.html56
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-canvas.html60
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image-data.html61
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-image.html60
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html65
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float.html496
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object-bufferData.html215
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-vertex-array-object.html738
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-atc.html431
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-etc.html154
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-pvrtc.html392
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc-srgb.html711
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-s3tc.html736
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-compressed-texture-size-limit.html246
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-renderer-info.html125
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-debug-shaders.html165
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-depth-texture.html352
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-framebuffer-unsupported.html147
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers-max-draw-buffers.html139
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-draw-buffers.html844
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-shared-resources.html861
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>
+