tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 };