tor-browser

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

webgl-blend-func-extended.js (23203B)


      1 "use strict";
      2 description("This test verifies the functionality of the WEBGL_blend_func_extended extension, if it is available.");
      3 
      4 debug("");
      5 
      6 var wtu = WebGLTestUtils;
      7 var gl = wtu.create3DContext("c", undefined, contextVersion);
      8 var ext;
      9 
     10 function runTestNoExtension() {
     11    debug("");
     12    debug("Testing getParameter without the extension");
     13    shouldBeNull("gl.getParameter(0x88FC /* MAX_DUAL_SOURCE_DRAW_BUFFERS_WEBGL */)");
     14    wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown");
     15    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
     16 
     17    if (contextVersion == 1) {
     18        debug("");
     19        debug("Testing SRC_ALPHA_SATURATE without the extension");
     20 
     21        gl.blendFunc(gl.ONE, gl.SRC_ALPHA_SATURATE);
     22        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "SRC_ALPHA_SATURATE not accepted as blendFunc dfactor");
     23        gl.blendFuncSeparate(gl.ONE, gl.SRC_ALPHA_SATURATE, gl.ONE, gl.ONE);
     24        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "SRC_ALPHA_SATURATE not accepted as blendFuncSeparate dstRGB");
     25        gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.SRC_ALPHA_SATURATE);
     26        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "SRC_ALPHA_SATURATE not accepted as blendFuncSeparate dstAlpha");
     27        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
     28    }
     29 
     30    debug("");
     31    debug("Testing SRC1 blend funcs without the extension");
     32 
     33    const extFuncs = {
     34        SRC1_COLOR_WEBGL: 0x88F9,
     35        SRC1_ALPHA_WEBGL: 0x8589,
     36        ONE_MINUS_SRC1_COLOR_WEBGL: 0x88FA,
     37        ONE_MINUS_SRC1_ALPHA_WEBGL: 0x88FB
     38    };
     39 
     40    for (const func in extFuncs) {
     41        gl.blendFunc(extFuncs[func], gl.ONE);
     42        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunc sfactor`);
     43        gl.blendFunc(gl.ONE, extFuncs[func]);
     44        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunc dfactor`);
     45        gl.blendFuncSeparate(extFuncs[func], gl.ONE, gl.ONE, gl.ONE);
     46        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate srcRGB`);
     47        gl.blendFuncSeparate(gl.ONE, extFuncs[func], gl.ONE, gl.ONE);
     48        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate dstRGB`);
     49        gl.blendFuncSeparate(gl.ONE, gl.ONE, extFuncs[func], gl.ONE);
     50        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate srcAlpha`);
     51        gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, extFuncs[func]);
     52        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate dstAlpha`);
     53        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
     54    }
     55 
     56    const dbi = gl.getExtension("OES_draw_buffers_indexed");
     57    if (!dbi) return;
     58 
     59    debug("");
     60    debug("Testing indexed SRC1 blend funcs without the extension");
     61    for (const func in extFuncs) {
     62        dbi.blendFunciOES(0, extFuncs[func], gl.ONE);
     63        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunciOES src`);
     64        dbi.blendFunciOES(0, gl.ONE, extFuncs[func]);
     65        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunciOES dst`);
     66        dbi.blendFuncSeparateiOES(0, extFuncs[func], gl.ONE, gl.ONE, gl.ONE);
     67        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES srcRGB`);
     68        dbi.blendFuncSeparateiOES(0, gl.ONE, extFuncs[func], gl.ONE, gl.ONE);
     69        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES dstRGB`);
     70        dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, extFuncs[func], gl.ONE);
     71        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES srcAlpha`);
     72        dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, gl.ONE, extFuncs[func]);
     73        wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES dstAlpha`);
     74        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
     75    }
     76 }
     77 
     78 function runEnumTests() {
     79    debug("");
     80    debug("Testing enums");
     81    shouldBe("ext.SRC1_COLOR_WEBGL", "0x88F9");
     82    shouldBe("ext.SRC1_ALPHA_WEBGL", "0x8589");
     83    shouldBe("ext.ONE_MINUS_SRC1_COLOR_WEBGL", "0x88FA");
     84    shouldBe("ext.ONE_MINUS_SRC1_ALPHA_WEBGL", "0x88FB");
     85    shouldBe("ext.MAX_DUAL_SOURCE_DRAW_BUFFERS_WEBGL", "0x88FC");
     86 }
     87 
     88 function runQueryTests() {
     89    debug("");
     90    debug("Testing getParameter");
     91    shouldBeGreaterThanOrEqual("gl.getParameter(ext.MAX_DUAL_SOURCE_DRAW_BUFFERS_WEBGL)", "1");
     92    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
     93 
     94    if (contextVersion == 1) {
     95        debug("");
     96        debug("Testing SRC_ALPHA_SATURATE with the extension");
     97 
     98        gl.blendFunc(gl.ONE, gl.SRC_ALPHA_SATURATE);
     99        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "SRC_ALPHA_SATURATE accepted as blendFunc dfactor");
    100        shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", "gl.SRC_ALPHA_SATURATE");
    101        shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", "gl.SRC_ALPHA_SATURATE");
    102        gl.blendFuncSeparate(gl.ONE, gl.SRC_ALPHA_SATURATE, gl.ONE, gl.ONE);
    103        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "SRC_ALPHA_SATURATE accepted as blendFuncSeparate dstRGB");
    104        shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", "gl.SRC_ALPHA_SATURATE");
    105        gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.SRC_ALPHA_SATURATE);
    106        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "SRC_ALPHA_SATURATE accepted as blendFuncSeparate dstAlpha");
    107        shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", "gl.SRC_ALPHA_SATURATE");
    108        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
    109    }
    110 
    111    const extFuncs = [
    112        "SRC1_COLOR_WEBGL",
    113        "SRC1_ALPHA_WEBGL",
    114        "ONE_MINUS_SRC1_COLOR_WEBGL",
    115        "ONE_MINUS_SRC1_ALPHA_WEBGL"
    116    ];
    117 
    118    debug("");
    119    debug("Testing blend state updates with SRC1 blend funcs");
    120    for (const func of extFuncs) {
    121        gl.blendFunc(ext[func], gl.ONE);
    122        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunc sfactor`);
    123        shouldBe("gl.getParameter(gl.BLEND_SRC_RGB)", `ext.${func}`);
    124        shouldBe("gl.getParameter(gl.BLEND_SRC_ALPHA)", `ext.${func}`);
    125        gl.blendFunc(gl.ONE, ext[func]);
    126        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunc dfactor`);
    127        shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", `ext.${func}`);
    128        shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", `ext.${func}`);
    129        gl.blendFuncSeparate(ext[func], gl.ONE, gl.ONE, gl.ONE);
    130        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate srcRGB`);
    131        shouldBe("gl.getParameter(gl.BLEND_SRC_RGB)", `ext.${func}`);
    132        gl.blendFuncSeparate(gl.ONE, ext[func], gl.ONE, gl.ONE);
    133        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate dstRGB`);
    134        shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", `ext.${func}`);
    135        gl.blendFuncSeparate(gl.ONE, gl.ONE, ext[func], gl.ONE);
    136        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate srcAlpha`);
    137        shouldBe("gl.getParameter(gl.BLEND_SRC_ALPHA)", `ext.${func}`);
    138        gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, ext[func]);
    139        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate dstAlpha`);
    140        shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", `ext.${func}`);
    141        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
    142    }
    143 
    144    const dbi = gl.getExtension("OES_draw_buffers_indexed");
    145    if (!dbi) return;
    146 
    147    debug("");
    148    debug("Testing indexed blend state updates with SRC1 blend funcs");
    149    for (const func of extFuncs) {
    150        dbi.blendFunciOES(0, ext[func], gl.ONE);
    151        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunciOES src`);
    152        shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_RGB, 0)", `ext.${func}`);
    153        shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_ALPHA, 0)", `ext.${func}`);
    154        dbi.blendFunciOES(0, gl.ONE, ext[func]);
    155        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunciOES dst`);
    156        shouldBe("gl.getIndexedParameter(gl.BLEND_DST_RGB, 0)", `ext.${func}`);
    157        shouldBe("gl.getIndexedParameter(gl.BLEND_DST_ALPHA, 0)", `ext.${func}`);
    158        dbi.blendFuncSeparateiOES(0, ext[func], gl.ONE, gl.ONE, gl.ONE);
    159        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES srcRGB`);
    160        shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_RGB, 0)", `ext.${func}`);
    161        dbi.blendFuncSeparateiOES(0, gl.ONE, ext[func], gl.ONE, gl.ONE);
    162        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES dstRGB`);
    163        shouldBe("gl.getIndexedParameter(gl.BLEND_DST_RGB, 0)", `ext.${func}`);
    164        dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, ext[func], gl.ONE);
    165        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES srcAlpha`);
    166        shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_ALPHA, 0)", `ext.${func}`);
    167        dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, gl.ONE, ext[func]);
    168        wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES dstAlpha`);
    169        shouldBe("gl.getIndexedParameter(gl.BLEND_DST_ALPHA, 0)", `ext.${func}`);
    170        wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
    171    }
    172 }
    173 
    174 function runShaderTests(extensionEnabled) {
    175    debug("");
    176    debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
    177 
    178    const shaderSets = [];
    179 
    180    const macro100 = `precision mediump float;
    181        void main() {
    182        #ifdef GL_EXT_blend_func_extended
    183            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    184        #else
    185            #error no GL_EXT_blend_func_extended;
    186        #endif
    187        }`;
    188    const macro300 = `#version 300 es
    189        out mediump vec4 my_FragColor;
    190        void main() {
    191        #ifdef GL_EXT_blend_func_extended
    192            my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    193        #else
    194            #error no GL_EXT_blend_func_extended;
    195        #endif
    196        }`;
    197    shaderSets.push([wtu.simpleVertexShader, macro100]);
    198    if (contextVersion == 2) {
    199        shaderSets.push([wtu.simpleVertexShaderESSL300, macro300]);
    200    }
    201 
    202    for (const shaders of shaderSets) {
    203        // Expect the macro shader to succeed ONLY if enabled
    204        if (wtu.setupProgram(gl, shaders)) {
    205            if (extensionEnabled) {
    206                testPassed("Macro defined in shaders when extension is enabled");
    207            } else {
    208                testFailed("Macro defined in shaders when extension is disabled");
    209            }
    210        } else {
    211            if (extensionEnabled) {
    212                testFailed("Macro not defined in shaders when extension is enabled");
    213            } else {
    214                testPassed("Macro not defined in shaders when extension is disabled");
    215            }
    216        }
    217    }
    218 
    219    shaderSets.length = 0;
    220 
    221    const missing100 = `
    222        void main() {
    223            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    224            gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0);
    225        }`;
    226    shaderSets.push([wtu.simpleVertexShader, missing100]);
    227 
    228    const missing300 = `#version 300 es
    229        layout(location = 0)            out mediump vec4 oColor0;
    230        layout(location = 0, index = 1) out mediump vec4 oColor1;
    231        void main() {
    232            oColor0 = vec4(1.0, 0.0, 0.0, 1.0);
    233            oColor1 = vec4(0.0, 1.0, 0.0, 1.0);
    234        }`;
    235    if (contextVersion == 2) {
    236        shaderSets.push([wtu.simpleVertexShaderESSL300, missing300]);
    237    }
    238 
    239    // Always expect the shader missing the #extension pragma to fail (whether enabled or not)
    240    for (const shaders of shaderSets) {
    241        if (wtu.setupProgram(gl, shaders)) {
    242            testFailed("Secondary fragment output allowed without #extension pragma");
    243        } else {
    244            testPassed("Secondary fragment output disallowed without #extension pragma");
    245        }
    246    }
    247 
    248    shaderSets.length = 0;
    249 
    250    const valid100 = `#extension GL_EXT_blend_func_extended : enable
    251        void main() {
    252            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    253            gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0);
    254        }`;
    255    shaderSets.push([wtu.simpleVertexShader, valid100]);
    256 
    257    const valid300 = `#version 300 es
    258        #extension GL_EXT_blend_func_extended : enable
    259        layout(location = 0)            out mediump vec4 oColor0;
    260        layout(location = 0, index = 1) out mediump vec4 oColor1;
    261        void main() {
    262            oColor0 = vec4(1.0, 0.0, 0.0, 1.0);
    263            oColor1 = vec4(0.0, 1.0, 0.0, 1.0);
    264        }`;
    265    if (contextVersion == 2) {
    266        shaderSets.push([wtu.simpleVertexShaderESSL300, valid300]);
    267    }
    268 
    269    // Try to compile a shader using a secondary fragment output that should only succeed if enabled
    270    for (const shaders of shaderSets) {
    271        if (wtu.setupProgram(gl, shaders)) {
    272            if (extensionEnabled) {
    273                testPassed("Secondary fragment output compiled successfully when extension enabled");
    274            } else {
    275                testFailed("Secondary fragment output compiled successfully when extension disabled");
    276            }
    277        } else {
    278            if (extensionEnabled) {
    279                testFailed("Secondary fragment output failed to compile when extension enabled");
    280            } else {
    281                testPassed("Secondary fragment output failed to compile when extension disabled");
    282            }
    283        }
    284    }
    285 
    286    // ESSL 3.00: Testing that multiple outputs require explicit locations
    287    if (contextVersion == 2) {
    288        const locations300 = `#version 300 es
    289            #extension GL_EXT_blend_func_extended : enable
    290            out mediump vec4 color0;
    291            out mediump vec4 color1;
    292            void main() {
    293                color0 = vec4(1.0, 0.0, 0.0, 1.0);
    294                color1 = vec4(0.0, 1.0, 0.0, 1.0);
    295            }`;
    296        if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, locations300])) {
    297            testFailed("Multiple fragment outputs compiled successfully without explicit locations");
    298        } else {
    299            testPassed("Multiple fragment outputs failed to compile without explicit locations");
    300        }
    301    }
    302 }
    303 
    304 function runMissingOutputsTests() {
    305    debug("");
    306    debug("Test draw calls with missing fragment outputs");
    307 
    308    wtu.setupUnitQuad(gl);
    309    gl.blendFunc(gl.ONE, ext.SRC1_COLOR_WEBGL);
    310 
    311    for (const enabled of [false, true]) {
    312        if (enabled) {
    313            gl.enable(gl.BLEND);
    314        } else {
    315            gl.disable(gl.BLEND);
    316        }
    317 
    318        for (const maskedOut of [false, true]) {
    319            gl.colorMask(!maskedOut, false, false, false);
    320 
    321            const label = `Dual-source blending ${enabled ? "ENABLED" : "DISABLED"}, ` +
    322                          `missing fragment outputs, and ` +
    323                          `${maskedOut ? "" : "NOT "}all color channels masked out`;
    324            debug(`ESSL 1.00: ${label}`);
    325 
    326            {
    327                const none = "void main() {}";
    328                wtu.setupProgram(gl, [wtu.simpleVertexShader, none]);
    329                wtu.drawUnitQuad(gl);
    330                wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION,
    331                                    "no fragment outputs");
    332            }
    333 
    334            {
    335                const fragColor = `
    336                    void main() {
    337                        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    338                    }`;
    339                wtu.setupProgram(gl, [wtu.simpleVertexShader, fragColor]);
    340                wtu.drawUnitQuad(gl);
    341                wtu.glErrorShouldBe(gl, (!enabled || maskedOut) ? gl.NO_ERROR : gl.INVALID_OPERATION,
    342                                    "only gl_FragColor");
    343            }
    344 
    345            {
    346                const secondaryFragColor = `#extension GL_EXT_blend_func_extended : enable
    347                    void main() {
    348                        gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0);
    349                    }`;
    350                wtu.setupProgram(gl, [wtu.simpleVertexShader, secondaryFragColor]);
    351                wtu.drawUnitQuad(gl);
    352                wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION,
    353                                    "only gl_SecondaryFragColorEXT");
    354            }
    355 
    356            if (contextVersion == 1) continue;
    357 
    358            debug(`ESSL 3.00: ${label}`);
    359 
    360            {
    361                const none = `#version 300 es
    362                    void main() {}`;
    363                wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, none]);
    364                wtu.drawUnitQuad(gl);
    365                wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION,
    366                                    "no fragment outputs");
    367            }
    368 
    369            {
    370                const color0 = `#version 300 es
    371                    out mediump vec4 color0;
    372                    void main() {
    373                        color0 = vec4(1.0, 0.0, 0.0, 1.0);
    374                    }`;
    375                wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, color0]);
    376                wtu.drawUnitQuad(gl);
    377                wtu.glErrorShouldBe(gl, (!enabled || maskedOut) ? gl.NO_ERROR : gl.INVALID_OPERATION,
    378                                    "only index 0 output");
    379            }
    380 
    381            {
    382                const color1 = `#version 300 es
    383                    #extension GL_EXT_blend_func_extended : enable
    384                    layout(location = 0, index = 1) out mediump vec4 color1;
    385                    void main() {
    386                        color1 = vec4(0.0, 1.0, 0.0, 1.0);
    387                    }`;
    388                wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, color1]);
    389                wtu.drawUnitQuad(gl);
    390                wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION,
    391                                    "only index 1 output");
    392            }
    393        }
    394    }
    395    gl.colorMask(true, true, true, true);
    396 }
    397 
    398 function runDrawBuffersLimitTests() {
    399    const dbi = gl.getExtension("OES_draw_buffers_indexed");
    400    if (!dbi) return;
    401 
    402    debug("");
    403    debug("Testing that dual-source blending limits the number of active draw buffers");
    404 
    405    const rb0 = gl.createRenderbuffer();
    406    gl.bindRenderbuffer(gl.RENDERBUFFER, rb0);
    407    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, 1, 1);
    408 
    409    const rb1 = gl.createRenderbuffer();
    410    gl.bindRenderbuffer(gl.RENDERBUFFER, rb1);
    411    gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, 1, 1);
    412 
    413    gl.bindRenderbuffer(gl.RENDERBUFFER, null);
    414 
    415    const fbo = gl.createFramebuffer();
    416    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
    417    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0);
    418    gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, rb1);
    419    wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
    420 
    421    const fs = `#version 300 es
    422        #extension GL_EXT_blend_func_extended : enable
    423        layout(location = 0, index = 0) out mediump vec4 color0;
    424        layout(location = 0, index = 1) out mediump vec4 color1;
    425        void main() {
    426            color0 = vec4(1.0, 0.0, 0.0, 1.0);
    427            color1 = vec4(0.0, 1.0, 0.0, 1.0);
    428        }`;
    429    wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, fs]);
    430 
    431    wtu.setupUnitQuad(gl);
    432 
    433    // Enable both draw buffers
    434    gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
    435 
    436    // Mask out draw buffer 1 to pass missing fragment outputs check
    437    dbi.colorMaskiOES(1, false, false, false, false);
    438 
    439    const extFuncs = [
    440        "SRC1_COLOR_WEBGL",
    441        "SRC1_ALPHA_WEBGL",
    442        "ONE_MINUS_SRC1_COLOR_WEBGL",
    443        "ONE_MINUS_SRC1_ALPHA_WEBGL"
    444    ];
    445 
    446    for (const func of extFuncs) {
    447        for (let slot = 0; slot < 4; slot++) {
    448            let param;
    449            switch (slot) {
    450                case 0:
    451                    param = "srcRGB";
    452                    gl.blendFuncSeparate(ext[func], gl.ONE, gl.ONE, gl.ONE);
    453                    break;
    454                case 1:
    455                    param = "dstRGB";
    456                    gl.blendFuncSeparate(gl.ONE, ext[func], gl.ONE, gl.ONE);
    457                    break;
    458                case 2:
    459                    param = "srcAlpha";
    460                    gl.blendFuncSeparate(gl.ONE, gl.ONE, ext[func], gl.ONE);
    461                    break;
    462                case 3:
    463                    param = "dstAlpha";
    464                    gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, ext[func]);
    465                    break;
    466            }
    467            debug(`Testing ${func} with ${param}`);
    468 
    469            // Limit must be applied even with blending disabled
    470            gl.disable(gl.BLEND);
    471            wtu.drawUnitQuad(gl);
    472            wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blending disabled");
    473 
    474            gl.enable(gl.BLEND);
    475            wtu.drawUnitQuad(gl);
    476            wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blending enabled");
    477 
    478            // Limit is not applied when non-SRC1 funcs are used
    479            gl.blendFunc(gl.ONE, gl.ONE);
    480            wtu.drawUnitQuad(gl);
    481            wtu.glErrorShouldBe(gl, gl.NO_ERROR, "dual-source blending disabled");
    482        }
    483    }
    484    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    485 }
    486 
    487 function runBlendingTests() {
    488    debug("");
    489    debug("Testing rendering with two most common dual-source blending configurations");
    490 
    491    const fs = `#extension GL_EXT_blend_func_extended : enable
    492        uniform mediump vec4 u_src0;
    493        uniform mediump vec4 u_src1;
    494        void main() {
    495            gl_FragColor             = u_src0;
    496            gl_SecondaryFragColorEXT = u_src1;
    497        }`;
    498    const program = wtu.setupProgram(gl, [wtu.simpleVertexShader, fs]);
    499    const uSrc0 = gl.getUniformLocation(program, "u_src0");
    500    const uSrc1 = gl.getUniformLocation(program, "u_src1");
    501 
    502    gl.enable(gl.BLEND);
    503    wtu.setupUnitQuad(gl);
    504    gl.clearColor(1.0, 1.0, 1.0, 1.0);
    505 
    506    gl.clear(gl.COLOR_BUFFER_BIT);
    507    gl.blendFunc(gl.ONE, ext.SRC1_COLOR_WEBGL);
    508    gl.uniform4f(uSrc0, 0.250, 0.375, 0.500, 0.625);
    509    gl.uniform4f(uSrc1, 0.125, 0.125, 0.125, 0.125);
    510    wtu.drawUnitQuad(gl);
    511    wtu.checkCanvas(gl, [96, 128, 159, 191], "Multiply destination by SRC1 and add SRC0", 2);
    512 
    513    gl.clear(gl.COLOR_BUFFER_BIT);
    514    gl.blendFunc(ext.SRC1_COLOR_WEBGL, ext.ONE_MINUS_SRC1_COLOR_WEBGL);
    515    gl.uniform4f(uSrc0, 0.125, 0.125, 0.125, 0.125);
    516    gl.uniform4f(uSrc1, 0.500, 0.375, 0.250, 0.125);
    517    wtu.drawUnitQuad(gl);
    518    wtu.checkCanvas(gl, [143, 171, 199, 227], "Per-channel color interpolation using SRC1", 2);
    519 }
    520 
    521 function runTest() {
    522    if (!gl) {
    523        testFailed("context does not exist");
    524        return;
    525    }
    526    testPassed("context exists");
    527 
    528    runTestNoExtension();
    529    runShaderTests(false);
    530 
    531    ext = gl.getExtension("WEBGL_blend_func_extended");
    532    wtu.runExtensionSupportedTest(gl, "WEBGL_blend_func_extended", ext !== null);
    533 
    534    if (ext !== null) {
    535        runEnumTests();
    536        runQueryTests();
    537        runShaderTests(true);
    538        runMissingOutputsTests();
    539        runDrawBuffersLimitTests();
    540        runBlendingTests();
    541    } else {
    542        testPassed("No WEBGL_blend_func_extended support -- this is legal");
    543    }
    544 }
    545 
    546 runTest();
    547 
    548 var successfullyParsed = true;