"use strict"; description("This test verifies the functionality of the WEBGL_blend_func_extended extension, if it is available."); debug(""); var wtu = WebGLTestUtils; var gl = wtu.create3DContext("c", undefined, contextVersion); var ext; function runTestNoExtension() { debug(""); debug("Testing getParameter without the extension"); shouldBeNull("gl.getParameter(0x88FC /* MAX_DUAL_SOURCE_DRAW_BUFFERS_WEBGL */)"); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown"); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); if (contextVersion == 1) { debug(""); debug("Testing SRC_ALPHA_SATURATE without the extension"); gl.blendFunc(gl.ONE, gl.SRC_ALPHA_SATURATE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "SRC_ALPHA_SATURATE not accepted as blendFunc dfactor"); gl.blendFuncSeparate(gl.ONE, gl.SRC_ALPHA_SATURATE, gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "SRC_ALPHA_SATURATE not accepted as blendFuncSeparate dstRGB"); gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.SRC_ALPHA_SATURATE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "SRC_ALPHA_SATURATE not accepted as blendFuncSeparate dstAlpha"); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); } debug(""); debug("Testing SRC1 blend funcs without the extension"); const extFuncs = { SRC1_COLOR_WEBGL: 0x88F9, SRC1_ALPHA_WEBGL: 0x8589, ONE_MINUS_SRC1_COLOR_WEBGL: 0x88FA, ONE_MINUS_SRC1_ALPHA_WEBGL: 0x88FB }; for (const func in extFuncs) { gl.blendFunc(extFuncs[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunc sfactor`); gl.blendFunc(gl.ONE, extFuncs[func]); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunc dfactor`); gl.blendFuncSeparate(extFuncs[func], gl.ONE, gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate srcRGB`); gl.blendFuncSeparate(gl.ONE, extFuncs[func], gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate dstRGB`); gl.blendFuncSeparate(gl.ONE, gl.ONE, extFuncs[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate srcAlpha`); gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, extFuncs[func]); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparate dstAlpha`); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); } const dbi = gl.getExtension("OES_draw_buffers_indexed"); if (!dbi) return; debug(""); debug("Testing indexed SRC1 blend funcs without the extension"); for (const func in extFuncs) { dbi.blendFunciOES(0, extFuncs[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunciOES src`); dbi.blendFunciOES(0, gl.ONE, extFuncs[func]); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFunciOES dst`); dbi.blendFuncSeparateiOES(0, extFuncs[func], gl.ONE, gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES srcRGB`); dbi.blendFuncSeparateiOES(0, gl.ONE, extFuncs[func], gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES dstRGB`); dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, extFuncs[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES srcAlpha`); dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, gl.ONE, extFuncs[func]); wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `${func} not accepted as blendFuncSeparateiOES dstAlpha`); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); } } function runEnumTests() { debug(""); debug("Testing enums"); shouldBe("ext.SRC1_COLOR_WEBGL", "0x88F9"); shouldBe("ext.SRC1_ALPHA_WEBGL", "0x8589"); shouldBe("ext.ONE_MINUS_SRC1_COLOR_WEBGL", "0x88FA"); shouldBe("ext.ONE_MINUS_SRC1_ALPHA_WEBGL", "0x88FB"); shouldBe("ext.MAX_DUAL_SOURCE_DRAW_BUFFERS_WEBGL", "0x88FC"); } function runQueryTests() { debug(""); debug("Testing getParameter"); shouldBeGreaterThanOrEqual("gl.getParameter(ext.MAX_DUAL_SOURCE_DRAW_BUFFERS_WEBGL)", "1"); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); if (contextVersion == 1) { debug(""); debug("Testing SRC_ALPHA_SATURATE with the extension"); gl.blendFunc(gl.ONE, gl.SRC_ALPHA_SATURATE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "SRC_ALPHA_SATURATE accepted as blendFunc dfactor"); shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", "gl.SRC_ALPHA_SATURATE"); shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", "gl.SRC_ALPHA_SATURATE"); gl.blendFuncSeparate(gl.ONE, gl.SRC_ALPHA_SATURATE, gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "SRC_ALPHA_SATURATE accepted as blendFuncSeparate dstRGB"); shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", "gl.SRC_ALPHA_SATURATE"); gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, gl.SRC_ALPHA_SATURATE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "SRC_ALPHA_SATURATE accepted as blendFuncSeparate dstAlpha"); shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", "gl.SRC_ALPHA_SATURATE"); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); } const extFuncs = [ "SRC1_COLOR_WEBGL", "SRC1_ALPHA_WEBGL", "ONE_MINUS_SRC1_COLOR_WEBGL", "ONE_MINUS_SRC1_ALPHA_WEBGL" ]; debug(""); debug("Testing blend state updates with SRC1 blend funcs"); for (const func of extFuncs) { gl.blendFunc(ext[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunc sfactor`); shouldBe("gl.getParameter(gl.BLEND_SRC_RGB)", `ext.${func}`); shouldBe("gl.getParameter(gl.BLEND_SRC_ALPHA)", `ext.${func}`); gl.blendFunc(gl.ONE, ext[func]); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunc dfactor`); shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", `ext.${func}`); shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", `ext.${func}`); gl.blendFuncSeparate(ext[func], gl.ONE, gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate srcRGB`); shouldBe("gl.getParameter(gl.BLEND_SRC_RGB)", `ext.${func}`); gl.blendFuncSeparate(gl.ONE, ext[func], gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate dstRGB`); shouldBe("gl.getParameter(gl.BLEND_DST_RGB)", `ext.${func}`); gl.blendFuncSeparate(gl.ONE, gl.ONE, ext[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate srcAlpha`); shouldBe("gl.getParameter(gl.BLEND_SRC_ALPHA)", `ext.${func}`); gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, ext[func]); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFuncSeparate dstAlpha`); shouldBe("gl.getParameter(gl.BLEND_DST_ALPHA)", `ext.${func}`); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); } const dbi = gl.getExtension("OES_draw_buffers_indexed"); if (!dbi) return; debug(""); debug("Testing indexed blend state updates with SRC1 blend funcs"); for (const func of extFuncs) { dbi.blendFunciOES(0, ext[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunciOES src`); shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_RGB, 0)", `ext.${func}`); shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_ALPHA, 0)", `ext.${func}`); dbi.blendFunciOES(0, gl.ONE, ext[func]); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} accepted as blendFunciOES dst`); shouldBe("gl.getIndexedParameter(gl.BLEND_DST_RGB, 0)", `ext.${func}`); shouldBe("gl.getIndexedParameter(gl.BLEND_DST_ALPHA, 0)", `ext.${func}`); dbi.blendFuncSeparateiOES(0, ext[func], gl.ONE, gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES srcRGB`); shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_RGB, 0)", `ext.${func}`); dbi.blendFuncSeparateiOES(0, gl.ONE, ext[func], gl.ONE, gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES dstRGB`); shouldBe("gl.getIndexedParameter(gl.BLEND_DST_RGB, 0)", `ext.${func}`); dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, ext[func], gl.ONE); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES srcAlpha`); shouldBe("gl.getIndexedParameter(gl.BLEND_SRC_ALPHA, 0)", `ext.${func}`); dbi.blendFuncSeparateiOES(0, gl.ONE, gl.ONE, gl.ONE, ext[func]); wtu.glErrorShouldBe(gl, gl.NO_ERROR, `${func} not accepted as blendFuncSeparateiOES dstAlpha`); shouldBe("gl.getIndexedParameter(gl.BLEND_DST_ALPHA, 0)", `ext.${func}`); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); } } function runShaderTests(extensionEnabled) { debug(""); debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled")); const shaderSets = []; const macro100 = `precision mediump float; void main() { #ifdef GL_EXT_blend_func_extended gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); #else #error no GL_EXT_blend_func_extended; #endif }`; const macro300 = `#version 300 es out mediump vec4 my_FragColor; void main() { #ifdef GL_EXT_blend_func_extended my_FragColor = vec4(1.0, 0.0, 0.0, 1.0); #else #error no GL_EXT_blend_func_extended; #endif }`; shaderSets.push([wtu.simpleVertexShader, macro100]); if (contextVersion == 2) { shaderSets.push([wtu.simpleVertexShaderESSL300, macro300]); } for (const shaders of shaderSets) { // Expect the macro shader to succeed ONLY if enabled if (wtu.setupProgram(gl, shaders)) { if (extensionEnabled) { testPassed("Macro defined in shaders when extension is enabled"); } else { testFailed("Macro defined in shaders when extension is disabled"); } } else { if (extensionEnabled) { testFailed("Macro not defined in shaders when extension is enabled"); } else { testPassed("Macro not defined in shaders when extension is disabled"); } } } shaderSets.length = 0; const missing100 = ` void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0); }`; shaderSets.push([wtu.simpleVertexShader, missing100]); const missing300 = `#version 300 es layout(location = 0) out mediump vec4 oColor0; layout(location = 0, index = 1) out mediump vec4 oColor1; void main() { oColor0 = vec4(1.0, 0.0, 0.0, 1.0); oColor1 = vec4(0.0, 1.0, 0.0, 1.0); }`; if (contextVersion == 2) { shaderSets.push([wtu.simpleVertexShaderESSL300, missing300]); } // Always expect the shader missing the #extension pragma to fail (whether enabled or not) for (const shaders of shaderSets) { if (wtu.setupProgram(gl, shaders)) { testFailed("Secondary fragment output allowed without #extension pragma"); } else { testPassed("Secondary fragment output disallowed without #extension pragma"); } } shaderSets.length = 0; const valid100 = `#extension GL_EXT_blend_func_extended : enable void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0); }`; shaderSets.push([wtu.simpleVertexShader, valid100]); const valid300 = `#version 300 es #extension GL_EXT_blend_func_extended : enable layout(location = 0) out mediump vec4 oColor0; layout(location = 0, index = 1) out mediump vec4 oColor1; void main() { oColor0 = vec4(1.0, 0.0, 0.0, 1.0); oColor1 = vec4(0.0, 1.0, 0.0, 1.0); }`; if (contextVersion == 2) { shaderSets.push([wtu.simpleVertexShaderESSL300, valid300]); } // Try to compile a shader using a secondary fragment output that should only succeed if enabled for (const shaders of shaderSets) { if (wtu.setupProgram(gl, shaders)) { if (extensionEnabled) { testPassed("Secondary fragment output compiled successfully when extension enabled"); } else { testFailed("Secondary fragment output compiled successfully when extension disabled"); } } else { if (extensionEnabled) { testFailed("Secondary fragment output failed to compile when extension enabled"); } else { testPassed("Secondary fragment output failed to compile when extension disabled"); } } } // ESSL 3.00: Testing that multiple outputs require explicit locations if (contextVersion == 2) { const locations300 = `#version 300 es #extension GL_EXT_blend_func_extended : enable out mediump vec4 color0; out mediump vec4 color1; void main() { color0 = vec4(1.0, 0.0, 0.0, 1.0); color1 = vec4(0.0, 1.0, 0.0, 1.0); }`; if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, locations300])) { testFailed("Multiple fragment outputs compiled successfully without explicit locations"); } else { testPassed("Multiple fragment outputs failed to compile without explicit locations"); } } } function runMissingOutputsTests() { debug(""); debug("Test draw calls with missing fragment outputs"); wtu.setupUnitQuad(gl); gl.blendFunc(gl.ONE, ext.SRC1_COLOR_WEBGL); for (const enabled of [false, true]) { if (enabled) { gl.enable(gl.BLEND); } else { gl.disable(gl.BLEND); } for (const maskedOut of [false, true]) { gl.colorMask(!maskedOut, false, false, false); const label = `Dual-source blending ${enabled ? "ENABLED" : "DISABLED"}, ` + `missing fragment outputs, and ` + `${maskedOut ? "" : "NOT "}all color channels masked out`; debug(`ESSL 1.00: ${label}`); { const none = "void main() {}"; wtu.setupProgram(gl, [wtu.simpleVertexShader, none]); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION, "no fragment outputs"); } { const fragColor = ` void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }`; wtu.setupProgram(gl, [wtu.simpleVertexShader, fragColor]); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, (!enabled || maskedOut) ? gl.NO_ERROR : gl.INVALID_OPERATION, "only gl_FragColor"); } { const secondaryFragColor = `#extension GL_EXT_blend_func_extended : enable void main() { gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0); }`; wtu.setupProgram(gl, [wtu.simpleVertexShader, secondaryFragColor]); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION, "only gl_SecondaryFragColorEXT"); } if (contextVersion == 1) continue; debug(`ESSL 3.00: ${label}`); { const none = `#version 300 es void main() {}`; wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, none]); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION, "no fragment outputs"); } { const color0 = `#version 300 es out mediump vec4 color0; void main() { color0 = vec4(1.0, 0.0, 0.0, 1.0); }`; wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, color0]); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, (!enabled || maskedOut) ? gl.NO_ERROR : gl.INVALID_OPERATION, "only index 0 output"); } { const color1 = `#version 300 es #extension GL_EXT_blend_func_extended : enable layout(location = 0, index = 1) out mediump vec4 color1; void main() { color1 = vec4(0.0, 1.0, 0.0, 1.0); }`; wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, color1]); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, maskedOut ? gl.NO_ERROR : gl.INVALID_OPERATION, "only index 1 output"); } } } gl.colorMask(true, true, true, true); } function runDrawBuffersLimitTests() { const dbi = gl.getExtension("OES_draw_buffers_indexed"); if (!dbi) return; debug(""); debug("Testing that dual-source blending limits the number of active draw buffers"); const rb0 = gl.createRenderbuffer(); gl.bindRenderbuffer(gl.RENDERBUFFER, rb0); gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, 1, 1); const rb1 = gl.createRenderbuffer(); gl.bindRenderbuffer(gl.RENDERBUFFER, rb1); gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, 1, 1); gl.bindRenderbuffer(gl.RENDERBUFFER, null); const fbo = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0); gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, rb1); wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE); const fs = `#version 300 es #extension GL_EXT_blend_func_extended : enable layout(location = 0, index = 0) out mediump vec4 color0; layout(location = 0, index = 1) out mediump vec4 color1; void main() { color0 = vec4(1.0, 0.0, 0.0, 1.0); color1 = vec4(0.0, 1.0, 0.0, 1.0); }`; wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, fs]); wtu.setupUnitQuad(gl); // Enable both draw buffers gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]); // Mask out draw buffer 1 to pass missing fragment outputs check dbi.colorMaskiOES(1, false, false, false, false); const extFuncs = [ "SRC1_COLOR_WEBGL", "SRC1_ALPHA_WEBGL", "ONE_MINUS_SRC1_COLOR_WEBGL", "ONE_MINUS_SRC1_ALPHA_WEBGL" ]; for (const func of extFuncs) { for (let slot = 0; slot < 4; slot++) { let param; switch (slot) { case 0: param = "srcRGB"; gl.blendFuncSeparate(ext[func], gl.ONE, gl.ONE, gl.ONE); break; case 1: param = "dstRGB"; gl.blendFuncSeparate(gl.ONE, ext[func], gl.ONE, gl.ONE); break; case 2: param = "srcAlpha"; gl.blendFuncSeparate(gl.ONE, gl.ONE, ext[func], gl.ONE); break; case 3: param = "dstAlpha"; gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ONE, ext[func]); break; } debug(`Testing ${func} with ${param}`); // Limit must be applied even with blending disabled gl.disable(gl.BLEND); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blending disabled"); gl.enable(gl.BLEND); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "blending enabled"); // Limit is not applied when non-SRC1 funcs are used gl.blendFunc(gl.ONE, gl.ONE); wtu.drawUnitQuad(gl); wtu.glErrorShouldBe(gl, gl.NO_ERROR, "dual-source blending disabled"); } } gl.bindFramebuffer(gl.FRAMEBUFFER, null); } function runBlendingTests() { debug(""); debug("Testing rendering with two most common dual-source blending configurations"); const fs = `#extension GL_EXT_blend_func_extended : enable uniform mediump vec4 u_src0; uniform mediump vec4 u_src1; void main() { gl_FragColor = u_src0; gl_SecondaryFragColorEXT = u_src1; }`; const program = wtu.setupProgram(gl, [wtu.simpleVertexShader, fs]); const uSrc0 = gl.getUniformLocation(program, "u_src0"); const uSrc1 = gl.getUniformLocation(program, "u_src1"); gl.enable(gl.BLEND); wtu.setupUnitQuad(gl); gl.clearColor(1.0, 1.0, 1.0, 1.0); gl.clear(gl.COLOR_BUFFER_BIT); gl.blendFunc(gl.ONE, ext.SRC1_COLOR_WEBGL); gl.uniform4f(uSrc0, 0.250, 0.375, 0.500, 0.625); gl.uniform4f(uSrc1, 0.125, 0.125, 0.125, 0.125); wtu.drawUnitQuad(gl); wtu.checkCanvas(gl, [96, 128, 159, 191], "Multiply destination by SRC1 and add SRC0", 2); gl.clear(gl.COLOR_BUFFER_BIT); gl.blendFunc(ext.SRC1_COLOR_WEBGL, ext.ONE_MINUS_SRC1_COLOR_WEBGL); gl.uniform4f(uSrc0, 0.125, 0.125, 0.125, 0.125); gl.uniform4f(uSrc1, 0.500, 0.375, 0.250, 0.125); wtu.drawUnitQuad(gl); wtu.checkCanvas(gl, [143, 171, 199, 227], "Per-channel color interpolation using SRC1", 2); } function runTest() { if (!gl) { testFailed("context does not exist"); return; } testPassed("context exists"); runTestNoExtension(); runShaderTests(false); ext = gl.getExtension("WEBGL_blend_func_extended"); wtu.runExtensionSupportedTest(gl, "WEBGL_blend_func_extended", ext !== null); if (ext !== null) { runEnumTests(); runQueryTests(); runShaderTests(true); runMissingOutputsTests(); runDrawBuffersLimitTests(); runBlendingTests(); } else { testPassed("No WEBGL_blend_func_extended support -- this is legal"); } } runTest(); var successfullyParsed = true;