tor-browser

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

read-pixels-test.html (16111B)


      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 <title>WebGL ReadPixels conformance test.</title>
     12 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
     13 <script src="../../js/desktop-gl-constants.js"></script>
     14 <script src="../../js/js-test-pre.js"></script>
     15 <script src="../../js/webgl-test-utils.js"> </script>
     16 </head>
     17 <body>
     18 <canvas id="example" width="200" height="200" style="width: 20px; height: 20px"></canvas>
     19 <canvas id="example2" width="200" height="200" style="width: 20px; height: 20px"></canvas>
     20 <div id="description"></div>
     21 <div id="console"></div>
     22 <script>
     23 "use strict";
     24 description("Checks that ReadPixels works as expected.");
     25 
     26 var wtu = WebGLTestUtils;
     27 let gl;
     28 
     29 debug("<h1>antialias = false</h1>")
     30 runTest(document.getElementById("example"), false);
     31 debug("<h1>antialias = true</h1>")
     32 runTest(document.getElementById("example2"), true);
     33 finishTest();
     34 
     35 var actual;
     36 var expected;
     37 
     38 function runTest(canvas, antialias) {
     39  gl = wtu.create3DContext(canvas, {antialias: antialias});
     40  var contextVersion = wtu.getDefault3DContextVersion();
     41 
     42  debug("");
     43  debug("Test null pixels");
     44  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, null);
     45  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "null pixels");
     46 
     47  debug("");
     48  debug("Test pixels size");
     49  gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0));
     50  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "empty pixels array with 0x0 read data");
     51  gl.readPixels(0, 0, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0));
     52  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "empty pixels array with 1x0 read data");
     53  gl.readPixels(0, 0, 0, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0));
     54  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "empty pixels array with 0x1 read data");
     55  gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(3));
     56  wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "small pixels array for 1x1 read data");
     57  if (contextVersion >= 2) {
     58    gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0), 1);
     59    wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "offset is greater than array size");
     60    gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(1), 1);
     61    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no space left in pixels array with 0x0 read data");
     62    gl.readPixels(0, 0, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(1), 1);
     63    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no space left in pixels array with 1x0 read data");
     64    gl.readPixels(0, 0, 0, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(1), 1);
     65    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no space left in pixels array with 0x1 read data");
     66    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4), 1);
     67    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "no space left in pixels array with 1x1 read data");
     68    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(5), 1);
     69    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "read 1x1 data fits into pixels with offset");
     70  }
     71 
     72  debug("");
     73  debug("Test combined depth-stencil type");
     74  // The combined type is undefined in WebGL 1.0 and never allowed as a read type in WebGL 2.0
     75  gl.readPixels(0, 0, 1, 1, gl.RGBA, 0x8DAD /* FLOAT_32_UNSIGNED_INT_24_8_REV */, new Uint8Array(32));
     76  wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "FLOAT_32_UNSIGNED_INT_24_8_REV is rejected");
     77  wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no extra error generated");
     78 
     79  var width = 2;
     80  var height = 2;
     81  var continueTestFunc = continueTestPart1;
     82 
     83  gl.clearColor(1, 1, 1, 1);
     84  gl.clear(gl.COLOR_BUFFER_BIT);
     85 
     86  // Resize the canvas to 2x2. This is an attempt to get stuff in the backbuffer.
     87  // that shouldn't be there.
     88  canvas.addEventListener("webglcontextlost", function(e) { e.preventDefault(); }, false);
     89  canvas.addEventListener("webglcontextrestored", continueTestAfterContextRestored, false);
     90  canvas.width = width;
     91  canvas.height = height;
     92  if (gl.getError() != gl.CONTEXT_LOST_WEBGL) {
     93    continueTestPart1();
     94  }
     95 
     96  function continueTestAfterContextRestored() {
     97    window.gl = wtu.create3DContext(canvas);
     98    var func = continueTestFunc;
     99    window.continueTestFunc = function() { testFailed("should not be here"); };
    100    func();
    101  }
    102 
    103  function continueTestPart1() {
    104    gl.clearColor(0.2, 0.6, 0.4, 1);
    105    gl.clear(gl.COLOR_BUFFER_BIT);
    106 
    107    var innerColor = [51, 153, 102, 255]; // (0.2, 0.6, 0.4, 1)
    108    var outerColor = [19, 72, 0, 198]; // Random color other than [0, 0, 0, 0]
    109 
    110    var tests = [
    111      { msg: 'in range', checkColor: innerColor, x:  0, y:  0,
    112        oneColor: innerColor, oneX: 0, oneY: 0},
    113      { msg: 'off top left', checkColor: outerColor, x: -1, y: -1,
    114        oneColor: innerColor, oneX: 1, oneY: 1},
    115      { msg: 'off bottom right', checkColor: outerColor, x:  1, y:  1,
    116        oneColor: innerColor, oneX: 0, oneY: 0},
    117      { msg: 'completely off top ', checkColor: outerColor, x:  0, y: -2,
    118        oneColor: outerColor, oneX: 0, oneY: 0},
    119      { msg: 'completely off bottom', checkColor: outerColor, x:  0, y:  2,
    120        oneColor: outerColor, oneX: 0, oneY: 0},
    121      { msg: 'completely off left', checkColor: outerColor, x: -2, y:  0,
    122        oneColor: outerColor, oneX: 0, oneY: 0},
    123      { msg: 'completeley off right', checkColor: outerColor, x:  2, y:  0,
    124        oneColor: outerColor, oneX: 0, oneY: 0}
    125    ];
    126 
    127    for (var tt = 0; tt < tests.length; ++tt) {
    128      var test = tests[tt];
    129      debug("");
    130      debug("checking: " + test.msg);
    131      checkBuffer(test.checkColor, test.x, test.y,
    132                  test.oneColor, test.oneX, test.oneY);
    133    }
    134 
    135    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no GL errors");
    136 
    137    function checkBuffer(checkColor, x, y, oneColor, oneX, oneY) {
    138      var buf = new Uint8Array(width * height * 4);
    139      // Initialize buf.
    140      for (var ii = 0; ii < width * height; ++ii) {
    141        buf[ii * 4] = outerColor[0];
    142        buf[ii * 4 + 1] = outerColor[1];
    143        buf[ii * 4 + 2] = outerColor[2];
    144        buf[ii * 4 + 3] = outerColor[3];
    145      }
    146      gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, buf);
    147      for (var yy = 0; yy < height; ++yy) {
    148        for (var xx = 0; xx < width; ++xx) {
    149          var offset = (yy * width + xx) * 4;
    150          var expectedColors = (oneX == xx && oneY == yy) ? oneColor : checkColor;
    151          var mismatch = false;
    152          for (var cc = 0; cc < 4; ++cc) {
    153            var expectedColor = expectedColors[cc];
    154            var color = buf[offset + cc];
    155            var diff = Math.abs(expectedColor - color);
    156            if (diff >= 3) {
    157              mismatch = true;
    158              break;
    159            }
    160          }
    161          assertMsg(!mismatch,
    162                    "color pixel at " + xx + ", " + yy + " should be about " + expectedColors +
    163                    ", was = " + [buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]]);
    164        }
    165      }
    166    }
    167 
    168    continueTestPart2();
    169  }
    170 
    171  function continueTestPart2() {
    172    let neverValidFormats = [gl.DEPTH_COMPONENT, gl.DEPTH_STENCIL, desktopGL.R8, gl.RGBA4];
    173    let maybeValidFormats = [gl.LUMINANCE, gl.LUMINANCE_ALPHA];
    174    if (contextVersion < 2) {
    175    // They are valid in WebGL 2 or higher
    176      maybeValidFormats = maybeValidFormats.concat([desktopGL.RED, desktopGL.RG_INTEGER, desktopGL.RGBA_INTEGER]);
    177    }
    178 
    179    let neverValidTypeInfo = [
    180      {type: desktopGL.UNSIGNED_INT_24_8,       dest: new Uint32Array(4)}
    181    ];
    182    let maybeValidTypeInfo = [];
    183    if (contextVersion < 2) {
    184    // They are valid in WebGL 2 or Higher
    185      maybeValidTypeInfo = maybeValidTypeInfo.concat([
    186        {type: gl.UNSIGNED_SHORT,                     dest: new Uint16Array(4)},
    187        {type: gl.SHORT,                              dest: new Int16Array(4)},
    188        {type: gl.BYTE,                               dest: new Int8Array(4)},
    189        {type: gl.UNSIGNED_INT,                       dest: new Uint32Array(4)},
    190        {type: desktopGL.UNSIGNED_INT_2_10_10_10_REV, dest: new Uint32Array(4)}
    191      ]);
    192    }
    193 
    194    debug("");
    195    debug("check non-default format or type");
    196    for (let format of neverValidFormats) {
    197      var buf = new Uint8Array(4);
    198      gl.readPixels(0, 0, 1, 1, format, gl.UNSIGNED_BYTE, buf);
    199      wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Should not be able to read as " + wtu.glEnumToString(gl, format));
    200    }
    201    for (let format of maybeValidFormats) {
    202      var buf = new Uint8Array(4);
    203      gl.readPixels(0, 0, 1, 1, format, gl.UNSIGNED_BYTE, buf);
    204      wtu.glErrorShouldBe(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], "Should not be able to read as " + wtu.glEnumToString(gl, format));
    205    }
    206 
    207    for (let info of neverValidTypeInfo) {
    208      var type = info.type;
    209      var dest = info.dest;
    210      gl.readPixels(0, 0, 1, 1, gl.RGBA, type, dest);
    211      wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Should not be able to read as " + wtu.glEnumToString(gl, type));
    212    }
    213    for (let info of maybeValidTypeInfo) {
    214      var type = info.type;
    215      var dest = info.dest;
    216      gl.readPixels(0, 0, 1, 1, gl.RGBA, type, dest);
    217      wtu.glErrorShouldBe(gl, [gl.INVALID_ENUM, gl.INVALID_OPERATION], "Should not be able to read as " + wtu.glEnumToString(gl, type));
    218    }
    219 
    220 
    221    // -
    222 
    223    const combinations = [
    224      {
    225        format: gl.RGB,
    226        type: gl.UNSIGNED_SHORT_5_6_5,
    227        dest: new Uint8Array(3),
    228      },
    229      {
    230        format: gl.RGBA,
    231        type: gl.UNSIGNED_SHORT_5_5_5_1,
    232        dest: new Uint16Array(1),
    233      },
    234      {
    235        format: gl.RGBA,
    236        type: gl.UNSIGNED_SHORT_4_4_4_4,
    237        dest: new Uint16Array(1),
    238      },
    239    ];
    240 
    241    const FORMATS = [
    242      {
    243        format: gl.RGBA,
    244        channels: 4,
    245      }, {
    246        format: gl.RGB,
    247        channels: 3,
    248      }, {
    249        format: gl.LUMINANCE_ALPHA,
    250        channels: 2,
    251      }, {
    252        format: gl.ALPHA,
    253        channels: 1,
    254      }, {
    255        format: gl.LUMINANCE,
    256        channels: 3,
    257      },
    258    ];
    259    if (contextVersion >= 2) {
    260      FORMATS.push(
    261        {
    262          format: gl.RED,
    263          channels: 1,
    264        }, {
    265          format: gl.RG,
    266          channels: 1,
    267        }, {
    268          format: gl.RGBA_INTEGER,
    269          channels: 4,
    270        }, {
    271          format: gl.RGB_INTEGER,
    272          channels: 3,
    273        }, {
    274          format: gl.RG_INTEGER,
    275          channels: 2,
    276        }, {
    277          format: gl.RED_INTEGER,
    278          channels: 1,
    279        }
    280      );
    281    }
    282 
    283    // -
    284 
    285    const TYPES = [
    286      {
    287        type: gl.UNSIGNED_BYTE,
    288        ctor: Uint8Array,
    289      }, {
    290        type: gl.BYTE,
    291        ctor: Int8Array,
    292      }, {
    293        type: gl.UNSIGNED_SHORT,
    294        ctor: Uint16Array,
    295      }, {
    296        type: gl.SHORT,
    297        ctor: Int16Array,
    298      }, {
    299        type: gl.UNSIGNED_INT,
    300        ctor: Uint32Array,
    301      }, {
    302        type: gl.INT,
    303        ctor: Int32Array,
    304      }, {
    305        type: gl.FLOAT,
    306        ctor: Float32Array,
    307      }
    308    ];
    309 
    310    if (contextVersion >= 2) {
    311      TYPES.push(
    312        {
    313          type: gl.HALF_FLOAT,
    314          ctor: Uint16Array,
    315        }
    316      );
    317    }
    318 
    319    const ext = gl.getExtension('OES_texture_half_float');
    320    if (ext) {
    321      TYPES.push(
    322        {
    323          type: ext.HALF_FLOAT_OES,
    324          ctor: Uint16Array,
    325        }
    326      );
    327    }
    328 
    329    for (const t of TYPES) {
    330      for (const f of FORMATS) {
    331        const desc = Object.assign({}, f, t);
    332        desc.dest = new desc.ctor(desc.channels);
    333        combinations.push(desc);
    334      }
    335    }
    336 
    337    // -
    338 
    339    debug("");
    340    debug("check invalid combinations of format/type");
    341 
    342    var implFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
    343    var implType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
    344    debug("IMPLEMENTATION_COLOR_READ_FORMAT: " + wtu.glEnumToString(gl, implFormat));
    345    debug("IMPLEMENTATION_COLOR_READ_TYPE: " + wtu.glEnumToString(gl, implType));
    346 
    347    for (var tt = 0; tt < combinations.length; ++ tt) {
    348      var info = combinations[tt];
    349      var format = info.format;
    350      var type = info.type;
    351      var dest = info.dest;
    352      gl.readPixels(0, 0, 1, 1, format, type, dest);
    353      // Only two format/type parameter pairs are accepted. GL_RGBA/GL_UNSIGNED_BYTE is always
    354      // accepted on default readbuffer. The other acceptable pair can be discovered by querying
    355      // GL_IMPLEMENTATION_COLOR_READ_FORMAT and GL_IMPLEMENTATION_COLOR_READ_TYPE.
    356      if ((format == gl.RGBA && type == gl.UNSIGNED_BYTE) || (format == implFormat && type == implType)) {
    357        wtu.glErrorShouldBe(
    358            gl, gl.NO_ERROR,
    359            "Should be able to read as " + wtu.glEnumToString(gl, format) +
    360            " / " + wtu.glEnumToString(gl, type));
    361      } else {
    362        wtu.glErrorShouldBe(
    363            gl, [gl.INVALID_OPERATION, gl.INVALID_ENUM],
    364            "Should not be able to read as " + wtu.glEnumToString(gl, format) +
    365            " / " + wtu.glEnumToString(gl, type));
    366      }
    367    }
    368 
    369    debug("");
    370    debug("check reading with lots of drawing");
    371    continueTestFunc = continueTestPart3;
    372    width = 1024;
    373    height = 1024;
    374    canvas.width = width;
    375    canvas.height = height;
    376    if (gl.getError() != gl.CONTEXT_LOST_WEBGL) {
    377      continueTestPart3();
    378    }
    379  }
    380 
    381  function continueTestPart3() {
    382    gl.viewport(0, 0, 1024, 1024);
    383    var program = wtu.setupTexturedQuad(gl);
    384    var loc = gl.getUniformLocation(program, "tex");
    385    gl.disable(gl.BLEND);
    386    gl.disable(gl.DEPTH_TEST);
    387    var colors = [[255, 0, 0, 255], [0, 255, 0, 255], [0, 0, 255, 255]];
    388    var textures = [];
    389    var results = [];
    390    for (var ii = 0; ii < colors.length; ++ii) {
    391      gl.activeTexture(gl.TEXTURE0 + ii);
    392      var tex = gl.createTexture();
    393      wtu.fillTexture(gl, tex, 1, 1, colors[ii]);
    394      textures.push(tex);
    395    }
    396    for (var ii = 0; ii < colors.length; ++ii) {
    397      for (var jj = 0; jj < 300 + ii + 1; ++jj) {
    398        gl.uniform1i(loc, jj % 3);
    399        gl.drawArrays(gl.TRIANGLES, 0, 6);
    400      }
    401      var buf = new Uint8Array(4);
    402      gl.readPixels(512, 512, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buf);
    403      results.push(buf);
    404      for (var kk = 0; kk < 99; ++kk) {
    405        gl.uniform1i(loc, (jj + kk) % 3);
    406        gl.drawArrays(gl.TRIANGLES, 0, 6);
    407      }
    408    }
    409    for (var ii = 0; ii < colors.length; ++ii) {
    410      var buf = results[ii];
    411      var color = colors[ii];
    412      actual = [buf[0], buf[1], buf[2], buf[3]];
    413      expected = [color[0], color[1], color[2], color[3]];
    414      shouldBe("actual", "expected");
    415    }
    416    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no GL errors");
    417 
    418    debug("");
    419    debug("check readback into Uint8ClampedArray");
    420    continueTestFunc = continueTestPart4;
    421    const kSize = 32;
    422    canvas.width = kSize;
    423    canvas.height = kSize;
    424    if (gl.getError() != gl.CONTEXT_LOST_WEBGL) {
    425      continueTestPart4();
    426    }
    427  }
    428 
    429  function continueTestPart4() {
    430    const kSize = 32;
    431    gl.viewport(0, 0, kSize, kSize);
    432    gl.clearColor(0.0, 1.0, 0.0, 1.0);
    433    gl.clear(gl.COLOR_BUFFER_BIT);
    434 
    435    var buf = new Uint8ClampedArray(4);
    436    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buf);
    437    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no GL errors reading back into a Uint8ClampedArray");
    438    if (buf[0] == 0 && buf[1] == 255 && buf[2] == 0 && buf[3] == 255) {
    439      testPassed("Readback into Uint8ClampedArray worked successfully");
    440    } else {
    441      assertMsg(false,
    442                "color pixel at 0, 0 should be [0, 255, 0, 255], was " +
    443                [buf[0], buf[1], buf[2], buf[3]]);
    444    }
    445 
    446    const validDatas = [
    447      `new Uint8Array(4)`,
    448      `new Uint8Array(new ArrayBuffer(4))`,
    449      `new Uint8ClampedArray(4)`,
    450      `new Uint8ClampedArray(new ArrayBuffer(4))`,
    451    ];
    452    if (window.SharedArrayBuffer) {
    453      validDatas.push(
    454        `new Uint8Array(new SharedArrayBuffer(4))`,
    455        `new Uint8ClampedArray(new SharedArrayBuffer(4))`
    456      );
    457    }
    458    for (const x of validDatas) {
    459      shouldNotThrow(`gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, ${x});`);
    460    }
    461  }
    462 }
    463 </script>
    464 </body>
    465 </html>