Bug 943190 - Add test for WEBGL_compressed_texture_etc1. - r=kamidphish
authorJeff Gilbert <jgilbert@mozilla.com>
Mon, 10 Mar 2014 15:42:58 -0700
changeset 191101 91c5ede1cb080f788c03cb77a855199b9a5b8b7d
parent 191100 636a4f14af757b845d554d89d9799d87c683c802
child 191102 33dfbb13ab63ddf5a9677b731fd0c1abff498185
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskamidphish
bugs943190
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 943190 - Add test for WEBGL_compressed_texture_etc1. - r=kamidphish
content/canvas/test/webgl/conformance/extensions/00_test_list.txt
content/canvas/test/webgl/conformance/extensions/webgl-compressed-texture-etc1.html
content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
content/canvas/test/webgl/mochitest-conformance-files.ini
--- a/content/canvas/test/webgl/conformance/extensions/00_test_list.txt
+++ b/content/canvas/test/webgl/conformance/extensions/00_test_list.txt
@@ -1,9 +1,10 @@
 oes-standard-derivatives.html
 ext-texture-filter-anisotropic.html
 oes-texture-float.html
 oes-vertex-array-object.html
 webgl-debug-renderer-info.html
 webgl-debug-shaders.html
+webgl-compressed-texture-etc1.html
 webgl-compressed-texture-s3tc.html
 --min-version 1.0.2 webgl-depth-texture.html
 ext-sRGB.html
new file mode 100644
--- /dev/null
+++ b/content/canvas/test/webgl/conformance/extensions/webgl-compressed-texture-etc1.html
@@ -0,0 +1,506 @@
+<!--
+
+/*
+** 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="../../resources/js-test-pre.js"></script>
+<script src="../resources/webgl-test.js"></script>
+<script src="../resources/webgl-test-utils.js"></script>
+<title>WebGL WEBGL_compressed_texture_etc1 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_etc1 extension, if it is available.");
+
+debug("");
+
+var img_4x4_rgb_etc1 = new Uint8Array([
+    0x00, 0xc0, 0x00, 0xff, 0x07, 0x45, 0x07, 0x45
+]);
+var img_8x8_rgb_etc1 = new Uint8Array([
+    0x00, 0xff, 0x55, 0xfc, 0xff, 0xff, 0x07, 0x45,
+    0x11, 0x11, 0xff, 0xfc, 0xf8, 0xba, 0x07, 0x45,
+    0xee, 0x00, 0xee, 0xfc, 0x07, 0x45, 0x07, 0x45,
+    0x00, 0x90, 0xf8, 0x92, 0x07, 0x45, 0xff, 0xff,
+]);
+
+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 validFormats = {
+    COMPRESSED_RGB_ETC1_WEBGL : 0x8D64
+};
+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_etc1");
+    if (!ext) {
+        testPassed("No WEBGL_compressed_texture_etc1 support -- this is legal");
+        runSupportedTest(false);
+    } else {
+        testPassed("Successfully enabled WEBGL_compressed_texture_etc1 extension");
+
+        runSupportedTest(true);
+        runTestExtension();
+    }
+}
+
+function runSupportedTest(extensionEnabled) {
+    var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_etc1");
+    if (name !== undefined) {
+        if (extensionEnabled) {
+            testPassed("WEBGL_compressed_texture_etc1 listed as supported and getExtension succeeded");
+        } else {
+            testFailed("WEBGL_compressed_texture_etc1 listed as supported but getExtension failed");
+        }
+    } else {
+        if (extensionEnabled) {
+            testFailed("WEBGL_compressed_texture_etc1 not listed as supported but getExtension succeeded");
+        } else {
+            testPassed("WEBGL_compressed_texture_etc1 not listed as supported and getExtension failed -- this is legal");
+        }
+    }
+}
+
+
+function runTestDisabled() {
+    debug("Testing binding enum with extension disabled");
+
+    shouldBe('gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS)', '[]');
+}
+
+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_etc1");
+
+    // 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
+    shouldBe("supportedFormats.length", "1");
+
+    // check that all 4 formats exist
+    for (var name in validFormats.length) {
+        formatExists(validFormats[name], supportedFormats);
+    }
+
+    // Test each format
+    testETC1_RGB();
+}
+
+function testETC1_RGB() {
+    var tests = [
+        {   width: 4,
+            height: 4,
+            channels: 3,
+            data: img_4x4_rgb_etc1,
+            format: ext.COMPRESSED_RGB_ETC1_WEBGL
+        },
+        {   width: 8,
+            height: 8,
+            channels: 3,
+            data: img_8x8_rgb_etc1,
+            format: ext.COMPRESSED_RGB_ETC1_WEBGL
+        }
+    ];
+    testETCTextures(tests);
+}
+
+function testETCTextures(tests) {
+    for (var ii = 0; ii < tests.length; ++ii) {
+        debug("<hr/>");
+        testETCTexture(tests[ii]);
+    }
+}
+
+function offset_color(c, o) {
+    return [
+        Math.min(Math.max(0, c[0] + o), 255),
+        Math.min(Math.max(0, c[1] + o), 255),
+        Math.min(Math.max(0, c[2] + o), 255),
+        c[3]
+    ];
+}
+
+function uncompressETC1Block(destBuffer, destX, destY, destWidth, src) {
+    'use strict';
+    var xx, yy, basecols;
+    var _deltatable = [ 0, 1, 2, 3, -4, -3, -2, -1 ];
+    var _modtable = [
+        [ 2,    8,  -2,   -8 ],
+        [ 5,   17,  -5,  -17 ],
+        [ 9,   29,  -9,  -29 ],
+        [ 13,  42, -13,  -42 ],
+        [ 18,  60, -18,  -60 ],
+        [ 24,  80, -24,  -80 ],
+        [ 33, 106, -33, -106 ],
+        [ 47, 183, -47, -183 ]
+    ];
+    var _sl = [
+        0x00, 0x01, 0x04, 0x05,
+        0x10, 0x11, 0x14, 0x15,
+        0x40, 0x41, 0x44, 0x45,
+        0x50, 0x51, 0x54, 0x55
+    ];
+    var _sh = [
+        0x00, 0x02, 0x08, 0x0a,
+        0x20, 0x22, 0x28, 0x2a,
+        0x80, 0x82, 0x88, 0x8a,
+        0xa0, 0xa2, 0xa8, 0xaa
+    ];
+
+    function extend_4to8bits(r, g, b) {
+        return [
+            (r & 0xf0) | ((r >> 4) & 0x0f),
+            (g & 0xf0) | ((g >> 4) & 0x0f),
+            (b & 0xf0) | ((b >> 4) & 0x0f),
+            255
+        ];
+    }
+
+    function extend_5to8bits(r, g, b) {
+        return [
+            (r & 0xf8) | ((r >> 5) & 0x07),
+            (g & 0xf8) | ((g >> 5) & 0x07),
+            (b & 0xf8) | ((b >> 5) & 0x07),
+            255
+        ];
+    }
+
+    function base_colors(src, mode) {
+        var col_1, col_2, didx, d;
+        if (mode === 'I') {
+            col_1 = extend_4to8bits(src[0], src[1], src[2]);
+            col_2 = extend_4to8bits(src[0] << 4, src[1] << 4, src[2] << 4);
+            return [ col_1, col_2 ];
+        }
+
+        if (mode === 'D') {
+            col_1 = extend_5to8bits(src[0], src[1], src[2]);
+            col_2 = extend_5to8bits(src[0] + 8 * _deltatable[(src[0] & 0x7)],
+                                    src[1] + 8 * _deltatable[(src[1] & 0x7)],
+                                    src[2] + 8 * _deltatable[(src[2] & 0x7)]);
+            return [ col_1, col_2 ];
+        }
+
+        return [];
+    }
+
+    function mode(src) {
+        return (src[3] & 0x2) ? 'D' : 'I';
+    }
+
+    function flip(src) {
+        return (src[3] & 0x1) === 0x1;
+    }
+
+    function subblock_modtable(src, sb) {
+        var shift = (sb ? 2 : 5);
+        var idx = (src[3] >> shift) & 0x7;
+        return _modtable[idx];
+    }
+
+    function interleave_table_indices(src) {
+        var result =
+                (_sl[src[7]         & 0xf] | _sh[src[5]        & 0xf]) |
+                ((_sl[(src[7] >> 4) & 0xf] | _sh[(src[5] >> 4) & 0xf]) << 8) |
+                ((_sl[src[6]        & 0xf] | _sh[src[4]        & 0xf]) << 16) |
+                ((_sl[(src[6] >> 4) & 0xf] | _sh[(src[4] >> 4) & 0xf]) << 24);
+        return result;
+    }
+
+    function subblock(n, flip) {
+        var mask = flip ? 0x2 : 0x8;
+        return (n & mask) ? 1 : 0;
+    }
+
+    var m = mode(src);
+    basecols = base_colors(src, m);
+
+    var alpha = 255;
+    var flipbit = flip(src);
+    var table_indices = interleave_table_indices(src);
+
+    var n = 0;
+    for (xx = 0; xx < 4; ++xx) {
+        for (yy = 0; yy < 4; ++yy) {
+            var dstOff = ((destY + yy) * destWidth + destX + xx) * 4;
+
+            var sb = subblock(n, flipbit);
+            var mod = subblock_modtable(src, sb);
+            var offset = mod[(table_indices & 0x3)];
+            var col = offset_color(basecols[sb], offset);
+
+            destBuffer[dstOff]     = col[0];
+            destBuffer[dstOff + 1] = col[1];
+            destBuffer[dstOff + 2] = col[2];
+            destBuffer[dstOff + 3] = alpha;
+            table_indices >>= 2;
+            n++;
+        }
+    }
+}
+
+function uncompressETC1(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 = 8;
+    for (var yy = 0; yy < blocksDown; ++yy) {
+        for (var xx = 0; xx < blocksAcross; ++xx) {
+            var srcOffset = (yy * blocksAcross + xx) * blockSize;
+            var srcblk = data.subarray(srcOffset, srcOffset + blockSize);
+            uncompressETC1Block(dest, xx * 4, yy * 4, width, srcblk);
+        }
+    }
+    return dest;
+}
+
+function testETCTexture(test) {
+    var data = new Uint8Array(test.data);
+    var width = test.width;
+    var height = test.height;
+    var format = test.format;
+
+    var uncompressedData = uncompressETC1(width, height, data, format);
+
+    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);
+    glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
+    gl.generateMipmap(gl.TEXTURE_2D);
+    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);
+
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data);
+    glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border");
+
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width + 4, height, 0, data);
+    glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height + 4, 0, data);
+    glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 4, height, 0, data);
+    glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 4, 0, data);
+    glErrorShouldBe(gl, gl.INVALID_VALUE, "data size does not match dimensions");
+
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data);
+    glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data);
+    glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data);
+    glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data);
+    glErrorShouldBe(gl, gl.NO_ERROR, "non multiple-of-4 supported");
+
+    if (width == 4) {
+      gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 1, height, 0, data);
+      glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
+      gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, 2, height, 0, data);
+      glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
+    }
+    if (height == 4) {
+      gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 1, 0, data);
+      glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
+      gl.compressedTexImage2D(gl.TEXTURE_2D, 1, format, width, 2, 0, data);
+      glErrorShouldBe(gl, gl.NO_ERROR, "valid dimensions for level > 0");
+    }
+
+    // Reupload the complete texture before SubImage tests.
+    gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data);
+    glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
+
+    /* OES_compressed_ETC1_RGB8_texture:
+     *   INVALID_OPERATION is generated by CompressedTexSubImage2D,
+     *   TexSubImage2D, or CopyTexSubImage2D if the texture image
+     *   <level> bound to <target> has internal format ETC1_RGB8_OES.
+     */
+    gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data);
+    glErrorShouldBe(gl, gl.INVALID_OPERATION, "ETC1 should not support compressedTexSubImage2D.");
+}
+
+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);
+        }
+    }
+    var img = document.createElement("img");
+    img.src = c.toDataURL();
+    return img;
+}
+
+function compareRect(actualWidth, actualHeight, actualChannels,
+                     dataWidth, dataHeight, expectedData,
+                     testData, testFormat)
+{
+    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])
+            ];
+
+            if (actual[actualOffset + 0] != expected[0] ||
+                actual[actualOffset + 1] != expected[1] ||
+                actual[actualOffset + 2] != expected[2] ||
+                actual[actualOffset + 3] != expected[3])
+            {
+                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");
+    }
+}
+
+debug("");
+var successfullyParsed = true;
+</script>
+<script>finishTest();</script>
+
+</body>
+</html>
--- a/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
+++ b/content/canvas/test/webgl/conformance/resources/webgl-test-utils.js
@@ -213,16 +213,50 @@ var setupSimpleTextureProgram = function
  *      created.
  */
 var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
   return setupUnitQuadWithTexCoords(gl, [ 0.0, 0.0 ], [ 1.0, 1.0 ],
                                     opt_positionLocation, opt_texcoordLocation);
 };
 
 /**
+ * Draws a previously setupUnitQuad.
+ * @param {!WebGLContext} gl The WebGLContext to use.
+ */
+var drawUnitQuad = function(gl) {
+  gl.drawArrays(gl.TRIANGLES, 0, 6);
+};
+
+/**
+ * Clears then Draws a previously setupUnitQuad.
+ * @param {!WebGLContext} gl The WebGLContext to use.
+ * @param {!Array.<number>} opt_color The color to fill clear with before
+ * drawing. A 4 element array where each element is in the range 0 to
+ * 255. Default [255, 255, 255, 255]
+ */
+var clearAndDrawUnitQuad = function(gl, opt_color) {
+  opt_color = opt_color || [255, 255, 255, 255];
+
+  // Save and restore.
+  var prevClearColor = gl.getParameter(gl.COLOR_CLEAR_VALUE);
+
+  gl.clearColor(opt_color[0] / 255,
+                opt_color[1] / 255,
+                opt_color[2] / 255,
+                opt_color[3] / 255);
+  gl.clear(gl.COLOR_BUFFER_BIT);
+  drawUnitQuad(gl);
+
+  gl.clearColor(prevClearColor[0],
+                prevClearColor[1],
+                prevClearColor[2],
+                prevClearColor[3]);
+};
+
+/**
  * Creates buffers for a textured unit quad with specified lower left
  * and upper right texture coordinates, and attaches them to vertex
  * attribs.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {!Array.<number>} lowerLeftTexCoords The texture coordinates for the lower left corner.
  * @param {!Array.<number>} upperRightTexCoords The texture coordinates for the upper right corner.
  * @param {number} opt_positionLocation The attrib location for position.
  * @param {number} opt_texcoordLocation The attrib location for texture coords.
@@ -642,17 +676,17 @@ var glErrorShouldBe = function(gl, glErr
                 getGLErrorAsString(gl, glError) + " : " + opt_msg);
   }
 };
 
 /**
  * Links a WebGL program, throws if there are errors.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {!WebGLProgram} program The WebGLProgram to link.
- * @param {function(string): void) opt_errorCallback callback for errors. 
+ * @param {function(string): void) opt_errorCallback callback for errors.
  */
 var linkProgram = function(gl, program, opt_errorCallback) {
   errFn = opt_errorCallback || testFailed;
   // Link the program
   gl.linkProgram(program);
 
   // Check the link status
   var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
@@ -958,18 +992,18 @@ var readFileList = function(url) {
   }
   return files;
 };
 
 /**
  * Loads a shader.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {string} shaderSource The shader source.
- * @param {number} shaderType The type of shader. 
- * @param {function(string): void) opt_errorCallback callback for errors. 
+ * @param {number} shaderType The type of shader.
+ * @param {function(string): void) opt_errorCallback callback for errors.
  * @return {!WebGLShader} The created shader.
  */
 var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
   var errFn = opt_errorCallback || error;
   // Create the shader object
   var shader = gl.createShader(shaderType);
   if (shader == null) {
     errFn("*** Error: unable to create shader '"+shaderSource+"'");
@@ -1000,17 +1034,17 @@ var loadShader = function(gl, shaderSour
   return shader;
 }
 
 /**
  * Loads a shader from a URL.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {file} file The URL of the shader source.
  * @param {number} type The type of shader.
- * @param {function(string): void) opt_errorCallback callback for errors. 
+ * @param {function(string): void) opt_errorCallback callback for errors.
  * @return {!WebGLShader} The created shader.
  */
 var loadShaderFromFile = function(gl, file, type, opt_errorCallback) {
   var shaderSource = readFile(file);
   return loadShader(gl, shaderSource, type, opt_errorCallback);
 };
 
 /**
@@ -1025,17 +1059,17 @@ var getScript = function(scriptId) {
 };
 
 /**
  * Loads a shader from a script tag.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {string} scriptId The id of the script tag.
  * @param {number} opt_shaderType The type of shader. If not passed in it will
  *     be derived from the type of the script tag.
- * @param {function(string): void) opt_errorCallback callback for errors. 
+ * @param {function(string): void) opt_errorCallback callback for errors.
  * @return {!WebGLShader} The created shader.
  */
 var loadShaderFromScript = function(
     gl, scriptId, opt_shaderType, opt_errorCallback) {
   var shaderSource = "";
   var shaderType;
   var shaderScript = document.getElementById(scriptId);
   if (!shaderScript) {
@@ -1067,17 +1101,17 @@ var loadStandardProgram = function(gl) {
   return program;
 };
 
 /**
  * Loads shaders from files, creates a program, attaches the shaders and links.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {string} vertexShaderPath The URL of the vertex shader.
  * @param {string} fragmentShaderPath The URL of the fragment shader.
- * @param {function(string): void) opt_errorCallback callback for errors. 
+ * @param {function(string): void) opt_errorCallback callback for errors.
  * @return {!WebGLProgram} The created program.
  */
 var loadProgramFromFile = function(
     gl, vertexShaderPath, fragmentShaderPath, opt_errorCallback) {
   var program = gl.createProgram();
   gl.attachShader(
       program,
       loadShaderFromFile(
@@ -1093,17 +1127,17 @@ var loadProgramFromFile = function(
 /**
  * Loads shaders from script tags, creates a program, attaches the shaders and
  * links.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {string} vertexScriptId The id of the script tag that contains the
  *        vertex shader.
  * @param {string} fragmentScriptId The id of the script tag that contains the
  *        fragment shader.
- * @param {function(string): void) opt_errorCallback callback for errors. 
+ * @param {function(string): void) opt_errorCallback callback for errors.
  * @return {!WebGLProgram} The created program.
  */
 var loadProgramFromScript = function loadProgramFromScript(
     gl, vertexScriptId, fragmentScriptId, opt_errorCallback) {
   var program = gl.createProgram();
   gl.attachShader(
       program,
       loadShaderFromScript(
@@ -1117,17 +1151,17 @@ var loadProgramFromScript = function loa
 };
 
 /**
  * Loads shaders from source, creates a program, attaches the shaders and
  * links.
  * @param {!WebGLContext} gl The WebGLContext to use.
  * @param {string} vertexShader The vertex shader.
  * @param {string} fragmentShader The fragment shader.
- * @param {function(string): void) opt_errorCallback callback for errors. 
+ * @param {function(string): void) opt_errorCallback callback for errors.
  * @return {!WebGLProgram} The created program.
  */
 var loadProgram = function(
     gl, vertexShader, fragmentShader, opt_errorCallback) {
   var program = gl.createProgram();
   gl.attachShader(
       program,
       loadShader(
@@ -1308,23 +1342,25 @@ var addShaderSource = function(element, 
     }, false);
   div.appendChild(l);
   div.appendChild(s);
   element.appendChild(div);
 }
 
 return {
   addShaderSource: addShaderSource,
+  clearAndDrawUnitQuad : clearAndDrawUnitQuad,
   create3DContext: create3DContext,
   create3DContextWithWrapperThatThrowsOnGLError:
     create3DContextWithWrapperThatThrowsOnGLError,
   checkCanvas: checkCanvas,
   checkCanvasRect: checkCanvasRect,
   createColoredTexture: createColoredTexture,
   drawQuad: drawQuad,
+  drawUnitQuad: drawUnitQuad,
   endsWith: endsWith,
   getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
   getFileListAsync: getFileListAsync,
   getLastError: getLastError,
   getScript: getScript,
   getSupportedExtensionWithKnownPrefixes: getSupportedExtensionWithKnownPrefixes,
   getUrlArguments: getUrlArguments,
   glEnumToString: glEnumToString,
--- a/content/canvas/test/webgl/mochitest-conformance-files.ini
+++ b/content/canvas/test/webgl/mochitest-conformance-files.ini
@@ -37,16 +37,17 @@ support-files =
   conformance/context/premultiplyalpha-test.html
   conformance/context/resource-sharing-test.html
   conformance/extensions/00_test_list.txt
   conformance/extensions/ext-sRGB.html
   conformance/extensions/ext-texture-filter-anisotropic.html
   conformance/extensions/oes-standard-derivatives.html
   conformance/extensions/oes-texture-float.html
   conformance/extensions/oes-vertex-array-object.html
+  conformance/extensions/webgl-compressed-texture-etc1.html
   conformance/extensions/webgl-compressed-texture-s3tc.html
   conformance/extensions/webgl-debug-renderer-info.html
   conformance/extensions/webgl-debug-shaders.html
   conformance/extensions/webgl-depth-texture.html
   conformance/glsl/00_test_list.txt
   conformance/glsl/functions/00_test_list.txt
   conformance/glsl/functions/glsl-function-abs.html
   conformance/glsl/functions/glsl-function-acos.html