tor-browser

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

webgl-shader-pixel-local-storage.html (19986B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <title>WebGL WEBGL_shader_pixel_local_storage Conformance Tests</title>
      6 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
      7 <script src="../../js/desktop-gl-constants.js"></script>
      8 <script src="../../js/js-test-pre.js"></script>
      9 <script src="../../js/webgl-test-utils.js"></script>
     10 <script src="../../js/tests/compositing-test.js"></script>
     11 <script src="../../js/tests/invalid-vertex-attrib-test.js"></script>
     12 </head>
     13 <body>
     14 <div id="description"></div>
     15 <canvas id="canvas" width="128" height="128" style="background-color:#080"> </canvas>
     16 <canvas id="canvas_no_alpha" width="128" height="128"> </canvas>
     17 <div id="console"></div>
     18 <script>
     19 "use strict";
     20 description("This test verifies the functionality of the WEBGL_shader_pixel_local_storage " +
     21            "extension, if it is available.");
     22 
     23 const wtu = WebGLTestUtils;
     24 const canvas = document.getElementById("canvas");
     25 const gl = wtu.create3DContext(canvas, {alpha: true}, 2);
     26 const gl_no_alpha = wtu.create3DContext("canvas_no_alpha", {alpha: false}, 2);
     27 let pls = null;
     28 
     29 // Outputs a fullscreen quad from a 4-vertex triangle strip.
     30 const fullscreenQuadVertexShader = `#version 300 es
     31 void main() {
     32    gl_Position.x = (gl_VertexID & 1) == 0 ? -1. : 1.;
     33    gl_Position.y = (gl_VertexID & 2) == 0 ? -1. : 1.;
     34    gl_Position.zw = vec2(0, 1);
     35 }`;
     36 
     37 function arraysEqual(a, b) {
     38  if (typeof a !== typeof b)
     39    return false;
     40  if (a.length != b.length)
     41    return false;
     42  for (let i = 0; i < a.length; ++i) {
     43    if (a[i] !== b[i])
     44      return false;
     45  }
     46  return true;
     47 }
     48 
     49 async function runTest() {
     50  if (!gl) {
     51    testFailed("WebGL2 context does not exist");
     52    finishTest();
     53    return;
     54  }
     55 
     56  debug("\nCheck the behavior surrounding WEBGL_shader_pixel_local_storage being enabled.");
     57  checkExtensionNotSupportedWhenDisabled();
     58  checkDependencyExtensionsEnabled(false);
     59  debug("Enable WEBGL_shader_pixel_local_storage.");
     60  pls = gl.getExtension("WEBGL_shader_pixel_local_storage");
     61  wtu.runExtensionSupportedTest(gl, "WEBGL_shader_pixel_local_storage", pls != null);
     62  if (!pls) {
     63    finishTest();
     64    return;
     65  }
     66  checkDependencyExtensionsEnabled(true);
     67 
     68  checkImplementationDependentLimits();
     69  checkInitialValues();
     70  checkWebGLNonNormativeBehavior();
     71 
     72  await checkRendering(gl);
     73  await checkRendering(gl_no_alpha);
     74 
     75  finishTest();
     76 }
     77 
     78 function checkExtensionNotSupportedWhenDisabled() {
     79  debug("\nCheck that a context does not support WEBGL_shader_pixel_local_storage before it is " +
     80        "enabled");
     81  shouldBeNull("gl.getParameter(0x96E0 /*MAX_PIXEL_LOCAL_STORAGE_PLANES_WEBGL*/)");
     82  wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
     83  shouldBeNull(
     84    "gl.getParameter(0x96E1 /*MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE_WEBGL*/)");
     85  wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
     86  shouldBeNull(
     87    "gl.getParameter(0x96E2 /*MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES_WEBGL*/)");
     88  wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
     89  shouldBeNull(
     90    "gl.getParameter(0x96E3 /*PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL*/)");
     91  wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
     92  wtu.glErrorShouldBe(gl, gl.NONE);
     93 }
     94 
     95 function checkDependencyExtensionsEnabled(enabled) {
     96  debug("\nCheck that dependency extensions of WEBGL_shader_pixel_local_storage are " +
     97        (enabled ? "enabled" : "disabled"));
     98  if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "OES_draw_buffers_indexed") !== undefined) {
     99    gl.getIndexedParameter(gl.BLEND_EQUATION_RGB, 1);
    100    wtu.glErrorShouldBe(gl, enabled ? gl.NONE : gl.INVALID_ENUM,
    101                        "OES_draw_buffers_indexed not enabled or disabled as expected");
    102  }
    103  if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "EXT_color_buffer_float") !== undefined) {
    104    gl.bindRenderbuffer(gl.RENDERBUFFER, gl.createRenderbuffer());
    105    gl.renderbufferStorage(gl.RENDERBUFFER, gl.R32F, 1, 1);
    106    wtu.glErrorShouldBe(gl, enabled ? gl.NONE : gl.INVALID_ENUM,
    107                        "EXT_color_buffer_float not enabled or disabled as expected");
    108    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    109  }
    110  if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "EXT_color_buffer_half_float") !== undefined) {
    111    gl.bindRenderbuffer(gl.RENDERBUFFER, gl.createRenderbuffer());
    112    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RG16F, 1, 1);
    113    wtu.glErrorShouldBe(gl, enabled ? gl.NONE : gl.INVALID_ENUM,
    114                        "EXT_color_buffer_half_float not enabled or disabled as expected");
    115    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    116  }
    117 }
    118 
    119 function checkImplementationDependentLimits() {
    120  debug("\nVerify conformant implementation-dependent PLS limits.");
    121  window.MAX_PIXEL_LOCAL_STORAGE_PLANES =
    122    gl.getParameter(pls.MAX_PIXEL_LOCAL_STORAGE_PLANES_WEBGL);
    123  window.MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE =
    124    gl.getParameter(pls.MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE_WEBGL);
    125  window.MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES =
    126    gl.getParameter(pls.MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES_WEBGL);
    127  wtu.glErrorShouldBe(gl, gl.NONE, "Pixel local storage queries should be supported.");
    128 
    129  window.MAX_COLOR_ATTACHMENTS = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS);
    130  window.MAX_DRAW_BUFFERS = gl.getParameter(gl.MAX_DRAW_BUFFERS);
    131 
    132  // Table 6.X: Impementation Dependent Pixel Local Storage Limits.
    133  shouldBeTrue("MAX_PIXEL_LOCAL_STORAGE_PLANES >= 4");
    134  shouldBeTrue("MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE >= 0");
    135  shouldBeTrue("MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES >= 4");
    136 
    137  // Logical deductions based on 6.X.
    138  shouldBeTrue(`MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES >=
    139               MAX_PIXEL_LOCAL_STORAGE_PLANES`);
    140  shouldBeTrue(`MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES >=
    141               MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE`);
    142  shouldBeTrue(`MAX_COLOR_ATTACHMENTS + MAX_PIXEL_LOCAL_STORAGE_PLANES >=
    143               MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES`);
    144  shouldBeTrue(`MAX_DRAW_BUFFERS + MAX_PIXEL_LOCAL_STORAGE_PLANES >=
    145               MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES`);
    146 }
    147 
    148 function checkInitialValues() {
    149  debug("\nCheck that PLS state has the correct initial values.");
    150  shouldBeTrue("gl.getParameter(pls.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
    151  wtu.glErrorShouldBe(
    152      gl, gl.NONE,
    153      "It's valid to query GL_PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL even when fbo 0 is bound.");
    154 
    155  // Table 6.Y: Pixel Local Storage State
    156  gl.bindFramebuffer(gl.FRAMEBUFFER, gl.createFramebuffer());
    157  shouldBeTrue("gl.getParameter(pls.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
    158  debug("Check the initial clear values for each plane.");
    159  const MAX_PIXEL_LOCAL_STORAGE_PLANES =
    160    gl.getParameter(pls.MAX_PIXEL_LOCAL_STORAGE_PLANES_WEBGL);
    161  for (let i = 0; i < MAX_PIXEL_LOCAL_STORAGE_PLANES; ++i)
    162  {
    163    expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    164        i, pls.PIXEL_LOCAL_FORMAT_WEBGL) == gl.NONE);
    165    expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    166        i, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) == null);
    167    expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    168        i, pls.PIXEL_LOCAL_TEXTURE_LEVEL_WEBGL) == 0);
    169    expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    170        i, pls.PIXEL_LOCAL_TEXTURE_LAYER_WEBGL) == 0);
    171    expectTrue(arraysEqual(
    172        pls.getFramebufferPixelLocalStorageParameterWEBGL(
    173            i, pls.PIXEL_LOCAL_CLEAR_VALUE_FLOAT_WEBGL),
    174        new Float32Array([0, 0, 0, 0])));
    175    expectTrue(arraysEqual(
    176        pls.getFramebufferPixelLocalStorageParameterWEBGL(
    177            i, pls.PIXEL_LOCAL_CLEAR_VALUE_INT_WEBGL),
    178        new Int32Array([0, 0, 0, 0])));
    179    expectTrue(arraysEqual(
    180        pls.getFramebufferPixelLocalStorageParameterWEBGL(
    181            i, pls.PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_WEBGL),
    182        new Uint32Array([0, 0, 0, 0])));
    183  }
    184  wtu.glErrorShouldBe(gl, gl.NONE);
    185  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    186 }
    187 
    188 function checkWebGLNonNormativeBehavior() {
    189  debug("\nCheck the WebGL-specific behavior not found in the " +
    190        "ANGLE_shader_pixel_local_storage specification.");
    191  gl.bindFramebuffer(gl.FRAMEBUFFER, gl.createFramebuffer());
    192 
    193  debug("If 'texture' has been deleted, generates an INVALID_OPERATION error.");
    194  wtu.glErrorShouldBe(gl, gl.NONE);
    195  const tex = gl.createTexture();
    196  gl.bindTexture(gl.TEXTURE_2D, tex);
    197  gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 1, 1);
    198  wtu.glErrorShouldBe(gl, gl.NONE);
    199  gl.deleteTexture(tex);
    200  pls.framebufferTexturePixelLocalStorageWEBGL(0, tex, 0, 0);
    201  wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
    202 
    203  debug("\nIf 'texture' was generated by a different WebGL2RenderingContext than this one, " +
    204        "generates an INVALID_OPERATION error.");
    205  const gl2 = wtu.create3DContext(null, null, 2);
    206  const tex2 = gl2.createTexture();
    207  gl2.bindTexture(gl2.TEXTURE_2D, tex2);
    208  gl2.texStorage2D(gl2.TEXTURE_2D, 1, gl2.RGBA8, 1, 1);
    209  pls.framebufferTexturePixelLocalStorageWEBGL(0, tex2, 0, 0);
    210  wtu.glErrorShouldBe(gl2, gl2.NONE);
    211  wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
    212 
    213  debug("\nIf value has less than srcOffset + 4 elements, generates an INVALID_VALUE error.");
    214  wtu.glErrorShouldBe(gl, gl.NONE);
    215  pls.framebufferPixelLocalClearValuefvWEBGL(0, new Float32Array(3));
    216  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    217  pls.framebufferPixelLocalClearValuefvWEBGL(1, [0, 0, 0]);
    218  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    219  pls.framebufferPixelLocalClearValueivWEBGL(2, new Int32Array(3));
    220  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    221  pls.framebufferPixelLocalClearValueivWEBGL(3, [0, 0, 0]);
    222  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    223  pls.framebufferPixelLocalClearValueuivWEBGL(4, new Uint32Array(3));
    224  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    225  pls.framebufferPixelLocalClearValueuivWEBGL(3, [0, 0, 0]);
    226  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    227  pls.framebufferPixelLocalClearValuefvWEBGL(2, new Float32Array(5), 2);
    228  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    229  pls.framebufferPixelLocalClearValuefvWEBGL(1, [0, 0, 0, 0, 0], 2);
    230  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    231  pls.framebufferPixelLocalClearValueivWEBGL(0, new Int32Array(5), 2);
    232  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    233  pls.framebufferPixelLocalClearValueivWEBGL(1, [0, 0, 0, 0, 0], 2);
    234  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    235  pls.framebufferPixelLocalClearValueuivWEBGL(2, new Uint32Array(5), 2);
    236  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    237  pls.framebufferPixelLocalClearValueuivWEBGL(3, [0, 0, 0, 0, 0], 2);
    238  wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
    239 
    240  debug("\nCheck that srcOffset works properly.");
    241  const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    242  pls.framebufferPixelLocalClearValuefvWEBGL(0, new Float32Array(arr), 1);
    243  wtu.glErrorShouldBe(gl, gl.NONE);
    244  shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    245                                0, pls.PIXEL_LOCAL_CLEAR_VALUE_FLOAT_WEBGL),
    246                            new Float32Array([1, 2, 3, 4]))`);
    247  pls.framebufferPixelLocalClearValuefvWEBGL(1, arr, 2);
    248  wtu.glErrorShouldBe(gl, gl.NONE);
    249  shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    250                                1, pls.PIXEL_LOCAL_CLEAR_VALUE_FLOAT_WEBGL),
    251                            [2, 3, 4, 5])`);
    252  pls.framebufferPixelLocalClearValueivWEBGL(2, new Int32Array(arr), 3);
    253  wtu.glErrorShouldBe(gl, gl.NONE);
    254  shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    255                                2, pls.PIXEL_LOCAL_CLEAR_VALUE_INT_WEBGL),
    256                            new Float32Array([3, 4, 5, 6]))`);
    257  pls.framebufferPixelLocalClearValueivWEBGL(3, arr, 4);
    258  wtu.glErrorShouldBe(gl, gl.NONE);
    259  shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    260                                3, pls.PIXEL_LOCAL_CLEAR_VALUE_INT_WEBGL),
    261                            [4, 5, 6, 7])`);
    262  pls.framebufferPixelLocalClearValueuivWEBGL(2, new Uint32Array(arr), 5);
    263  wtu.glErrorShouldBe(gl, gl.NONE);
    264  shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    265                                2, pls.PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_WEBGL),
    266                            new Uint32Array([5, 6, 7, 8]))`);
    267  pls.framebufferPixelLocalClearValueuivWEBGL(1, arr, 6);
    268  wtu.glErrorShouldBe(gl, gl.NONE);
    269  shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
    270                                1, pls.PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_WEBGL),
    271                            [6, 7, 8, 9])`);
    272  wtu.glErrorShouldBe(gl, gl.NONE);
    273 
    274  debug("\nCheck that PIXEL_LOCAL_TEXTURE_NAME_WEBGL returns a WebGLTexture.");
    275  shouldBeTrue(`pls.getFramebufferPixelLocalStorageParameterWEBGL(
    276                    0, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) === null`);
    277  window.validTex = gl.createTexture();
    278  gl.bindTexture(gl.TEXTURE_2D, validTex);
    279  gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 1, 1);
    280  wtu.glErrorShouldBe(gl, gl.NONE);
    281  pls.framebufferTexturePixelLocalStorageWEBGL(0, validTex, 0, 0);
    282  shouldBeTrue(`pls.getFramebufferPixelLocalStorageParameterWEBGL(
    283                    0, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) === validTex`);
    284  pls.framebufferTexturePixelLocalStorageWEBGL(0, null, 0, 0);
    285  shouldBeTrue(`pls.getFramebufferPixelLocalStorageParameterWEBGL(
    286                    0, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) === null`);
    287 
    288  wtu.glErrorShouldBe(gl, gl.NONE);
    289  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    290 }
    291 
    292 async function checkRendering(localGL) {
    293  const localCanvas = localGL.canvas;
    294  const alpha = localGL.getContextAttributes().alpha;
    295  debug("\nCheck very simple rendering with {alpha: " + alpha + "}");
    296 
    297  const localPLS = localGL.getExtension("WEBGL_shader_pixel_local_storage");
    298  if (!localPLS) {
    299    testFailed("localGL doesn't support pixel local storage.");
    300    return;
    301  }
    302 
    303  const tex = localGL.createTexture();
    304  localGL.bindTexture(localGL.TEXTURE_2D, tex);
    305  localGL.texStorage2D(localGL.TEXTURE_2D, 1, localGL.RGBA8, localCanvas.width, localCanvas.height);
    306  wtu.glErrorShouldBe(localGL, localGL.NONE);
    307 
    308  const plsFBO = localGL.createFramebuffer();
    309  localGL.bindFramebuffer(localGL.FRAMEBUFFER, plsFBO);
    310  localPLS.framebufferTexturePixelLocalStorageWEBGL(0, tex, 0, 0);
    311  wtu.glErrorShouldBe(localGL, localGL.NONE);
    312 
    313  localGL.viewport(0, 0, localCanvas.width, localCanvas.height);
    314 
    315  // Adds a uniform color into the existing color in pixel local storage.
    316  const fs = `#version 300 es
    317  #extension GL_ANGLE_shader_pixel_local_storage : require
    318  precision lowp float;
    319  uniform vec4 color;
    320  layout(binding=0, rgba8) uniform lowp pixelLocalANGLE pls;
    321  void main() {
    322    vec4 newColor = color + pixelLocalLoadANGLE(pls);
    323    pixelLocalStoreANGLE(pls, newColor);
    324  }`;
    325 
    326  const program = wtu.setupProgram(localGL, [fullscreenQuadVertexShader, fs]);
    327  if (!program) {
    328    testFailed("Failed to compile program.");
    329    return;
    330  }
    331 
    332  localGL.useProgram(program);
    333  const colorUniLocation = localGL.getUniformLocation(program, "color");
    334  wtu.glErrorShouldBe(localGL, localGL.NONE);
    335 
    336  // Disable color mask to ensure PLS and canvas manage their own color masks properly.
    337  localGL.colorMask(false, true, false, true);
    338 
    339  // Set global variables for shouldBeTrue().
    340  window.localGL = localGL;
    341  window.localPLS = localPLS;
    342 
    343  debug("\nCheck that pixel local storage works properly");
    344  localGL.disable(localGL.DITHER);
    345  localPLS.beginPixelLocalStorageWEBGL([localPLS.LOAD_OP_ZERO_WEBGL]);
    346  wtu.glErrorShouldBe(localGL, localGL.NONE);
    347  shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 1");
    348 
    349  localGL.uniform4f(colorUniLocation, 0, 1, 0, 0);
    350  localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
    351 
    352  localPLS.pixelLocalStorageBarrierWEBGL();
    353 
    354  localGL.uniform4f(colorUniLocation, 1, 0, 0, 0);
    355  localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
    356 
    357  localPLS.endPixelLocalStorageWEBGL([localPLS.STORE_OP_STORE_WEBGL]);
    358  wtu.glErrorShouldBe(localGL, localGL.NONE);
    359  shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
    360 
    361  const readFBO = localGL.createFramebuffer();
    362  localGL.bindFramebuffer(localGL.READ_FRAMEBUFFER, readFBO);
    363  localGL.framebufferTexture2D(localGL.READ_FRAMEBUFFER, localGL.COLOR_ATTACHMENT0,
    364                               localGL.TEXTURE_2D, tex, 0);
    365  wtu.glErrorShouldBe(localGL, localGL.NONE);
    366  wtu.checkCanvas(localGL, [255, 255, 0, 0]);
    367 
    368  debug("\nCheck that alpha is properly handled in the main canvas.");
    369  localGL.bindFramebuffer(localGL.DRAW_FRAMEBUFFER, null);
    370  localGL.blitFramebuffer(0, 0, localCanvas.width, localCanvas.height, 0, 0, localCanvas.width,
    371                          localCanvas.height, localGL.COLOR_BUFFER_BIT, localGL.NEAREST);
    372  localGL.bindFramebuffer(localGL.FRAMEBUFFER, null);
    373  wtu.glErrorShouldBe(localGL, localGL.NONE);
    374  wtu.checkCanvas(localGL, [255, 255, 0, alpha ? 0 : 255]);
    375 
    376  localGL.bindFramebuffer(localGL.FRAMEBUFFER, plsFBO);
    377  localPLS.beginPixelLocalStorageWEBGL([localPLS.LOAD_OP_LOAD_WEBGL]);
    378  wtu.glErrorShouldBe(localGL, localGL.NONE);
    379  shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 1");
    380 
    381  debug("\nGoing down from composite.");
    382 
    383  // The canvas should get cleared after compositing, even if PLS is active and color mask is
    384  // disabled.
    385  await new Promise(resolve => wtu.waitForComposite(resolve));
    386 
    387  // Reset global variables for shouldBeTrue() after await.
    388  window.localGL = localGL;
    389  window.localPLS = localPLS;
    390 
    391  debug("\nBack from composite!");
    392  debug("\nPLS should still be active on plsFBO even after being interrupted for compositing.");
    393  wtu.glErrorShouldBe(localGL, localGL.NONE);
    394  shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 1");
    395 
    396  localGL.uniform4f(colorUniLocation, 0, 0, 1, 0);
    397  localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
    398 
    399  localPLS.endPixelLocalStorageWEBGL([localPLS.STORE_OP_STORE_WEBGL]);
    400  wtu.glErrorShouldBe(localGL, localGL.NONE);
    401  shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
    402 
    403  debug("\nThe canvas should have gotten cleared while PLS was active.");
    404  localGL.bindFramebuffer(localGL.FRAMEBUFFER, null);
    405  wtu.checkCanvas(localGL, [0, 0, 0, alpha ? 0 : 255]);
    406 
    407  debug("\nThe additional PLS draw to plsFBO should have still worked after being interrupted " +
    408        "for compositing.");
    409  localGL.bindFramebuffer(localGL.READ_FRAMEBUFFER, readFBO);
    410  wtu.checkCanvas(localGL, [255, 255, 255, 0]);
    411  wtu.glErrorShouldBe(localGL, localGL.NONE);
    412 
    413  // Draws 'tex' to the canvas.
    414  const fs2 = `#version 300 es
    415  uniform lowp sampler2D tex;
    416  out lowp vec4 fragColor;
    417  void main() {
    418    ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));
    419    fragColor = texelFetch(tex, pixelCoord, 0);
    420  }`;
    421 
    422  const program2 = wtu.setupProgram(localGL, [fullscreenQuadVertexShader, fs2]);
    423  if (!program2) {
    424    testFailed("Failed to compile program2.");
    425    return;
    426  }
    427 
    428  debug("\nBlue should still be disabled in the color mask. Alpha is not disabled but should be " +
    429        "implicitly disabled since the canvas doesn't have alpha.");
    430  localGL.useProgram(program2);
    431  localGL.uniform1i(localGL.getUniformLocation(program2, "tex"), 0);
    432  localGL.bindFramebuffer(localGL.FRAMEBUFFER, null);
    433  localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
    434  wtu.checkCanvas(localGL, [0, 255, 0, alpha ? 0 : 255]);
    435 
    436  debug("\nThe client's color mask should have been preserved.");
    437  shouldBeTrue(`arraysEqual(localGL.getParameter(localGL.COLOR_WRITEMASK),
    438                            [false, true, false, true])`);
    439 }
    440 
    441 runTest();
    442 var successfullyParsed = true;
    443 </script>
    444 </body>
    445 </html>