webgl-compressed-texture-size-limit.js (9581B)
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 'use strict'; 8 9 var runCompressedTextureSizeLimitTest = function(maxArrayBufferSizeBytes, positiveCubeMapMaxSize) { 10 11 function numLevelsFromSize(size) { 12 var levels = 0; 13 while ((size >> levels) > 0) { 14 ++levels; 15 } 16 return levels; 17 } 18 19 // More formats can be added here when more texture compression extensions are enabled in WebGL. 20 var validFormats = { 21 COMPRESSED_RGB_S3TC_DXT1_EXT : 0x83F0, 22 COMPRESSED_RGBA_S3TC_DXT1_EXT : 0x83F1, 23 COMPRESSED_RGBA_S3TC_DXT3_EXT : 0x83F2, 24 COMPRESSED_RGBA_S3TC_DXT5_EXT : 0x83F3, 25 }; 26 27 // format specific restrictions for COMPRESSED_RGB_S3TC_DXT1_EXT and COMPRESSED_RGBA_S3TC_DXT1_EXT 28 // on the byteLength of the ArrayBufferView, pixels 29 function func1 (width, height) 30 { 31 return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 8; 32 } 33 34 // format specific restrictions for COMPRESSED_RGBA_S3TC_DXT3_EXT and COMPRESSED_RGBA_S3TC_DXT5_EXT 35 // on the byteLength of the ArrayBufferView, pixels 36 function func2 (width, height) 37 { 38 return Math.floor((width + 3) / 4) * Math.floor((height + 3) / 4) * 16; 39 } 40 41 var wtu = WebGLTestUtils; 42 var gl = wtu.create3DContext("example"); 43 var tests = [ 44 // More tests can be added here when more texture compression extensions are enabled in WebGL. 45 // Level 0 image width and height must be a multiple of the sizeStep. 46 { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGB_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, 47 { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT1_EXT, dataType: Uint8Array, func: func1, sizeStep: 4}, 48 { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT3_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, 49 { extension: "WEBGL_compressed_texture_s3tc", format: validFormats.COMPRESSED_RGBA_S3TC_DXT5_EXT, dataType: Uint8Array, func: func2, sizeStep: 4}, 50 ]; 51 52 // Note: We expressly only use 2 textures because first a texture will be defined 53 // using all mip levels of 1 format, then for a moment it will have mixed formats which 54 // may uncover bugs. 55 var targets = [ 56 { target: gl.TEXTURE_2D, 57 maxSize: gl.getParameter(gl.MAX_TEXTURE_SIZE), 58 tex: gl.createTexture(), 59 targets: [gl.TEXTURE_2D] 60 }, 61 { target: gl.TEXTURE_CUBE_MAP, 62 maxSize: gl.getParameter(gl.MAX_CUBE_MAP_TEXTURE_SIZE), 63 tex: gl.createTexture(), 64 targets: [ 65 gl.TEXTURE_CUBE_MAP_POSITIVE_X, 66 gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 67 gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 68 gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 69 gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 70 gl.TEXTURE_CUBE_MAP_NEGATIVE_Z 71 ] 72 } 73 ]; 74 75 function getSharedArrayBufferSize() { 76 var sharedArrayBufferSize = 0; 77 for (var tt = 0; tt < tests.length; ++tt) { 78 var test = tests[tt]; 79 for (var trg = 0; trg < targets.length; ++trg) { 80 var t = targets[trg]; 81 var bufferSizeNeeded; 82 if (t.target === gl.TEXTURE_CUBE_MAP) { 83 var positiveTestSize = Math.min(2048, t.maxSize); 84 bufferSizeNeeded = test.func(positiveTestSize, positiveTestSize); 85 } else { 86 bufferSizeNeeded = test.func(t.maxSize, test.sizeStep); 87 } 88 if (bufferSizeNeeded > sharedArrayBufferSize) { 89 sharedArrayBufferSize = bufferSizeNeeded; 90 } 91 bufferSizeNeeded = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); 92 // ArrayBuffers can be at most 4GB (minus 1 byte). 93 if (bufferSizeNeeded > sharedArrayBufferSize && bufferSizeNeeded <= maxArrayBufferSizeBytes) { 94 sharedArrayBufferSize = bufferSizeNeeded; 95 } 96 } 97 } 98 return sharedArrayBufferSize; 99 } 100 101 // Share an ArrayBuffer among tests to avoid too many large allocations 102 var sharedArrayBuffer = new ArrayBuffer(getSharedArrayBufferSize()); 103 104 gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); 105 106 var trg = 0; 107 var tt = 0; 108 runNextTest(); 109 110 function runNextTest() { 111 var t = targets[trg]; 112 113 if (tt == 0) { 114 var tex = t.tex; 115 gl.bindTexture(t.target, tex); 116 117 debug(""); 118 debug("max size for " + wtu.glEnumToString(gl, t.target) + ": " + t.maxSize); 119 } 120 121 var test = tests[tt]; 122 testFormatType(t, test); 123 ++tt; 124 if (tt == tests.length) { 125 tt = 0; 126 ++trg; 127 if (trg == targets.length) { 128 finishTest(); 129 return; 130 } 131 } 132 wtu.dispatchPromise(runNextTest); 133 } 134 135 function testFormatType(t, test) { 136 var positiveTestSize = t.maxSize; 137 var positiveTestOtherDimension = test.sizeStep; 138 if (t.target === gl.TEXTURE_CUBE_MAP) { 139 // Can't always test the maximum size since that can cause OOM: 140 positiveTestSize = Math.min(positiveCubeMapMaxSize, t.maxSize); 141 // Cube map textures need to be square: 142 positiveTestOtherDimension = positiveTestSize; 143 } 144 var positiveTestLevels = numLevelsFromSize(positiveTestSize); 145 var numLevels = numLevelsFromSize(t.maxSize); 146 debug(""); 147 debug("num levels: " + numLevels + ", levels used in positive test: " + positiveTestLevels); 148 149 debug(""); 150 151 // Query the extension and store globally so shouldBe can access it 152 var ext = wtu.getExtensionWithKnownPrefixes(gl, test.extension); 153 if (ext) { 154 155 testPassed("Successfully enabled " + test.extension + " extension"); 156 157 for (var j = 0; j < t.targets.length; ++j) { 158 var target = t.targets[j]; 159 debug(""); 160 debug(wtu.glEnumToString(gl, target) + " " + wtu.glEnumToString(ext, test.format)); 161 162 // positive test 163 var size = positiveTestSize; 164 var otherDimension = positiveTestOtherDimension; 165 for (var i = 0; i < positiveTestLevels; i++) { 166 var pixels = new test.dataType(sharedArrayBuffer, 0, test.func(size, otherDimension)); 167 gl.compressedTexImage2D(target, i, test.format, size, otherDimension, 0, pixels); 168 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "uploading compressed texture should generate NO_ERROR." 169 + "level is " + i + ", size is " + size + "x" + otherDimension); 170 size /= 2; 171 otherDimension /= 2; 172 if (otherDimension < 1) { 173 otherDimension = 1; 174 } 175 } 176 177 var numLevels = numLevelsFromSize(t.maxSize); 178 179 // out of bounds tests 180 181 // width or height out of bounds 182 if (t.target != gl.TEXTURE_CUBE_MAP) { 183 // only width out of bounds 184 var wideAndShortDataSize = test.func(t.maxSize + test.sizeStep, test.sizeStep); 185 var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, wideAndShortDataSize); 186 gl.compressedTexImage2D(target, 0, test.format, t.maxSize + test.sizeStep, test.sizeStep, 0, pixelsNegativeTest1); 187 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width out of bounds: should generate INVALID_VALUE." 188 + " level is 0, size is " + (t.maxSize + test.sizeStep) + "x" + (test.sizeStep)); 189 190 // only height out of bounds 191 var narrowAndTallDataSize = test.func(test.sizeStep, t.maxSize + test.sizeStep); 192 var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, narrowAndTallDataSize); 193 gl.compressedTexImage2D(target, 0, test.format, test.sizeStep, t.maxSize + test.sizeStep, 0, pixelsNegativeTest1); 194 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "height out of bounds: should generate INVALID_VALUE." 195 + " level is 0, size is " + (test.sizeStep) + "x" + (t.maxSize + test.sizeStep)); 196 } 197 198 // both width and height out of the maximum bounds simultaneously 199 var squareDataSize = test.func(t.maxSize + test.sizeStep, t.maxSize + test.sizeStep); 200 // this check assumes that each element is 1 byte 201 if (squareDataSize > sharedArrayBuffer.byteLength) { 202 testPassed("Unable to test square texture larger than maximum size due to ArrayBuffer size limitations -- this is legal"); 203 } else { 204 var pixelsNegativeTest1 = new test.dataType(sharedArrayBuffer, 0, squareDataSize); 205 gl.compressedTexImage2D(target, 0, test.format, t.maxSize + test.sizeStep, t.maxSize + test.sizeStep, 0, pixelsNegativeTest1); 206 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width and height out of bounds: should generate INVALID_VALUE." 207 + " level is 0, size is " + (t.maxSize + test.sizeStep) + "x" + (t.maxSize + test.sizeStep)); 208 } 209 210 // level out of bounds 211 var pixelsNegativeTest2 = new test.dataType(sharedArrayBuffer, 0, test.func(256, 256)); 212 gl.compressedTexImage2D(target, numLevels, test.format, 256, 256, 0, pixelsNegativeTest2); 213 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "level out of bounds: should generate INVALID_VALUE." 214 + " level is " + numLevels + ", size is 256x256"); 215 //width and height out of bounds for specified level 216 gl.compressedTexImage2D(target, numLevels - 1, test.format, 256, 256, 0, pixelsNegativeTest2); 217 wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "width or height out of bounds for specified level: should generate INVALID_VALUE." 218 + " level is " + (numLevels - 1) + ", size is 256x256"); 219 } 220 } 221 else { 222 testPassed("No " + test.extension + " extension support -- this is legal"); 223 } 224 } 225 226 };