webgl-compressed-texture-pvrtc.html (13603B)
1 <!-- 2 Copyright (c) 2019 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 10 <meta charset="utf-8"> 11 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 12 <script src="../../js/js-test-pre.js"></script> 13 <script src="../../js/webgl-test-utils.js"></script> 14 <title>WebGL WEBGL_compressed_texture_pvrtc Conformance Tests</title> 15 <style> 16 img { 17 border: 1px solid black; 18 margin-right: 1em; 19 } 20 .testimages { 21 } 22 23 .testimages br { 24 clear: both; 25 } 26 27 .testimages > div { 28 float: left; 29 margin: 1em; 30 } 31 </style> 32 </head> 33 <body> 34 <div id="description"></div> 35 <canvas id="canvas" width="8" height="8" style="width: 8px; height: 8px;"></canvas> 36 <div id="console"></div> 37 <script> 38 "use strict"; 39 description("This test verifies the functionality of the WEBGL_compressed_texture_pvrtc extension, if it is available."); 40 41 debug(""); 42 43 var pvrtc_4x4_2bpp = new Uint8Array([ 44 0x77, 0x22, 0x77, 0x22, 0xbb, 0x2b, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 45 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 46 ]); 47 48 var pvrtc_4x4_4bpp = new Uint8Array([ 49 0x1b, 0x1b, 0x1b, 0x1b, 0xba, 0x2b, 0x00, 0x80, 0x1b, 0x1b, 0x1b, 0x1b, 0xba, 0x2b, 0x00, 0x80, 50 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 ]); 52 53 var pvrtc_4x4_rgba_decoded = new Uint8Array([ 54 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xba, 0x44, 55 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb5, 0x44, 56 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb5, 0x44, 57 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xb8, 0x76, 0x76, 0x71, 0x8a, 0xbd, 0xbd, 0xb7, 0x44, 58 ]); 59 60 var pvrtc_4x4_rgb_decoded = new Uint8Array([ 61 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xba, 0xff, 62 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb5, 0xff, 63 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x43, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb5, 0xff, 64 0x00, 0x00, 0x00, 0xff, 0x46, 0x46, 0x46, 0xff, 0x76, 0x76, 0x71, 0xff, 0xbd, 0xbd, 0xb7, 0xff, 65 ]); 66 67 var wtu = WebGLTestUtils; 68 var contextVersion = wtu.getDefault3DContextVersion(); 69 var canvas = document.getElementById("canvas"); 70 var gl = wtu.create3DContext(canvas, {antialias: false}); 71 var program = wtu.setupTexturedQuad(gl); 72 var ext = null; 73 var vao = null; 74 var validFormats = { 75 COMPRESSED_RGB_PVRTC_4BPPV1_IMG : 0x8C00, 76 COMPRESSED_RGB_PVRTC_2BPPV1_IMG : 0x8C01, 77 COMPRESSED_RGBA_PVRTC_4BPPV1_IMG : 0x8C02, 78 COMPRESSED_RGBA_PVRTC_2BPPV1_IMG : 0x8C03, 79 }; 80 var name; 81 var supportedFormats; 82 83 if (!gl) { 84 testFailed("WebGL context does not exist"); 85 } else { 86 testPassed("WebGL context exists"); 87 88 // Run tests with extension disabled 89 runTestDisabled(); 90 91 // Query the extension and store globally so shouldBe can access it 92 ext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_pvrtc"); 93 if (!ext) { 94 testPassed("No WEBGL_compressed_texture_pvrtc support -- this is legal"); 95 runSupportedTest(false); 96 } else { 97 testPassed("Successfully enabled WEBGL_compressed_texture_pvrtc extension"); 98 99 runSupportedTest(true); 100 runTestExtension(); 101 } 102 } 103 104 function runSupportedTest(extensionEnabled) { 105 var name = wtu.getSupportedExtensionWithKnownPrefixes(gl, "WEBGL_compressed_texture_pvrtc"); 106 if (name !== undefined) { 107 if (extensionEnabled) { 108 testPassed("WEBGL_compressed_texture_pvrtc listed as supported and getExtension succeeded"); 109 } else { 110 testFailed("WEBGL_compressed_texture_pvrtc listed as supported but getExtension failed"); 111 } 112 } else { 113 if (extensionEnabled) { 114 testFailed("WEBGL_compressed_texture_pvrtc not listed as supported but getExtension succeeded"); 115 } else { 116 testPassed("WEBGL_compressed_texture_pvrtc not listed as supported and getExtension failed -- this is legal"); 117 } 118 } 119 } 120 121 122 function runTestDisabled() { 123 debug("Testing binding enum with extension disabled"); 124 125 supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); 126 shouldBe("supportedFormats", "[]"); 127 } 128 129 function formatExists(format, supportedFormats) { 130 for (var ii = 0; ii < supportedFormats.length; ++ii) { 131 if (format == supportedFormats[ii]) { 132 testPassed("supported format " + formatToString(format) + " is exists"); 133 return; 134 } 135 } 136 testFailed("supported format " + formatToString(format) + " does not exist"); 137 } 138 139 function formatToString(format) { 140 for (var p in ext) { 141 if (ext[p] == format) { 142 return p; 143 } 144 } 145 return "0x" + format.toString(16); 146 } 147 148 function runTestExtension() { 149 debug("Testing WEBGL_compressed_texture_pvrtc"); 150 151 // check that all format enums exist. 152 for (name in validFormats) { 153 var expected = "0x" + validFormats[name].toString(16); 154 var actual = "ext['" + name + "']"; 155 shouldBe(actual, expected); 156 } 157 158 supportedFormats = gl.getParameter(gl.COMPRESSED_TEXTURE_FORMATS); 159 // There should be exactly 4 formats for both WebGL 1.0 and WebGL 2.0. 160 shouldBe("supportedFormats.length", "4"); 161 162 // check that all 4 formats exist 163 for (var name in validFormats.length) { 164 formatExists(validFormats[name], supportedFormats); 165 } 166 167 // Test each format 168 testPVRTC_RGBA_2BPP(); 169 testPVRTC_RGB_2BPP(); 170 testPVRTC_RGBA_4BPP(); 171 testPVRTC_RGB_4BPP(); 172 } 173 174 function testPVRTC_RGBA_2BPP() { 175 var tests = [ 176 { width: 4, 177 height: 4, 178 channels: 4, 179 data: pvrtc_4x4_2bpp, 180 raw: pvrtc_4x4_rgba_decoded, 181 format: ext.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 182 } 183 ]; 184 testPVRTCTextures(tests); 185 } 186 187 function testPVRTC_RGB_2BPP() { 188 var tests = [ 189 { width: 4, 190 height: 4, 191 channels: 4, 192 data: pvrtc_4x4_2bpp, 193 raw: pvrtc_4x4_rgb_decoded, 194 format: ext.COMPRESSED_RGB_PVRTC_2BPPV1_IMG 195 } 196 ]; 197 testPVRTCTextures(tests); 198 } 199 200 function testPVRTC_RGBA_4BPP() { 201 var tests = [ 202 { width: 4, 203 height: 4, 204 channels: 4, 205 data: pvrtc_4x4_4bpp, 206 raw: pvrtc_4x4_rgba_decoded, 207 format: ext.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 208 } 209 ]; 210 testPVRTCTextures(tests); 211 } 212 213 function testPVRTC_RGB_4BPP() { 214 var tests = [ 215 { width: 4, 216 height: 4, 217 channels: 4, 218 data: pvrtc_4x4_4bpp, 219 raw: pvrtc_4x4_rgb_decoded, 220 format: ext.COMPRESSED_RGB_PVRTC_4BPPV1_IMG 221 } 222 ]; 223 testPVRTCTextures(tests); 224 } 225 226 function testPVRTCTextures(tests) { 227 debug("<hr/>"); 228 for (var ii = 0; ii < tests.length; ++ii) { 229 testPVRTCTexture(tests[ii]); 230 } 231 } 232 233 function testPVRTCTexture(test) { 234 var data = new Uint8Array(test.data); 235 var width = test.width; 236 var height = test.height; 237 var format = test.format; 238 var uncompressedData = test.raw; 239 240 canvas.width = width; 241 canvas.height = height; 242 gl.viewport(0, 0, width, height); 243 debug("testing " + formatToString(format) + " " + width + "x" + height); 244 245 var tex = gl.createTexture(); 246 gl.bindTexture(gl.TEXTURE_2D, tex); 247 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 248 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 249 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 250 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 251 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 0, data); 252 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture"); 253 gl.generateMipmap(gl.TEXTURE_2D); 254 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "trying to generate mipmaps from compressed texture"); 255 wtu.clearAndDrawUnitQuad(gl); 256 compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "NEAREST"); 257 // Test again with linear filtering. 258 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 259 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 260 wtu.clearAndDrawUnitQuad(gl); 261 compareRect(width, height, test.channels, width, height, uncompressedData, data, format, undefined, "LINEAR"); 262 263 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height, 1, data); 264 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "non 0 border"); 265 266 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 1, height, 0, data); 267 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); 268 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width - 2, height, 0, data); 269 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); 270 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 1, 0, data); 271 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); 272 gl.compressedTexImage2D(gl.TEXTURE_2D, 0, format, width, height - 2, 0, data); 273 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "invalid dimensions"); 274 275 gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height, format, data); 276 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "compressedTexSubImage2D allowed for reloading of complete textures"); 277 278 gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width - 2, height, format, data); 279 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); 280 gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height - 2, format, data); 281 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); 282 gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 2, 0, width - 2, height, format, data); 283 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); 284 gl.compressedTexSubImage2D(gl.TEXTURE_2D, 0, 0, 2, width, height - 2, format, data); 285 wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "compressedTexSubImage2D not allowed for partial texture updates"); 286 } 287 288 function insertImg(element, caption, img) { 289 var div = document.createElement("div"); 290 div.appendChild(img); 291 var label = document.createElement("div"); 292 label.appendChild(document.createTextNode(caption)); 293 div.appendChild(label); 294 element.appendChild(div); 295 } 296 297 function makeImage(imageWidth, imageHeight, dataWidth, data, alpha) { 298 var scale = 8; 299 var c = document.createElement("canvas"); 300 c.width = imageWidth * scale; 301 c.height = imageHeight * scale; 302 var ctx = c.getContext("2d"); 303 for (var yy = 0; yy < imageHeight; ++yy) { 304 for (var xx = 0; xx < imageWidth; ++xx) { 305 var offset = (yy * dataWidth + xx) * 4; 306 ctx.fillStyle = "rgba(" + 307 data[offset + 0] + "," + 308 data[offset + 1] + "," + 309 data[offset + 2] + "," + 310 (alpha ? data[offset + 3] / 255 : 1) + ")"; 311 ctx.fillRect(xx * scale, yy * scale, scale, scale); 312 } 313 } 314 return wtu.makeImageFromCanvas(c); 315 } 316 function compareRect( 317 actualWidth, actualHeight, actualChannels, 318 dataWidth, dataHeight, expectedData, 319 testData, testFormat, tolerance, filteringMode) { 320 if(typeof(tolerance) == 'undefined') { tolerance = 5; } 321 var actual = new Uint8Array(actualWidth * actualHeight * 4); 322 gl.readPixels( 323 0, 0, actualWidth, actualHeight, gl.RGBA, gl.UNSIGNED_BYTE, actual); 324 325 var div = document.createElement("div"); 326 div.className = "testimages"; 327 insertImg(div, "expected", makeImage( 328 actualWidth, actualHeight, dataWidth, expectedData, 329 actualChannels == 4)); 330 insertImg(div, "actual", makeImage( 331 actualWidth, actualHeight, actualWidth, actual, 332 actualChannels == 4)); 333 div.appendChild(document.createElement('br')); 334 document.getElementById("console").appendChild(div); 335 336 var failed = false; 337 for (var yy = 0; yy < actualHeight; ++yy) { 338 for (var xx = 0; xx < actualWidth; ++xx) { 339 var actualOffset = (yy * actualWidth + xx) * 4; 340 var expectedOffset = (yy * dataWidth + xx) * 4; 341 var expected = [ 342 expectedData[expectedOffset + 0], 343 expectedData[expectedOffset + 1], 344 expectedData[expectedOffset + 2], 345 (actualChannels == 3 ? 255 : expectedData[expectedOffset + 3]) 346 ]; 347 for (var jj = 0; jj < 4; ++jj) { 348 if (Math.abs(actual[actualOffset + jj] - expected[jj]) > tolerance) { 349 failed = true; 350 var was = actual[actualOffset + 0].toString(); 351 for (var j = 1; j < 4; ++j) { 352 was += "," + actual[actualOffset + j]; 353 } 354 testFailed('at (' + xx + ', ' + yy + 355 ') expected: ' + expected + ' was ' + was); 356 } 357 } 358 } 359 } 360 if (!failed) { 361 testPassed("texture rendered correctly with " + filteringMode + " filtering"); 362 } 363 } 364 365 debug(""); 366 var successfullyParsed = true; 367 </script> 368 <script src="../../js/js-test-post.js"></script> 369 370 </body> 371 </html>