summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/js/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-15 03:34:50 +0000
commitdef92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch)
tree2ef34b9ad8bb9a9220e05d60352558b15f513894 /dom/canvas/test/webgl-conf/checkout/js/tests
parentAdding debian version 125.0.3-1. (diff)
downloadfirefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz
firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout/js/tests')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js6
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/drawingbuffer-storage-test.js275
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js68
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/tests/webgl-blend-func-extended.js548
8 files changed, 896 insertions, 5 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js
index 46d155f5f1..04396c9b32 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/compressed-texture-utils.js
@@ -67,6 +67,10 @@ let testCompressedFormatsUnavailableWhenExtensionDisabled = function(gl, compres
if (compressedFormats.hasOwnProperty(name)) {
gl.compressedTexImage2D(gl.TEXTURE_2D, 0, compressedFormats[name], testSize, testSize, 0, new Uint8Array(expectedByteLength(testSize, testSize, compressedFormats[name])));
wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Trying to use format " + name + " with extension disabled.");
+ if (gl.texStorage2D) {
+ gl.texStorage2D(gl.TEXTURE_2D, 1, compressedFormats[name], testSize, testSize);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Trying to use format " + name + " with texStorage2D with extension disabled.");
+ }
}
}
gl.bindTexture(gl.TEXTURE_2D, null);
@@ -255,4 +259,4 @@ return {
testTexStorageLevelDimensions: testTexStorageLevelDimensions,
};
-})(); \ No newline at end of file
+})();
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/drawingbuffer-storage-test.js b/dom/canvas/test/webgl-conf/checkout/js/tests/drawingbuffer-storage-test.js
new file mode 100644
index 0000000000..330171b320
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/drawingbuffer-storage-test.js
@@ -0,0 +1,275 @@
+/*
+Copyright (c) 2023 The Khronos Group Inc.
+Use of this source code is governed by an MIT-style license that can be
+found in the LICENSE.txt file.
+*/
+
+"use strict";
+
+let gl;
+let oldViewport;
+let width;
+let height;
+let format;
+let hasDrawingBufferStorage;
+let maxRenderbufferSize;
+
+function runTest(contextVersion) {
+ description();
+ debug("");
+
+ function initialize() {
+ let canvas = document.createElement("canvas");
+ gl = wtu.create3DContext(canvas, {antialias: false});
+ if (!gl) {
+ testFailed("context does not exist");
+ return [0, 0];
+ }
+
+ hasDrawingBufferStorage = `drawingBufferStorage` in gl;
+ if (!hasDrawingBufferStorage) {
+ testPassed("drawingBufferStorage not present -- skipping test");
+ return;
+ }
+
+ maxRenderbufferSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
+ }
+
+ function testPixel(expected, actual, tol) {
+ let str = 'approx equal: expected: ' + expected + ', actual: ' + actual + ', tolerance: ' + tol;
+ for (let i = 0; i < 4; ++i) {
+ if (Math.abs(expected[i] - actual[i]) > tol) {
+ testFailed(str);
+ return;
+ }
+ }
+ testPassed(str);
+ }
+
+ function srgbToLinear(x) {
+ if (x < 0.0)
+ return 0.0;
+ if (x < 0.04045)
+ return x / 12.92;
+ if (x < 1.0) {
+ return Math.pow((x + 0.055)/1.044, 2.4);
+ }
+ return 1.0;
+ }
+
+ function testClearColor() {
+ // Make a fresh canvas.
+ let canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+
+ gl = wtu.create3DContext(canvas, {antialias: false});
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+ testPassed("context exists");
+ shouldBe('gl.drawingBufferFormat', 'gl.RGBA8');
+
+ let testCase = function(f, size, clearColor, expectedPixel, tolerance) {
+ format = f;
+ width = size[0];
+ height = size[1];
+
+ gl.drawingBufferStorage(format, width, height);
+ shouldBe('gl.getError()', 'gl.NO_ERROR');
+
+ shouldBe('gl.drawingBufferFormat', 'format');
+ shouldBe('gl.drawingBufferWidth', 'width');
+ shouldBe('gl.drawingBufferHeight', 'height');
+
+ gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ let buf;
+ if (format == 0x881A /*RGBA16F*/) {
+ buf = new Float32Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT, buf);
+ } else {
+ buf = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, buf);
+ }
+ testPixel(expectedPixel, buf, tolerance);
+ }
+
+ debug('Testing RGBA8');
+ testCase(gl.RGBA8, [16, 32],
+ [16 / 255, 32 / 255, 64 / 255, 128 / 255],
+ [16, 32, 64, 128],
+ 0);
+
+ // WebGL 1 must use EXT_sRGB for SRGB8_ALPHA8.
+ let srgb8_alpha8 = gl.SRGB8_ALPHA8;
+ if (!srgb8_alpha8) {
+ let ext = gl.getExtension('EXT_sRGB');
+ if (ext) {
+ srgb8_alpha8 = ext.SRGB8_ALPHA8_EXT;
+ }
+ }
+ if (srgb8_alpha8) {
+ debug('Testing SRGB8_ALPHA8');
+ testCase(srgb8_alpha8, [16, 32],
+ [srgbToLinear(64/255), srgbToLinear(16/255), srgbToLinear(32/255), 128 / 255],
+ [64, 16, 32, 128],
+ 1);
+ }
+
+ if (gl.getExtension('EXT_color_buffer_float')) {
+ // WebGL 1 must use EXT_color_buffer_half_float for RGBA16F.
+ let rgba16f = gl.RGBA16F;
+ if (!rgba16f) {
+ let ext = gl.getExtension('EXT_color_buffer_half_float');
+ if (ext) {
+ rgba16f = ext.RGBA16F_EXT;
+ }
+ }
+
+ debug('Testing RGBA16F');
+ testCase(rgba16f, [18, 28],
+ [0.25, 0.5, 0.75, 0.125],
+ [0.25, 0.5, 0.75, 0.125],
+ 0.00001);
+ } else {
+ debug('Skipping RGBA16F');
+ }
+
+ shouldBe('gl.getError()', 'gl.NO_ERROR');
+ }
+
+ function testNoAlpha() {
+ let canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+ gl = wtu.create3DContext(canvas, {alpha:false});
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+ debug('Testing alpha:false');
+
+ // Report RGB8 for the format.
+ shouldBe('gl.drawingBufferFormat', 'gl.RGB8');
+
+ // If WebGLContextAttributes.alpha is false, generate INVALID_OPERATION.
+ gl.drawingBufferStorage(gl.RGBA8, 16, 16);
+ shouldBe('gl.getError()', 'gl.INVALID_OPERATION');
+ }
+
+ function testMissingExtension() {
+ let canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+ gl = wtu.create3DContext(canvas);
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+
+ debug('Testing use of RGBA16F without enabling EXT_color_buffer_float');
+ gl.drawingBufferStorage(gl.RGBA16F, 16, 16);
+ shouldBe('gl.getError()', 'gl.INVALID_ENUM');
+ }
+
+ function testMaxSize() {
+ let canvas = document.createElement("canvas");
+ canvas.width = 16;
+ canvas.height = 16;
+ gl = wtu.create3DContext(canvas);
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+
+ debug('Testing maximum size');
+ gl.drawingBufferStorage(gl.RGBA8, maxRenderbufferSize, maxRenderbufferSize);
+ shouldBe('gl.getError()', 'gl.NONE');
+ shouldBe('gl.drawingBufferWidth', 'maxRenderbufferSize');
+ shouldBe('gl.drawingBufferHeight', 'maxRenderbufferSize');
+
+ debug('Testing over-maximum width and ehgith');
+ gl.drawingBufferStorage(gl.RGBA8, maxRenderbufferSize+1, 16);
+ shouldBe('gl.getError()', 'gl.INVALID_VALUE');
+ gl.drawingBufferStorage(gl.RGBA8, 16, maxRenderbufferSize+1);
+ shouldBe('gl.getError()', 'gl.INVALID_VALUE');
+ shouldBe('gl.drawingBufferWidth', 'maxRenderbufferSize');
+ shouldBe('gl.drawingBufferHeight', 'maxRenderbufferSize');
+ }
+
+ function testDrawToCanvas() {
+ let canvasGL = document.createElement("canvas");
+ canvasGL.width = 16;
+ canvasGL.height = 16;
+ gl = wtu.create3DContext(canvasGL);
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+
+ let canvas2D = document.createElement("canvas");
+ canvas2D.width = 16;
+ canvas2D.height = 16;
+ let ctx = canvas2D.getContext('2d');
+ let imageData = new ImageData(16, 16);
+
+ let testCase = function(f, clearColor, canvasColor, tolerance) {
+ gl.drawingBufferStorage(f, 16, 16);
+ gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ ctx.putImageData(imageData, 0, 0);
+ ctx.drawImage(canvasGL, 0, 0);
+ testPixel(canvasColor, ctx.getImageData(8, 8, 1, 1).data, tolerance);
+ }
+
+ debug('Drawing RGBA to canvas');
+ testCase(gl.RGBA8, [16/255, 32/255, 64/255, 64/255], [64, 128, 255, 64], 0);
+
+ // WebGL 1 must use EXT_sRGB for SRGB8_ALPHA8.
+ let srgb8_alpha8 = gl.SRGB8_ALPHA8;
+ if (!srgb8_alpha8) {
+ let ext = gl.getExtension('EXT_sRGB');
+ if (ext) {
+ srgb8_alpha8 = ext.SRGB8_ALPHA8_EXT;
+ }
+ }
+ if (srgb8_alpha8) {
+ debug('Drawing opaque SRGB8_ALPHA8 to canvas');
+ testCase(srgb8_alpha8,
+ [srgbToLinear(64/255), srgbToLinear(32/255), srgbToLinear(16/255), 1.0],
+ [64, 32, 16, 255],
+ 1);
+
+ debug('Drawing transparent SRGB8_ALPHA8 to canvas');
+ // We set the tolerance to 5 because of compounding error. The backbuffer
+ // may be off by 1, and then un-premultiplying alpha of 64/55 will multiply
+ // that error by 4. Then add one to be safe.
+ testCase(srgb8_alpha8,
+ [srgbToLinear(32/255), srgbToLinear(64/255), srgbToLinear(16/255), 64/255],
+ [128, 255, 64, 64],
+ 5);
+ }
+
+ if (gl.getExtension('EXT_color_buffer_float')) {
+ debug('Drawing transparent RGBA16F to canvas');
+ testCase(gl.RGBA16F,
+ [32/255, 64/255, 16/255, 64/255],
+ [128, 255, 64, 64],
+ 1);
+ }
+ }
+
+ let wtu = WebGLTestUtils;
+ initialize();
+ if (hasDrawingBufferStorage) {
+ testClearColor();
+ testNoAlpha();
+ testMissingExtension();
+ testMaxSize();
+ testDrawToCanvas();
+ }
+}
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js
index 51509e8a6e..2975ec0fe4 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/ext-color-buffer-half-float.js
@@ -422,6 +422,33 @@ if (!gl) {
runRGB16FNegativeTest();
}
+ if (version == 1) {
+ debug("");
+ debug("Testing that component type framebuffer attachment queries are rejected with the extension disabled");
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB565, 8, 8);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup renderbuffer should succeed.");
+ shouldBeNull('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 0x8211 /* FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE */)');
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Query must fail.");
+ gl.deleteRenderbuffer(rbo);
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 8, 8, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup texture should succeed.");
+ shouldBeNull('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 0x8211 /* FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE */)');
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Query must fail.");
+ gl.deleteTexture(tex);
+
+ gl.deleteFramebuffer(fbo);
+ }
+
let oesTextureHalfFloat = null;
if (version == 1) {
// oesTextureHalfFloat implicitly enables EXT_color_buffer_half_float if supported
@@ -466,6 +493,47 @@ if (!gl) {
runCopyTexImageTest(true);
runUniqueObjectTest();
+
+ {
+ debug("");
+ debug("Testing that component type framebuffer attachment queries are accepted with the extension enabled");
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0,gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB565, 8, 8);
+ shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT)', 'ext.UNSIGNED_NORMALIZED_EXT');
+ gl.renderbufferStorage(gl.RENDERBUFFER, ext.RGBA16F_EXT, 8, 8);
+ shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT)', 'gl.FLOAT');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No errors after valid renderbuffer attachment queries.");
+
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT,gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, 8, 8);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No errors after depth-stencil renderbuffer setup.");
+ shouldBeNull('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, ext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT)');
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Component type query is not allowed for combined depth-stencil attachments.");
+ gl.deleteRenderbuffer(rbo);
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 8, 8, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT)', 'ext.UNSIGNED_NORMALIZED_EXT');
+ const tex_ext = gl.getExtension("OES_texture_half_float");
+ if (version > 1 || tex_ext) {
+ if (version > 1)
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA16F, 8, 8, 0, gl.RGBA, gl.HALF_FLOAT, null);
+ else
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 8, 8, 0, gl.RGBA, tex_ext.HALF_FLOAT_OES, null);
+ shouldBe('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, ext.FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT)', 'gl.FLOAT');
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No errors after valid texture attachment queries.");
+ gl.deleteTexture(tex);
+
+ gl.deleteFramebuffer(fbo);
+ }
}
}
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js
index 14cf4628be..504b70564e 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-image-bitmap-from-video.js
@@ -14,7 +14,6 @@ function generateTest(internalFormat, pixelFormat, pixelType, prologue, resource
{ src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', },
{ src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', },
{ src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', },
- { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', },
];
function init()
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js
index 6e8bcf96e9..8dadde2d69 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-2d-with-video.js
@@ -27,7 +27,6 @@ function generateTest(internalFormat, pixelFormat, pixelType, prologue, resource
{ src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', },
{ src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', },
{ src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', },
- { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', },
];
function init()
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js
index a268f7d8d5..b1dbd33913 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-image-bitmap-from-video.js
@@ -14,7 +14,6 @@ function generateTest(internalFormat, pixelFormat, pixelType, prologue, resource
{ src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', },
{ src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', },
{ src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', },
- { src: resourcePath + "red-green.theora.ogv" , type: 'video/ogg; codecs="theora, vorbis"', },
];
function init()
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js
index 0c2c40e8a5..43ad660070 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/tex-image-and-sub-image-3d-with-video.js
@@ -29,7 +29,6 @@ function generateTest(internalFormat, pixelFormat, pixelType, prologue, resource
{ src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', },
{ src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', },
{ src: resourcePath + "red-green.webmvp8.webm", type: 'video/webm; codecs="vp8, vorbis"', },
- { src: resourcePath + "red-green.theora.ogv", type: 'video/ogg; codecs="theora, vorbis"', },
];
function init()
diff --git a/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-blend-func-extended.js b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-blend-func-extended.js
new file mode 100644
index 0000000000..086d9cfb16
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/js/tests/webgl-blend-func-extended.js
@@ -0,0 +1,548 @@
+"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;