Source code
Revision control
Copy as Markdown
Other Tools
<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
<title>WebGL WEBGL_compressed_texture_pvrtc Conformance Tests</title>
<style>
img {
border: 1px solid black;
margin-right: 1em;
}
.testimages {
}
.testimages br {
clear: both;
}
.testimages > div {
float: left;
margin: 1em;
}
</style>
</head>
<body>
<div id="description"></div>
<canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas>
<div id="console"></div>
<script>
"use strict";
description("This test verifies the functionality of the WEBGL_compressed_texture_pvrtc extension, if it is available.");
debug("");
var pvrtc_4x4_2bpp = new Uint8Array([
0x77, 0x22, 0x77, 0x22, 0xbb, 0x2b, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
var pvrtc_4x4_4bpp = new Uint8Array([
0x1b, 0x1b, 0x1b, 0x1b, 0xba, 0x2b, 0x00, 0x80, 0x1b, 0x1b, 0x1b, 0x1b, 0xba, 0x2b, 0x00, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
var pvrtc_4x4_rgba_decoded = new Uint8Array([
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xba, 0x44,
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb5, 0x44,
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb5, 0x44,
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb7, 0x44,
]);
var pvrtc_4x4_rgb_decoded = new Uint8Array([
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xba, 0xff,
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb5, 0xff,
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb5, 0xff,
0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb7, 0xff,
]);
var wtu = WebGLTestUtils;
var contextVersion = wtu.getDefault3DContextVersion();
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas, {antialias: false});
var program = wtu.setupTexturedQuad(gl);
var ext = null;
var vao = null;
var validFormats = {
COMPRESSED_RGB_PVRTC_4BPPV1_IMG : 0x8C00,
COMPRESSED_RGB_PVRTC_2BPPV1_IMG : 0x8C01,
COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0x8C02,
COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : 0x8C03,
};
var name;
var supportedFormats;
if (!gl) {
testFailed("WebGL context does not exist");
} else {
testPassed("WebGL context exists");
// Run tests with extension disabled
runTestDisabled();
// Query the extension and store globally so shouldBe can access it
ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_pvrtc");
if (!ext) {
testPassed("No WEBGL_compressed_texture_pvrtc support -- this is legal");
runSupportedTest(false);
} else {
testPassed("Successfully enabled WEBGL_compressed_texture_pvrtc extension");
runSupportedTest(true);
runTestExtension();
}
}
function runSupportedTest(extensionEnabled) {
var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_pvrtc");
if (name !== undefined) {
if (extensionEnabled) {
testPassed("WEBGL_compressed_texture_pvrtc listed as supported and getExtension succeeded");
} else {
testFailed("WEBGL_compressed_texture_pvrtc listed as supported but getExtension failed");
}
} else {
if (extensionEnabled) {
testFailed("WEBGL_compressed_texture_pvrtc not listed as supported but getExtension succeeded");
} else {
testPassed("WEBGL_compressed_texture_pvrtc not listed as supported and getExtension failed -- this is legal");
}
}
}
function runTestDisabled() {
debug("Testing binding enum with extension disabled");
supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
shouldBe("supportedFormats", "[]");
}
function formatExists(format, supportedFormats) {
for (var ii = 0; ii < supportedFormats.length; ++ii) {
if (format == supportedFormats[ii]) {
testPassed("supported format " + formatToString(format) + " is exists");
return;
}
}
testFailed("supported format " + formatToString(format) + " does not exist");
}
function formatToString(format) {
for (var p in ext) {
if (ext[p] == format) {
return p;
}
}
return "0x" + format.toString(16);
}
function runTestExtension() {
debug("Testing WEBGL_compressed_texture_pvrtc");
// check that all format enums exist.
for (name in validFormats) {
var expected = "0x" + validFormats[name].toString(16);
var actual = "ext['" + name + "']";
shouldBe(actual, expected);
}
supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS);
// There should be exactly 4 formats for both WebGL 1.0 and WebGL 2.0.
shouldBe("supportedFormats.length", "4");
// check that all 4 formats exist
for (var name in validFormats.length) {
formatExists(validFormats[name], supportedFormats);
}
// Test each format
testPVRTC_RGBA_2BPP();
testPVRTC_RGB_2BPP();
testPVRTC_RGBA_4BPP();
testPVRTC_RGB_4BPP();
}
function testPVRTC_RGBA_2BPP() {
var tests = [
{ width: 4,
height: 4,
channels: 4,
data: pvrtc_4x4_2bpp,
raw: pvrtc_4x4_rgba_decoded,
format: ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG
}
];
testPVRTCTextures(tests);
}
function testPVRTC_RGB_2BPP() {
var tests = [
{ width: 4,
height: 4,
channels: 4,
data: pvrtc_4x4_2bpp,
raw: pvrtc_4x4_rgb_decoded,
format: ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG
}
];
testPVRTCTextures(tests);
}
function testPVRTC_RGBA_4BPP() {
var tests = [
{ width: 4,
height: 4,
channels: 4,
data: pvrtc_4x4_4bpp,
raw: pvrtc_4x4_rgba_decoded,
format: ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
}
];
testPVRTCTextures(tests);
}
function testPVRTC_RGB_4BPP() {
var tests = [
{ width: 4,
height: 4,
channels: 4,
data: pvrtc_4x4_4bpp,
raw: pvrtc_4x4_rgb_decoded,
format: ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG
}
];
testPVRTCTextures(tests);
}
function testPVRTCTextures(tests) {
debug("<hr/>");
for (var ii = 0; ii < tests.length; ++ii) {
testPVRTCTexture(tests[ii]);
}
}
function testPVRTCTexture(test) {
var data = new Uint8Array(test.data);
var width = test.width;
var height = test.height;
var format = test.format;
var uncompressedData = test.raw;
canvas.width = width;
canvas.height = height;
gl.viewport(0, 0, width, height);
debug("testing " + formatToString(format) + " " + width + "x" + height);
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture");
gl.generateMipmap(gl.TEXTURE_2D);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture");
wtu.clearAndDrawUnitQuad(gl);
compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "NEAREST");
// Test again with linear filtering.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
wtu.clearAndDrawUnitQuad(gl);
compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "LINEAR");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data);
wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "compressedTexSubImage2D allowed for reloading of complete textures");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 2, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 2, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 2, 0, width - 2, height, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates");
gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 2, width, height - 2, format, data);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates");
}
function insertImg(element, caption, img) {
var div = document.createElement("div");
div.appendChild(img);
var label = document.createElement("div");
label.appendChild(document.createTextNode(caption));
div.appendChild(label);
element.appendChild(div);
}
function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) {
var scale = 8;
var c = document.createElement("canvas");
c.width = imageWidth * scale;
c.height = imageHeight * scale;
var ctx = c.getContext("2d");
for (var yy = 0; yy < imageHeight; ++yy) {
for (var xx = 0; xx < imageWidth; ++xx) {
var offset = (yy * dataWidth + xx) * 4;
ctx.fillStyle = "rgba(" +
data[offset + 0] + "," +
data[offset + 1] + "," +
data[offset + 2] + "," +
(alpha ? data[offset + 3] / 255 : 1) + ")";
ctx.fillRect(xx * scale, yy * scale, scale, scale);
}
}
return wtu.makeImageFromCanvas(c);
}
function compareRect(
actualWidth, actualHeight, actualChannels,
dataWidth, dataHeight, expectedData,
testData, testFormat, tolerance, filteringMode) {
if(typeof(tolerance) == 'undefined') { tolerance = 5; }
var actual = new Uint8Array(actualWidth * actualHeight * 4);
gl.readPixels(
0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual);
var div = document.createElement("div");
div.className = "testimages";
insertImg(div, "expected", makeImage(
actualWidth, actualHeight, dataWidth, expectedData,
actualChannels == 4));
insertImg(div, "actual", makeImage(
actualWidth, actualHeight, actualWidth, actual,
actualChannels == 4));
div.appendChild(document.createElement('br'));
document.getElementById("console").appendChild(div);
var failed = false;
for (var yy = 0; yy < actualHeight; ++yy) {
for (var xx = 0; xx < actualWidth; ++xx) {
var actualOffset = (yy * actualWidth + xx) * 4;
var expectedOffset = (yy * dataWidth + xx) * 4;
var expected = [
expectedData[expectedOffset + 0],
expectedData[expectedOffset + 1],
expectedData[expectedOffset + 2],
(actualChannels == 3 ? 255 : expectedData[expectedOffset + 3])
];
for (var jj = 0; jj < 4; ++jj) {
if (Math.abs(actual[actualOffset + jj] - expected[jj]) > tolerance) {
failed = true;
var was = actual[actualOffset + 0].toString();
for (var j = 1; j < 4; ++j) {
was += "," + actual[actualOffset + j];
}
testFailed('at (' + xx + ', ' + yy +
') expected: ' + expected + ' was ' + was);
}
}
}
}
if (!failed) {
testPassed("texture rendered correctly with " + filteringMode + " filtering");
}
}
debug("");
var successfullyParsed = true;
</script>
<script src="../../js/js-test-post.js"></script>
</body>
</html>