tor-browser

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

blitframebuffer-test.html (19542B)


      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 BlitFramebuffer Tests</title>
     12 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
     13 <script src="../../js/js-test-pre.js"></script>
     14 <script src="../../js/webgl-test-utils.js"></script>
     15 </head>
     16 <body>
     17 <canvas id="example" width="8" height="8"></canvas>
     18 <div id="description"></div>
     19 <div id="console"></div>
     20 <script>
     21 "use strict";
     22 
     23 var wtu = WebGLTestUtils;
     24 description("This test verifies the functionality of blitFramebuffer for some corner cases.");
     25 
     26 var width = 8;
     27 var height = 8;
     28 
     29 var gl = wtu.create3DContext("example", undefined, 2);
     30 if (!gl) {
     31    testFailed("WebGL context does not exist");
     32 } else {
     33    testPassed("WebGL context exists");
     34    blit_framebuffer_repeated();
     35    blit_framebuffer_feedback_loop();
     36    blit_framebuffer_multisampling_srgb();
     37 }
     38 
     39 function blit_framebuffer_repeated() {
     40    debug("");
     41    debug("This test verifies repeated calls to blitFramebuffer.");
     42 
     43    // Create offscreen fbo and its color attachment.
     44    var tex_2d = gl.createTexture();
     45    gl.bindTexture(gl.TEXTURE_2D, tex_2d);
     46    gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
     47    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
     48    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
     49    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
     50    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
     51 
     52    var fb = gl.createFramebuffer();
     53    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
     54    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0);
     55    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
     56        testFailed("Framebuffer incomplete.");
     57        return;
     58    }
     59 
     60    var prog = wtu.setupColorQuad(gl, 0);
     61    wtu.setFloatDrawColor(gl, [ 1.0, 0.0, 0.0, 1.0 ]);
     62    wtu.drawUnitQuad(gl);
     63    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb);
     64    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
     65    gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
     66    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
     67    wtu.checkCanvas(gl, [ 255, 0, 0, 255 ], "should be red at first");
     68 
     69    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
     70    wtu.setFloatDrawColor(gl, [ 0.0, 1.0, 0.0, 1.0 ]);
     71    wtu.drawUnitQuad(gl);
     72    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb);
     73    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
     74    gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
     75    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
     76    wtu.checkCanvas(gl, [ 0, 255, 0, 255 ], "should be green");
     77 }
     78 
     79 function blit_framebuffer_feedback_loop() {
     80 
     81    debug("");
     82    debug("This test checks whether the src resource and dst resource have identical images.");
     83    // Create read fbo and its color attachment.
     84    var tex_2d = gl.createTexture();
     85    gl.bindTexture(gl.TEXTURE_2D, tex_2d);
     86    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
     87    gl.generateMipmap(gl.TEXTURE_2D);
     88 
     89    var fb0 = gl.createFramebuffer();
     90    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
     91    gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0);
     92    if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
     93        testFailed("Framebuffer incomplete.");
     94        return;
     95    }
     96 
     97    // Create draw fbo and its color attachment.
     98    var rb0 = gl.createRenderbuffer();
     99    gl.bindRenderbuffer(gl.RENDERBUFFER, rb0);
    100    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, width, height);
    101 
    102    var fb1 = gl.createFramebuffer();
    103    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    104    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0);
    105    if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    106        testFailed("Framebuffer incomplete.");
    107        return;
    108    }
    109 
    110    // Blit framebuffer, all conditions are OK.
    111    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    112    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed.");
    113 
    114    // Blit framebuffer, the src buffer and the dst buffer should not be identical.
    115    // Exactly the same read/draw fbo
    116    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    117    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb0);
    118    gl.blitFramebuffer(0, 0, 2, 2, 4, 4, 6, 6, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    119    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw buffer are identical.");
    120 
    121    // Exactly the same read/draw framebuffer: default framebuffer
    122    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
    123    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
    124    gl.blitFramebuffer(0, 0, 2, 2, 4, 4, 6, 6, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    125    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw buffer are identical.");
    126 
    127    // The same image with the same level bound to read/draw buffer.
    128    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    129    gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0);
    130    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    131    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 0);
    132    if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE ||
    133        gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    134        testFailed("Framebuffer incomplete.");
    135        return;
    136    }
    137    gl.blitFramebuffer(0, 0, 2, 2, 4, 4, 6, 6, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    138    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw color buffer are identical.");
    139 
    140    // The same image in read/draw buffer, but different levels are bound to read/draw buffer respectively.
    141    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    142    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex_2d, 1);
    143    if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    144        testFailed("Framebuffer incomplete.");
    145        return;
    146    }
    147    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    148    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed if read/draw buffer has the same image with different levels.");
    149 
    150    // The same cube_map image in read/draw buffer, but different faces are bound to read/draw buffer respectively.
    151    var tex_cube_map = gl.createTexture();
    152    gl.bindTexture(gl.TEXTURE_CUBE_MAP, tex_cube_map);
    153    gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    154    gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    155    gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    156    gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    157    gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    158    gl.texImage2D(gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, gl.RGBA8, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    159    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    160    gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0);
    161    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    162    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, tex_cube_map, 0);
    163    if ((gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) ||
    164        (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)) {
    165        testFailed("Framebuffer incomplete.");
    166        return;
    167    }
    168    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    169    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed if read/draw buffer has the same CUBE_MAP image with different faces.");
    170 
    171    // The same 3D/2D_ARRAY image in read/draw buffer, but different layers are bound to read/draw buffer respectively.
    172    var tex_2d_array = gl.createTexture();
    173    gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex_2d_array);
    174    var depth = 2;
    175    gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, gl.RGBA8, width, height, depth, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    176    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    177    var level = 0, layer = 0;
    178    gl.framebufferTextureLayer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex_2d_array, level, layer);
    179    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    180    layer = 1;
    181    gl.framebufferTextureLayer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, tex_2d_array, level, layer);
    182    if ((gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) ||
    183        (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)) {
    184        testFailed("Framebuffer incomplete.");
    185        return;
    186    }
    187    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    188    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed if read/draw buffer has the same 3D/2D_ARRAY image with different layers.");
    189 
    190    // The same image are bound as depth buffer in both read framebuffer and draw framebuffer
    191    var rb1 = gl.createRenderbuffer();
    192    gl.bindRenderbuffer(gl.RENDERBUFFER, rb1);
    193    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, width, height);
    194    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    195    gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0);
    196    gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb1);
    197    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    198    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, tex_cube_map, 0);
    199    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb1);
    200    if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE ||
    201        gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    202        testFailed("Framebuffer incomplete.");
    203        return;
    204    }
    205    // But the mask doesn't have depth buffer bit.
    206    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    207    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed.");
    208 
    209    // The mask has depth buffer bit.
    210    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT, gl.NEAREST);
    211    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw framebuffer have identical depth buffer attachment.");
    212 
    213    // The same image are bound as stencil buffer in both read framebuffer and draw framebuffer
    214    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    215    gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb1);
    216    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    217    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, rb1);
    218    if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE ||
    219        gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    220        testFailed("Framebuffer incomplete.");
    221        return;
    222    }
    223    // But the mask doesn't have stencil buffer bit.
    224    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.NEAREST);
    225    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed.");
    226 
    227    // The mask has stencil buffer bit.
    228    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT, gl.NEAREST);
    229    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw framebuffer have identical stencil buffer attachment.");
    230 
    231    // The same image are bound as color buffer in both read framebuffer and draw framebuffer
    232    var rb2 = gl.createRenderbuffer();
    233    gl.bindRenderbuffer(gl.RENDERBUFFER, rb2);
    234    gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH24_STENCIL8, width, height);
    235    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    236    gl.framebufferTexture2D(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0);
    237    gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
    238    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
    239    gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb2);
    240    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    241    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, tex_cube_map, 0);
    242    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_CUBE_MAP_POSITIVE_X, tex_cube_map, 0);
    243    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, rb1);
    244    if (gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE ||
    245        gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    246        testFailed("Framebuffer incomplete.");
    247        return;
    248    }
    249    // But the mask doesn't have color buffer bit.
    250    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.DEPTH_BUFFER_BIT, gl.NEAREST);
    251    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed.");
    252 
    253    // The mask has color buffer bit, but the same image is not specified as draw buffer.
    254    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT, gl.NEAREST);
    255    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer should succeed.");
    256 
    257    // The mask has color buffer bit, the same image is specified as both read buffer and draw buffer.
    258    gl.drawBuffers([gl.COLOR_ATTACHENT0, gl.COLOR_ATTACHMENT1]);
    259    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT, gl.NEAREST);
    260    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer should generate INVALID_OPERATION if read/draw buffers have identical color buffer attachment.");
    261 
    262    gl.bindTexture(gl.TEXTURE_2D, null);
    263    gl.bindTexture(gl.TEXTURE_CUBE_MAP, null);
    264    gl.bindTexture(gl.TEXTURE_2D_ARRAY, null);
    265    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    266    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
    267    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
    268    gl.deleteTexture(tex_2d);
    269    gl.deleteTexture(tex_cube_map);
    270    gl.deleteTexture(tex_2d_array);
    271    gl.deleteRenderbuffer(rb0);
    272    gl.deleteRenderbuffer(rb1);
    273    gl.deleteRenderbuffer(rb2);
    274    gl.deleteFramebuffer(fb0);
    275    gl.deleteFramebuffer(fb1);
    276 };
    277 
    278 function blit_framebuffer_multisampling_srgb() {
    279 
    280    debug("");
    281    debug("This test vefify the functionality of blitframebuffer from or to a multisampled srgb image.");
    282 
    283    // Read buffer can have multisampled srgb image, but draw buffers can not.
    284    var rb0 = gl.createRenderbuffer();
    285    var fb0 = gl.createFramebuffer();
    286    var rb1 = gl.createRenderbuffer();
    287    var fb1 = gl.createFramebuffer();
    288    var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.SRGB8_ALPHA8, gl.SAMPLES);
    289    gl.bindRenderbuffer(gl.RENDERBUFFER, rb0);
    290    gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl.SRGB8_ALPHA8, width, height);
    291    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fb0);
    292    gl.framebufferRenderbuffer(gl.READ_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0);
    293 
    294    gl.bindRenderbuffer(gl.RENDERBUFFER, rb1);
    295    gl.renderbufferStorage(gl.RENDERBUFFER, gl.SRGB8_ALPHA8, width, height);
    296    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    297    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb1);
    298    if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE ||
    299        gl.checkFramebufferStatus(gl.READ_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    300        testFailed("Framebuffer incomplete.");
    301        return;
    302    }
    303    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.LINEAR);
    304    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "blitFramebuffer from multisampled srgb image should succeed.");
    305 
    306    gl.bindRenderbuffer(gl.RENDERBUFFER, rb1);
    307    gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl.SRGB8_ALPHA8, width, height);
    308    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    309    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb1);
    310    if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    311        testFailed("Framebuffer incomplete.");
    312        return;
    313    }
    314    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.LINEAR);
    315    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer to a multisampled srgb image should generate INVALID_OPERATION.");
    316 
    317    // BlitFramebuffer from a multisampled srgb image, the src region and the dst region must be exactly the same.
    318    gl.bindRenderbuffer(gl.RENDERBUFFER, rb1);
    319    gl.renderbufferStorage(gl.RENDERBUFFER, gl.SRGB8_ALPHA8, width, height);
    320    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    321    gl.framebufferRenderbuffer(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb1);
    322    if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    323        testFailed("Framebuffer incomplete.");
    324        return;
    325    }
    326    gl.blitFramebuffer(0, 0, 2, 2, 2, 2, 4, 4, gl.COLOR_BUFFER_BIT, gl.LINEAR);
    327    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer from a multisampled srgb image, the src region and the dst region must be exactly the same.");
    328 
    329    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 4, 4, gl.COLOR_BUFFER_BIT, gl.LINEAR);
    330    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer from a multisampled srgb image, the src region and the dst region must be exactly the same.");
    331 
    332    // BlitFramebuffer from a multisampled srgb image, the format/type must be exactly the same. So blit from a multisampled srgb image to a linear image is invalid.
    333    var tex = gl.createTexture();
    334    gl.bindTexture(gl.TEXTURE_2D, tex);
    335    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
    336    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fb1);
    337    gl.framebufferTexture2D(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
    338    if (gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
    339        testFailed("Framebuffer incomplete.");
    340        return;
    341    }
    342    gl.blitFramebuffer(0, 0, 2, 2, 0, 0, 2, 2, gl.COLOR_BUFFER_BIT, gl.LINEAR);
    343    wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blitFramebuffer from a multisampled srgb image, the format/type must be exactly the same.");
    344 
    345    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    346    gl.bindTexture(gl.TEXTURE_2D, null);
    347    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
    348    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
    349    gl.deleteRenderbuffer(rb0);
    350    gl.deleteRenderbuffer(rb1);
    351    gl.deleteTexture(tex);
    352    gl.deleteFramebuffer(fb0);
    353    gl.deleteFramebuffer(fb1);
    354 }
    355 
    356 var successfullyParsed = true;
    357 </script>
    358 <script src="../../js/js-test-post.js"></script>
    359 
    360 </body>
    361 </html>