Bug 943190 - Add test for WEBGL_compressed_texture_etc1. - r=kamidphish

This commit is contained in:
Jeff Gilbert 2014-03-10 15:42:58 -07:00
parent 7e236f76f8
commit cfbd1710d1
4 changed files with 552 additions and 8 deletions

View File

@ -4,6 +4,7 @@ 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

View File

@ -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>

View File

@ -217,6 +217,40 @@ var setupUnitQuad = function(gl, opt_positionLocation, opt_texcoordLocation) {
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
@ -647,7 +681,7 @@ var glErrorShouldBe = function(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;
@ -963,8 +997,8 @@ var readFileList = function(url) {
* 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) {
@ -1005,7 +1039,7 @@ var loadShader = function(gl, shaderSource, shaderType, opt_errorCallback) {
* @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) {
@ -1030,7 +1064,7 @@ var getScript = function(scriptId) {
* @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(
@ -1072,7 +1106,7 @@ var loadStandardProgram = function(gl) {
* @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(
@ -1098,7 +1132,7 @@ var loadProgramFromFile = function(
* 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(
@ -1122,7 +1156,7 @@ var loadProgramFromScript = function loadProgramFromScript(
* @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(
@ -1313,6 +1347,7 @@ var addShaderSource = function(element, label, source) {
return {
addShaderSource: addShaderSource,
clearAndDrawUnitQuad : clearAndDrawUnitQuad,
create3DContext: create3DContext,
create3DContextWithWrapperThatThrowsOnGLError:
create3DContextWithWrapperThatThrowsOnGLError,
@ -1320,6 +1355,7 @@ return {
checkCanvasRect: checkCanvasRect,
createColoredTexture: createColoredTexture,
drawQuad: drawQuad,
drawUnitQuad: drawUnitQuad,
endsWith: endsWith,
getExtensionWithKnownPrefixes: getExtensionWithKnownPrefixes,
getFileListAsync: getFileListAsync,

View File

@ -42,6 +42,7 @@ support-files =
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