summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout
diff options
context:
space:
mode:
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/canvas/00_test_list.txt2
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/canvas/drawingbuffer-storage-test.html27
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt6
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-clip-control.html185
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-depth-clamp.html168
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html59
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-polygon-offset-clamp.html172
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html34
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html8
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-mirror-clamp-to-edge.html217
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html26
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html65
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-blend-func-extended.html26
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-polygon-mode.html185
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/reading/read-pixels-test.html37
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/exif-orientation.html44
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html2
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-npot-video.html1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-srgb-upload.html16
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance/uniforms/uniform-location.html4
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/00_test_list.txt1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/canvas/00_test_list.txt1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/canvas/drawingbuffer-storage-test.html27
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt11
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-conservative-depth.html145
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-disjoint-timer-query-webgl2.html58
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-render-snorm.html201
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/nv-shader-noperspective-interpolation.html251
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html474
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-shader-multisample-interpolation.html313
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-blend-func-extended.html26
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-clip-cull-distance.html475
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-provoking-vertex.html165
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-render-shared-exponent.html251
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-shader-pixel-local-storage.html445
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-stencil-texturing.html279
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/00_test_list.txt1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/texture-bias.html146
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/query/00_test_list.txt1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/query/occlusion-query-scissor.html117
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html112
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html31
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html408
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/00_test_list.txt1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-10bpc.html66
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-new-formats.html30
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html11
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html66
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/00_test_list.txt12
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-16gb-wasm-memory.html51
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-4gb-wasm-memory.html51
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-16gb-wasm-memory.html52
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-4gb-wasm-memory.html52
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html48
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-4gb-wasm-memory.html48
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-16gb-wasm-memory.html51
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-4gb-wasm-memory.html50
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-16gb-wasm-memory.html85
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-4gb-wasm-memory.html85
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-16gb-wasm-memory.html86
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-4gb-wasm-memory.html86
-rw-r--r--dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js29
-rw-r--r--dom/canvas/test/webgl-conf/checkout/extra/tex-image-with-video-test.js1
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js21
-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
-rw-r--r--dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js45
-rw-r--r--dom/canvas/test/webgl-conf/checkout/py/lint/lint.allowlist3
-rw-r--r--dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-cw.jpg (renamed from dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-cw.jpg)bin4840 -> 4840 bytes
-rw-r--r--dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-ccw.jpg (renamed from dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-ccw.jpg)bin4841 -> 4841 bytes
-rw-r--r--dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-cw.jpg (renamed from dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-ccw.jpg)bin4901 -> 4901 bytes
-rw-r--r--dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-ccw.jpg (renamed from dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-cw.jpg)bin4900 -> 4900 bytes
-rw-r--r--dom/canvas/test/webgl-conf/checkout/resources/npot-video.theora.ogvbin24630 -> 0 bytes
-rw-r--r--dom/canvas/test/webgl-conf/checkout/resources/red-gradient-8x1-10bit-untagged.pngbin0 -> 90 bytes
-rw-r--r--dom/canvas/test/webgl-conf/checkout/resources/red-green.theora.ogvbin10292 -> 0 bytes
-rw-r--r--dom/canvas/test/webgl-conf/checkout/test-guidelines.md6
87 files changed, 6669 insertions, 496 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/00_test_list.txt
index 558163de17..c0cdadcd03 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/00_test_list.txt
@@ -4,6 +4,8 @@ canvas-test.html
canvas-zero-size.html
drawingbuffer-static-canvas-test.html
--min-version 1.0.2 drawingbuffer-hd-dpi-test.html
+# Uncomment once fully passing on WebGL 1
+# --min-version 1.0.4 --max-version 1.9.9 drawingbuffer-storage-test.html
drawingbuffer-test.html
--min-version 1.0.3 draw-webgl-to-canvas-test.html
--min-version 1.0.3 draw-static-webgl-to-multiple-canvas-test.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/canvas/drawingbuffer-storage-test.html b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/drawingbuffer-storage-test.html
new file mode 100644
index 0000000000..285cd047d1
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/canvas/drawingbuffer-storage-test.html
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL drawingBufferStorage Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/drawingbuffer-storage-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+runTest(1);
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt
index 9a72b67ef0..e121e90676 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/00_test_list.txt
@@ -1,15 +1,19 @@
--min-version 1.0.3 --max-version 1.9.9 angle-instanced-arrays.html
--min-version 1.0.3 --max-version 1.9.9 angle-instanced-arrays-out-of-bounds.html
--min-version 1.0.3 --max-version 1.9.9 ext-blend-minmax.html
+--min-version 1.0.4 ext-clip-control.html
--min-version 1.0.4 ext-color-buffer-half-float.html
+--min-version 1.0.4 ext-depth-clamp.html
--min-version 1.0.4 ext-float-blend.html
--min-version 1.0.4 ext-texture-compression-bptc.html
--min-version 1.0.4 ext-texture-compression-rgtc.html
--min-version 1.0.4 ext-disjoint-timer-query.html
--min-version 1.0.3 --max-version 1.9.9 ext-frag-depth.html
+--min-version 1.0.4 ext-polygon-offset-clamp.html
--min-version 1.0.3 --max-version 1.9.9 ext-shader-texture-lod.html
--min-version 1.0.3 --max-version 1.9.9 ext-sRGB.html
--min-version 1.0.2 ext-texture-filter-anisotropic.html
+--min-version 1.0.4 ext-texture-mirror-clamp-to-edge.html
--min-version 1.0.2 get-extension.html
--min-version 1.0.4 khr-parallel-shader-compile.html
--max-version 1.9.9 oes-standard-derivatives.html
@@ -29,6 +33,7 @@
--min-version 1.0.3 --max-version 1.9.9 oes-texture-half-float-with-video.html
--min-version 1.0.2 --max-version 1.9.9 oes-element-index-uint.html
--min-version 1.0.4 --max-version 1.9.9 oes-fbo-render-mipmap.html
+--min-version 1.0.4 --max-version 1.9.9 webgl-blend-func-extended.html
webgl-debug-renderer-info.html
webgl-debug-shaders.html
--min-version 1.0.4 webgl-compressed-texture-astc.html
@@ -44,3 +49,4 @@ webgl-debug-shaders.html
--min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-framebuffer-unsupported.html
--min-version 1.0.4 --max-version 1.9.9 webgl-draw-buffers-max-draw-buffers.html
--min-version 1.0.4 webgl-multi-draw.html
+--min-version 1.0.4 webgl-polygon-mode.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-clip-control.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-clip-control.html
new file mode 100644
index 0000000000..f5c557c839
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-clip-control.html
@@ -0,0 +1,185 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_clip_control Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_clip_control extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c");
+var ext;
+const w = gl.drawingBufferWidth;
+const h = gl.drawingBufferHeight;
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check the parameters without the extension");
+ shouldBeNull("gl.getParameter(0x935C /* CLIP_ORIGIN_EXT */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBeNull("gl.getParameter(0x935D /* CLIP_DEPTH_MODE_EXT */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.LOWER_LEFT_EXT", "0x8CA1");
+ shouldBe("ext.UPPER_LEFT_EXT", "0x8CA2");
+
+ shouldBe("ext.NEGATIVE_ONE_TO_ONE_EXT", "0x935E");
+ shouldBe("ext.ZERO_TO_ONE_EXT", "0x935F");
+
+ shouldBe("ext.CLIP_ORIGIN_EXT", "0x935C");
+ shouldBe("ext.CLIP_DEPTH_MODE_EXT", "0x935D");
+}
+
+function checkQueries() {
+ debug("");
+ debug("Check default state");
+ shouldBe('gl.getParameter(ext.CLIP_ORIGIN_EXT)', 'ext.LOWER_LEFT_EXT');
+ shouldBe('gl.getParameter(ext.CLIP_DEPTH_MODE_EXT)', 'ext.NEGATIVE_ONE_TO_ONE_EXT');
+ debug("");
+ debug("Check state updates using the new function");
+ ext.clipControlEXT(ext.UPPER_LEFT_EXT, ext.ZERO_TO_ONE_EXT);
+ shouldBe('gl.getParameter(ext.CLIP_ORIGIN_EXT)', 'ext.UPPER_LEFT_EXT');
+ shouldBe('gl.getParameter(ext.CLIP_DEPTH_MODE_EXT)', 'ext.ZERO_TO_ONE_EXT');
+ ext.clipControlEXT(ext.LOWER_LEFT_EXT, ext.ZERO_TO_ONE_EXT);
+ shouldBe('gl.getParameter(ext.CLIP_ORIGIN_EXT)', 'ext.LOWER_LEFT_EXT');
+ shouldBe('gl.getParameter(ext.CLIP_DEPTH_MODE_EXT)', 'ext.ZERO_TO_ONE_EXT');
+ ext.clipControlEXT(ext.UPPER_LEFT_EXT, ext.NEGATIVE_ONE_TO_ONE_EXT);
+ shouldBe('gl.getParameter(ext.CLIP_ORIGIN_EXT)', 'ext.UPPER_LEFT_EXT');
+ shouldBe('gl.getParameter(ext.CLIP_DEPTH_MODE_EXT)', 'ext.NEGATIVE_ONE_TO_ONE_EXT');
+ ext.clipControlEXT(ext.LOWER_LEFT_EXT, ext.NEGATIVE_ONE_TO_ONE_EXT);
+ shouldBe('gl.getParameter(ext.CLIP_ORIGIN_EXT)', 'ext.LOWER_LEFT_EXT');
+ shouldBe('gl.getParameter(ext.CLIP_DEPTH_MODE_EXT)', 'ext.NEGATIVE_ONE_TO_ONE_EXT');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkDepthMode() {
+ debug("");
+ debug("Check depth mode toggling");
+
+ gl.enable(gl.DEPTH_TEST);
+ gl.clear(gl.DEPTH_BUFFER_BIT);
+
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShader,
+ wtu.simpleColorFragmentShader]);
+ gl.useProgram(program);
+ const colorLoc = gl.getUniformLocation(program, "u_color");
+ wtu.setupUnitQuad(gl);
+
+ // Draw red at 0 with the default depth mode
+ gl.uniform4f(colorLoc, 1, 0, 0, 1);
+ ext.clipControlEXT(ext.LOWER_LEFT_EXT, ext.NEGATIVE_ONE_TO_ONE_EXT);
+ wtu.drawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h, [255, 0, 0, 255]);
+
+ // Draw green at 0, depth test must fail
+ gl.uniform4f(colorLoc, 0, 1, 0, 1);
+ wtu.drawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h, [255, 0, 0, 255]);
+
+ // Draw green at 0 after switching the depth mode
+ ext.clipControlEXT(ext.LOWER_LEFT_EXT, ext.ZERO_TO_ONE_EXT);
+ wtu.drawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h, [0, 255, 0, 255]);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkClipOrigin() {
+ debug("");
+ debug("Check clip origin toggling");
+
+ gl.disable(gl.DEPTH_TEST);
+
+ const vertexShader = `
+ attribute vec4 vPosition;
+ varying float y;
+ void main() {
+ gl_Position = vPosition;
+ y = vPosition.y;
+ }`;
+ const fragmentShader = `
+ precision mediump float;
+ varying float y;
+ void main() {
+ if (y > 0.0) {
+ gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ } else {
+ gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
+ }
+ }`;
+
+ const program = wtu.setupProgram(gl, [vertexShader, fragmentShader]);
+ gl.useProgram(program);
+
+ wtu.setupUnitQuad(gl);
+
+ ext.clipControlEXT(ext.LOWER_LEFT_EXT, ext.NEGATIVE_ONE_TO_ONE_EXT);
+ wtu.drawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h / 2 - 2, [0, 255, 0, 255]);
+ wtu.checkCanvasRect(gl, 0, h / 2 + 2, w, h / 2 - 2, [255, 0, 0, 255]);
+
+ ext.clipControlEXT(ext.UPPER_LEFT_EXT, ext.NEGATIVE_ONE_TO_ONE_EXT);
+ wtu.drawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h / 2 - 2, [255, 0, 0, 255]);
+ wtu.checkCanvasRect(gl, 0, h / 2 + 2, w, h / 2 - 2, [0, 255, 0, 255]);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function runTestExtension() {
+ checkEnums();
+ checkQueries();
+ checkDepthMode();
+ checkClipOrigin();
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("EXT_clip_control");
+
+ wtu.runExtensionSupportedTest(gl, "EXT_clip_control", ext !== null);
+
+ if (ext !== null) {
+ runTestExtension();
+ } else {
+ testPassed("No EXT_clip_control support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-depth-clamp.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-depth-clamp.html
new file mode 100644
index 0000000000..6082c9880c
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-depth-clamp.html
@@ -0,0 +1,168 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_depth_clamp Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_depth_clamp extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c");
+var ext;
+const w = gl.drawingBufferWidth;
+const h = gl.drawingBufferHeight;
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check the parameter without the extension");
+ shouldBeNull("gl.getParameter(0x864F /* DEPTH_CLAMP_EXT */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ debug("Check the cap without the extension");
+ gl.disable(0x864F /* DEPTH_CLAMP_EXT */);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "cap unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ gl.enable(0x864F /* DEPTH_CLAMP_EXT */);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "cap unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ shouldBeFalse("gl.isEnabled(0x864F /* DEPTH_CLAMP_EXT */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "cap unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.DEPTH_CLAMP_EXT", "0x864F");
+}
+
+function checkQueries() {
+ debug("");
+ debug("Check default state");
+ shouldBeFalse('gl.isEnabled(ext.DEPTH_CLAMP_EXT)');
+ shouldBeFalse('gl.getParameter(ext.DEPTH_CLAMP_EXT)');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ debug("");
+ debug("Check state update using the new capability");
+ gl.enable(ext.DEPTH_CLAMP_EXT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBeTrue('gl.isEnabled(ext.DEPTH_CLAMP_EXT)');
+ shouldBeTrue('gl.getParameter(ext.DEPTH_CLAMP_EXT)');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ gl.disable(ext.DEPTH_CLAMP_EXT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBeFalse('gl.isEnabled(ext.DEPTH_CLAMP_EXT)');
+ shouldBeFalse('gl.getParameter(ext.DEPTH_CLAMP_EXT)');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkClamping() {
+ debug("");
+ debug("Check depth clamp operation");
+
+ // Draw a red quad located beyond the clipping volume.
+ // When depth clamping is enabled, it must be drawn.
+ const vertexShader = `
+ attribute vec2 vPosition;
+ uniform float u_depth;
+ void main() {
+ gl_Position = vec4(vPosition, u_depth, 1.0);
+ }`;
+ const program = wtu.setupProgram(gl, [vertexShader,
+ wtu.simpleColorFragmentShader]);
+ gl.useProgram(program);
+ const colorLoc = gl.getUniformLocation(program, "u_color");
+ const depthLoc = gl.getUniformLocation(program, "u_depth");
+
+ wtu.setupUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ // Red beyond the far plane
+ gl.uniform4f(colorLoc, 1, 0, 0, 1);
+ gl.uniform1f(depthLoc, +2);
+
+ // Clipped
+ wtu.clearAndDrawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h, [255, 255, 255, 255]);
+
+ // Enable depth clamping, disable depth clipping
+ gl.enable(ext.DEPTH_CLAMP_EXT);
+
+ // Not clipped
+ wtu.clearAndDrawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h, [255, 0, 0, 255]);
+
+ // Green beyond the near plane
+ gl.uniform4f(colorLoc, 0, 1, 0, 1);
+ gl.uniform1f(depthLoc, -2);
+
+ // Not clipped
+ wtu.clearAndDrawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h, [0, 255, 0, 255]);
+
+ // Blue beyond the near plane with clamping disabled
+ gl.disable(ext.DEPTH_CLAMP_EXT);
+ gl.uniform4f(colorLoc, 0, 0, 1, 1);
+
+ // Clipped
+ wtu.clearAndDrawUnitQuad(gl);
+ wtu.checkCanvasRect(gl, 0, 0, w, h, [255, 255, 255, 255]);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function runTestExtension() {
+ checkEnums();
+ checkQueries();
+ checkClamping();
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("EXT_depth_clamp");
+
+ wtu.runExtensionSupportedTest(gl, "EXT_depth_clamp", ext !== null);
+
+ if (ext !== null) {
+ runTestExtension();
+ } else {
+ testPassed("No EXT_depth_clamp support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html
index 1fc29cc445..7965987c64 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-disjoint-timer-query.html
@@ -25,7 +25,9 @@ description("This test verifies the functionality of the EXT_disjoint_timer_quer
var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas);
+var gl2 = null;
var ext = null;
+var ext2 = null;
var query = null;
var query2 = null;
var elapsed_query = null;
@@ -61,6 +63,8 @@ if (!gl) {
runTimeStampTest();
}
verifyQueryResultsNotAvailable();
+ verifyDeleteQueryBehavior();
+ verifyDeleteQueryErrorBehavior();
window.requestAnimationFrame(checkQueryResults);
}
@@ -245,6 +249,61 @@ function verifyQueryResultsNotAvailable() {
testPassed("Queries' results didn't become available in a spin loop");
}
+function verifyDeleteQueryBehavior() {
+ debug("");
+ debug("Testing deleting an active query should end it.");
+
+ // Use a new context for this test
+ gl2 = wtu.create3DContext(null, null, 1);
+ if (!gl2) return;
+ ext2 = gl2.getExtension("EXT_disjoint_timer_query");
+ if (!ext2) return;
+
+ query = ext2.createQueryEXT();
+ ext2.beginQueryEXT(ext2.TIME_ELAPSED_EXT, query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE, "The query began successfully");
+ ext2.deleteQueryEXT(query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE, "Deletion of the active query succeeds");
+ shouldBeNull("ext2.getQueryEXT(ext2.TIME_ELAPSED_EXT, ext2.CURRENT_QUERY_EXT)");
+ shouldBeFalse("ext2.isQueryEXT(query)");
+ query = ext2.createQueryEXT();
+ ext2.beginQueryEXT(ext2.TIME_ELAPSED_EXT, query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE, "Beginning a new query succeeds");
+ ext2.endQueryEXT(ext2.TIME_ELAPSED_EXT);
+ ext2.deleteQueryEXT(query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE);
+ query = null;
+ ext2 = null;
+ gl2 = null;
+}
+
+function verifyDeleteQueryErrorBehavior() {
+ debug("");
+ debug("Testing deleting a query created by another context.");
+
+ // Use new contexts for this test
+ gl2 = wtu.create3DContext(null, null, 1);
+ var gl3 = wtu.create3DContext(null, null, 1);
+ if (!gl2 || !gl3) return;
+ ext2 = gl2.getExtension("EXT_disjoint_timer_query");
+ var ext3 = gl3.getExtension("EXT_disjoint_timer_query");
+ if (!ext2 || !ext3) return;
+
+ query = ext2.createQueryEXT();
+ ext2.beginQueryEXT(ext2.TIME_ELAPSED_EXT, query);
+ ext3.deleteQueryEXT(query);
+ wtu.glErrorShouldBe(gl3, gl3.INVALID_OPERATION);
+ shouldBeTrue("ext2.isQueryEXT(query)");
+ shouldBe("ext2.getQueryEXT(ext2.TIME_ELAPSED_EXT, ext2.CURRENT_QUERY_EXT)", "query");
+ ext2.endQueryEXT(ext2.TIME_ELAPSED_EXT);
+ ext2.deleteQueryEXT(query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE);
+ query = null;
+ ext2 = null;
+ gl2 = null;
+ gl3 = null;
+}
+
function checkQueryResults() {
if (availability_retry > 0) {
// Make a reasonable attempt to wait for the queries' results to become available.
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-polygon-offset-clamp.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-polygon-offset-clamp.html
new file mode 100644
index 0000000000..ce14e96fc7
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-polygon-offset-clamp.html
@@ -0,0 +1,172 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_polygon_offset_clamp Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_polygon_offset_clamp extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext();
+var ext;
+const w = gl.drawingBufferWidth;
+const h = gl.drawingBufferHeight;
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check the parameter without the extension");
+ shouldBeNull("gl.getParameter(0x8E1B /* POLYGON_OFFSET_CLAMP_EXT */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.POLYGON_OFFSET_CLAMP_EXT", "0x8E1B");
+}
+
+function checkQueries() {
+ debug("");
+ debug("Check default state");
+ shouldBe('gl.getParameter(gl.POLYGON_OFFSET_FACTOR)', '0.0');
+ shouldBe('gl.getParameter(gl.POLYGON_OFFSET_UNITS)', '0.0');
+ shouldBe('gl.getParameter(ext.POLYGON_OFFSET_CLAMP_EXT)', '0.0');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ debug("");
+ debug("Check state update using the new function");
+ ext.polygonOffsetClampEXT(1.0, 2.0, 3.0);
+ shouldBe('gl.getParameter(gl.POLYGON_OFFSET_FACTOR)', '1.0');
+ shouldBe('gl.getParameter(gl.POLYGON_OFFSET_UNITS)', '2.0');
+ shouldBe('gl.getParameter(ext.POLYGON_OFFSET_CLAMP_EXT)', '3.0');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ debug("");
+ debug("Check that the unextended function resets the clamp value to zero");
+ gl.polygonOffset(4.0, 5.0);
+ shouldBe('gl.getParameter(gl.POLYGON_OFFSET_FACTOR)', '4.0');
+ shouldBe('gl.getParameter(gl.POLYGON_OFFSET_UNITS)', '5.0');
+ shouldBe('gl.getParameter(ext.POLYGON_OFFSET_CLAMP_EXT)', '0.0');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkClamping() {
+ debug("");
+ debug("Check polygon offset clamp operation");
+
+ // The shader creates a depth slope from left (0) to right (1).
+ //
+ // This test issues two draw calls:
+ //
+ // Draw 2 (green): factor width, offset 0, clamp 0.5, depth test: Greater
+ // ^ | __________________
+ // | | __/ __/
+ // | V __/ __/ <--- Draw 1 (red): factor width, offset 0, clamp 0.25
+ // | __/ __/
+ // | __/ __/
+ // |/ __/
+ // | __/
+ // | __/
+ // |/
+ // |
+ // |
+ // +------------------------------->
+ //
+ // Result: <---------green--------><--red-->
+
+
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShader,
+ wtu.simpleColorFragmentShader]);
+ gl.useProgram(program);
+ const colorLoc = gl.getUniformLocation(program, "u_color");
+
+ const buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ new Float32Array([-1, -1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1]),
+ gl.STATIC_DRAW);
+ gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(0);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ gl.enable(gl.POLYGON_OFFSET_FILL);
+ gl.clearColor(0, 0, 0, 1);
+ gl.clearDepth(0);
+ gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
+
+ gl.enable(gl.DEPTH_TEST);
+
+ gl.depthFunc(gl.ALWAYS);
+ gl.uniform4f(colorLoc, 1, 0, 0, 1);
+ ext.polygonOffsetClampEXT(w, 0, 0.25);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ gl.depthFunc(gl.GREATER);
+ gl.uniform4f(colorLoc, 0, 1, 0, 1);
+ ext.polygonOffsetClampEXT(w, 0, 0.5);
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+ wtu.checkCanvasRect(
+ gl,
+ 0, 0, 3 * w / 4 - 1, h,
+ [0, 255, 0, 255], "should be green");
+
+ wtu.checkCanvasRect(
+ gl,
+ 3 * w / 4 + 1, 0, w / 4 - 1, h,
+ [255, 0, 0, 255], "should be red");
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function runTestExtension() {
+ checkEnums();
+ checkQueries();
+ checkClamping();
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("EXT_polygon_offset_clamp");
+
+ wtu.runExtensionSupportedTest(gl, "EXT_polygon_offset_clamp", ext !== null);
+
+ if (ext !== null) {
+ runTestExtension();
+ } else {
+ testPassed("No EXT_polygon_offset_clamp support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html
index 7e8353f7f2..a997bdb4ab 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-sRGB.html
@@ -222,6 +222,40 @@ if (!gl) {
runFormatTest(textureFormatFixture, false);
runFormatTest(renderbufferFormatFixture, false);
+ {
+ var fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ debug("Checking getFramebufferAttachmentParameter with a renderbuffer");
+ {
+ var rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB565, 1, 1);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ shouldBeNull('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 0x8210 /* FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT */)');
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ gl.deleteRenderbuffer(rbo);
+ }
+
+ debug("Checking getFramebufferAttachmentParameter with a texture");
+ {
+ var tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ shouldBeNull('gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, 0x8210 /* FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT */)');
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM);
+ gl.bindTexture(gl.TEXTURE_2D, null);
+ gl.deleteTexture(tex);
+ }
+
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.deleteFramebuffer(fbo);
+ }
+
debug("");
debug("Checking sRGB texture support");
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html
index 70dcf9ba7b..d9c34af25f 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-compression-rgtc.html
@@ -120,16 +120,16 @@ function runTestExtension() {
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RED_RGTC1_EXT, 4, 4, 0, gl.RED, gl.UNSIGNED_BYTE, null);
- wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_RED_RGTC1_EXT fails with texImage2D");
+ wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_RED_RGTC1_EXT fails with texImage2D");
gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_SIGNED_RED_RGTC1_EXT, 4, 4, 0, gl.RED, gl.BYTE, null);
- wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_SIGNED_RED_RGTC1_EXT fails with texImage2D");
+ wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_SIGNED_RED_RGTC1_EXT fails with texImage2D");
gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_RED_GREEN_RGTC2_EXT, 4, 4, 0, gl.RG, gl.UNSIGNED_BYTE, null);
- wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_RED_GREEN_RGTC2_EXT fails with texImage2D");
+ wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_RED_GREEN_RGTC2_EXT fails with texImage2D");
gl.texImage2D(gl.TEXTURE_2D, 0, ext.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT, 4, 4, 0, gl.RG, gl.BYTE, null);
- wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT fails with texImage2D");
+ wtu.glErrorShouldBe(gl, [gl.INVALID_VALUE, gl.INVALID_OPERATION], "COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT fails with texImage2D");
gl.deleteTexture(tex);
}
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-mirror-clamp-to-edge.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-mirror-clamp-to-edge.html
new file mode 100644
index 0000000000..4e2f980b4b
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-mirror-clamp-to-edge.html
@@ -0,0 +1,217 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_texture_mirror_clamp_to_edge Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_texture_mirror_clamp_to_edge extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c");
+const w = gl.drawingBufferWidth;
+const h = gl.drawingBufferHeight;
+var ext;
+var sampler;
+
+const pnames = ['TEXTURE_WRAP_S', 'TEXTURE_WRAP_T'];
+if (gl.TEXTURE_WRAP_R) {
+ pnames.push('TEXTURE_WRAP_R');
+}
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check the texture parameter without the extension");
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+
+ const MIRROR_CLAMP_TO_EDGE_EXT = 0x8743;
+
+ for (const pname of pnames) {
+ gl.texParameteri(gl.TEXTURE_2D, gl[pname], MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `value unknown for ${pname} via texParameteri without enabling the extension`);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+ gl.texParameterf(gl.TEXTURE_2D, gl[pname], MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `value unknown for ${pname} via texParameterf without enabling the extension`);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+ }
+
+ if (!gl.createSampler) return;
+
+ const sampler = gl.createSampler();
+ for (const pname of pnames) {
+ gl.samplerParameteri(sampler, gl[pname], MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `value unknown for ${pname} via samplerParameteri without enabling the extension`);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+ gl.samplerParameterf(sampler, gl[pname], MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, `value unknown for ${pname} via samplerParameterf without enabling the extension`);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+ }
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.MIRROR_CLAMP_TO_EDGE_EXT", "0x8743");
+}
+
+function checkQueries() {
+ debug("");
+ debug("Check texture and sampler state updates");
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+
+ for (const pname of pnames) {
+ gl.texParameteri(gl.TEXTURE_2D, gl[pname], ext.MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texParameteri");
+ shouldBe(`gl.getTexParameter(gl.TEXTURE_2D, gl.${pname})`, "ext.MIRROR_CLAMP_TO_EDGE_EXT");
+ gl.texParameteri(gl.TEXTURE_2D, gl[pname], gl.REPEAT);
+
+ gl.texParameterf(gl.TEXTURE_2D, gl[pname], ext.MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from texParameterf");
+ shouldBe(`gl.getTexParameter(gl.TEXTURE_2D, gl.${pname})`, "ext.MIRROR_CLAMP_TO_EDGE_EXT");
+ gl.texParameterf(gl.TEXTURE_2D, gl[pname], gl.REPEAT);
+ }
+
+ if (!gl.createSampler) return;
+
+ sampler = gl.createSampler();
+ for (const pname of pnames) {
+ gl.samplerParameteri(sampler, gl[pname], ext.MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from samplerParameteri");
+ shouldBe(`gl.getSamplerParameter(sampler, gl.${pname})`, "ext.MIRROR_CLAMP_TO_EDGE_EXT");
+ gl.samplerParameteri(sampler, gl[pname], gl.REPEAT);
+
+ gl.samplerParameterf(sampler, gl[pname], ext.MIRROR_CLAMP_TO_EDGE_EXT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors from samplerParameterf");
+ shouldBe(`gl.getSamplerParameter(sampler, gl.${pname})`, "ext.MIRROR_CLAMP_TO_EDGE_EXT");
+ gl.samplerParameterf(sampler, gl[pname], gl.REPEAT);
+ }
+}
+
+function checkSampling() {
+ debug("");
+ debug(`Check texture sampling with mirror-clamp-to-edge mode`);
+
+ wtu.setupUnitQuad(gl);
+ const vs = `precision highp float;
+ attribute vec4 vPosition;
+ varying vec2 texCoord;
+ void main() {
+ gl_Position = vec4(vPosition.xy, 0.0, 1.0);
+ texCoord = vPosition.xy * 2.0;
+ }`;
+ const program = wtu.setupProgram(gl, [vs, wtu.simpleTextureFragmentShader]);
+ gl.useProgram(program);
+
+ const black = [ 0, 0, 0, 255];
+ const red = [255, 0, 0, 255];
+ const green = [ 0, 255, 0, 255];
+ const blue = [ 0, 0, 255, 255];
+ const data = new Uint8Array([...black, ...red, ...green, ...blue]);
+
+ function checkPixels() {
+ function checkPixel(x, y, color) {
+ const screen = (s, t) => s * (t * 0.5 + 0.5);
+ wtu.checkCanvasRect(gl, screen(w, x), screen(h, y), 1, 1, color,
+ `(${x.toFixed(3)}, ${y.toFixed(3)}): ${color} `);
+ }
+ for (const signX of [+1, -1]) {
+ for (const signY of [+1, -1]) {
+ // This function expects screen-space coordinates
+ // normalized to [-1, +1]. The region from [0, 0]
+ // to [+1, +1] behaves like regular clamp-to-edge.
+ // Other three quadrants should be mirrored.
+ checkPixel(signX * 0.125, signY * 0.125, black);
+ checkPixel(signX * 0.375, signY * 0.125, red);
+ checkPixel(signX * 0.750, signY * 0.125, red);
+ checkPixel(signX * 0.125, signY * 0.375, green);
+ checkPixel(signX * 0.125, signY * 0.750, green);
+ checkPixel(signX * 0.375, signY * 0.375, blue);
+ checkPixel(signX * 0.750, signY * 0.375, blue);
+ checkPixel(signX * 0.375, signY * 0.750, blue);
+ checkPixel(signX * 0.750, signY * 0.750, blue);
+ }
+ }
+ }
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, ext.MIRROR_CLAMP_TO_EDGE_EXT);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, ext.MIRROR_CLAMP_TO_EDGE_EXT);
+
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture created and configured");
+
+ wtu.drawUnitQuad(gl);
+ checkPixels();
+
+ if (!gl.createSampler) return;
+
+ debug("");
+ debug(`Check texture sampling with mirror-clamp-to-edge mode using a sampler object`);
+
+ const texWithSampler = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, texWithSampler);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2, 2, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
+
+ sampler = gl.createSampler();
+ gl.bindSampler(0, sampler);
+ gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_S, ext.MIRROR_CLAMP_TO_EDGE_EXT);
+ gl.samplerParameteri(sampler, gl.TEXTURE_WRAP_T, ext.MIRROR_CLAMP_TO_EDGE_EXT);
+ gl.samplerParameteri(sampler, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.samplerParameteri(sampler, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture created and sampler configured");
+
+ wtu.drawUnitQuad(gl);
+ checkPixels();
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+ testPassed("context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("EXT_texture_mirror_clamp_to_edge");
+ wtu.runExtensionSupportedTest(gl, "EXT_texture_mirror_clamp_to_edge", ext !== null);
+
+ if (ext !== null) {
+ checkEnums();
+ checkQueries();
+ checkSampling();
+ } else {
+ testPassed("No EXT_texture_mirror_clamp_to_edge support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html
index 2915a2d2de..6a8b1981e5 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/khr-parallel-shader-compile.html
@@ -169,6 +169,32 @@ let extraCode = '';
testPassed(`COMPLETION_STATUS_KHR sucessfully transitioned from false to true`);
}
+ debug("Checking that compiling lots of programs in parallel eventually completes.");
+ let programs = [];
+ for (let i = 0; i < 256; ++i) {
+ gl.shaderSource(vs, vertexSource());
+ gl.shaderSource(fs, fragmentSource());
+ gl.compileShader(vs);
+ gl.compileShader(fs);
+ let program = gl.createProgram();
+ gl.attachShader(program, vs);
+ gl.attachShader(program, fs);
+ gl.linkProgram(program);
+ programs.push(program);
+ }
+ let allDone = false;
+ while (!allDone) {
+ allDone = true;
+ for (let i = 0; i < programs.length; ++i) {
+ if (!gl.getProgramParameter(programs[i], COMPLETION_STATUS_KHR)) {
+ allDone = false;
+ break;
+ }
+ }
+ if (!allDone) {
+ await new Promise(requestAnimationFrame);
+ }
+ }
debug("Checking that status is true when context is lost.");
if (loseContext) {
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html
index 3ccbd50f15..cb406706f9 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float-with-video.html
@@ -33,7 +33,6 @@ function testPrologue(gl) {
<video width="640" height="228" id="vid" controls muted>
<source src="../../resources/red-green.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
<source src="../../resources/red-green.webmvp8.webm" type='video/webm; codecs="vp8, vorbis"' />
- <source src="../../resources/red-green.theora.ogv" type='video/ogg; codecs="theora, vorbis"' />
</video>
</body>
</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html
index a757cb22ec..8bec35b9cf 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-float.html
@@ -60,6 +60,7 @@ debug("");
var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas);
+var ext = null;
if (!gl) {
testFailed("WebGL context does not exist");
@@ -81,6 +82,33 @@ if (!gl) {
// the extension has not been enabled yet.
runTextureCreationTest(testProgram, false);
+ {
+ 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_EXT */)');
+ 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_EXT */)');
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "Query must fail.");
+ gl.deleteTexture(tex);
+
+ gl.deleteFramebuffer(fbo);
+ }
+
if (!gl.getExtension("OES_texture_float")) {
testPassed("No OES_texture_float support -- this is legal");
} else {
@@ -109,6 +137,43 @@ if (!gl) {
runRenderTargetAndReadbackTest(testProgram, gl.RGBA, 4, [10000, 10000, 10000, 10000], 0.5, true);
runFramebufferTest();
+ {
+ debug("");
+ debug("Testing that component type framebuffer attachment queries are accepted with the extension enabled");
+ ext = gl.getExtension("WEBGL_color_buffer_float");
+ shouldBeNonNull('ext');
+ 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.RGBA32F_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');
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 8, 8, 0, gl.RGBA, gl.FLOAT, 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);
+ }
+
debug("");
debug("Test float32 blending without EXT_float_blend.");
testExtFloatBlend(gl.RGBA);
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html
index 719b332113..d6076b29f9 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-texture-half-float-with-video.html
@@ -38,7 +38,6 @@ function testPrologue(gl) {
<video width="640" height="228" id="vid" controls muted>
<source src="../../resources/red-green.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"' />
<source src="../../resources/red-green.webmvp8.webm" type='video/webm; codecs="vp8, vorbis"' />
- <source src="../../resources/red-green.theora.ogv" type='video/ogg; codecs="theora, vorbis"' />
</video>
</body>
</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-blend-func-extended.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-blend-func-extended.html
new file mode 100644
index 0000000000..62e25adaa2
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-blend-func-extended.html
@@ -0,0 +1,26 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL 1.0 WEBGL_blend_func_extended Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+var contextVersion = 1;
+</script>
+<script src="../../js/tests/webgl-blend-func-extended.js"></script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-polygon-mode.html b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-polygon-mode.html
new file mode 100644
index 0000000000..fd65f692a6
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-polygon-mode.html
@@ -0,0 +1,185 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_polygon_mode Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the WEBGL_polygon_mode extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c");
+var ext;
+const w = gl.drawingBufferWidth;
+const h = gl.drawingBufferHeight;
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check the parameters without the extension");
+ shouldBeNull("gl.getParameter(0x0B40 /* POLYGON_MODE_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBeNull("gl.getParameter(0x2A02 /* POLYGON_OFFSET_LINE_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ debug("Check the cap without the extension");
+ gl.disable(0x2A02 /* POLYGON_OFFSET_LINE_WEBGL */);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "cap unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ gl.enable(0x2A02 /* POLYGON_OFFSET_LINE_WEBGL */);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "cap unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ shouldBeFalse("gl.isEnabled(0x2A02 /* POLYGON_OFFSET_LINE_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "cap unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.POLYGON_MODE_WEBGL", "0x0B40");
+ shouldBe("ext.POLYGON_OFFSET_LINE_WEBGL", "0x2A02");
+ shouldBe("ext.LINE_WEBGL", "0x1B01");
+ shouldBe("ext.FILL_WEBGL", "0x1B02");
+}
+
+function checkQueries() {
+ debug("");
+ debug("Check default state");
+ shouldBe('gl.getParameter(ext.POLYGON_MODE_WEBGL)', 'ext.FILL_WEBGL');
+ shouldBeFalse('gl.getParameter(ext.POLYGON_OFFSET_LINE_WEBGL)');
+ shouldBeFalse('gl.isEnabled(ext.POLYGON_OFFSET_LINE_WEBGL)');
+ debug("");
+ debug("Check state updates");
+ ext.polygonModeWEBGL(gl.FRONT_AND_BACK, ext.LINE_WEBGL);
+ shouldBe('gl.getParameter(ext.POLYGON_MODE_WEBGL)', 'ext.LINE_WEBGL');
+ ext.polygonModeWEBGL(gl.FRONT_AND_BACK, ext.FILL_WEBGL);
+ shouldBe('gl.getParameter(ext.POLYGON_MODE_WEBGL)', 'ext.FILL_WEBGL');
+ debug("");
+ debug("Check errors");
+ ext.polygonModeWEBGL(gl.FRONT, ext.LINE_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "invalid face");
+ ext.polygonModeWEBGL(gl.FRONT_AND_BACK, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "invalid mode");
+ shouldBe('gl.getParameter(ext.POLYGON_MODE_WEBGL)', 'ext.FILL_WEBGL');
+ debug("");
+ debug("Check cap updates");
+ gl.enable(ext.POLYGON_OFFSET_LINE_WEBGL);
+ shouldBeTrue('gl.getParameter(ext.POLYGON_OFFSET_LINE_WEBGL)');
+ shouldBeTrue('gl.isEnabled(ext.POLYGON_OFFSET_LINE_WEBGL)');
+ gl.disable(ext.POLYGON_OFFSET_LINE_WEBGL);
+ shouldBeFalse('gl.getParameter(ext.POLYGON_OFFSET_LINE_WEBGL)');
+ shouldBeFalse('gl.isEnabled(ext.POLYGON_OFFSET_LINE_WEBGL)');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function checkDiagonal(r, g, b) {
+ const pixels = new Uint8Array(w * h * 4);
+ gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+ for (let i = 0; i < w; i++)
+ {
+ const baseOffset = (i * w + i) * 4;
+ if (pixels[baseOffset + 0] != r ||
+ pixels[baseOffset + 1] != g ||
+ pixels[baseOffset + 2] != b) {
+ testFailed(`Unexpected diagonal color at (${i}, ${i})`);
+ return;
+ }
+ }
+ testPassed("Expected diagonal color");
+}
+
+function checkLineMode() {
+ debug("");
+ debug("Check line polygon mode");
+
+ gl.enable(gl.DEPTH_TEST);
+
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShader,
+ wtu.simpleColorFragmentShader]);
+ gl.useProgram(program);
+ const colorLoc = gl.getUniformLocation(program, "u_color");
+
+ wtu.setupUnitQuad(gl);
+
+ // Draw red quad with lines
+ gl.uniform4f(colorLoc, 1, 0, 0, 1);
+ ext.polygonModeWEBGL(gl.FRONT_AND_BACK, ext.LINE_WEBGL);
+ wtu.clearAndDrawUnitQuad(gl);
+
+ // Nothing is drawn inside triangles
+ wtu.checkCanvasRect(gl, 2, 17, 13, 13, [255, 255, 255, 255]);
+ wtu.checkCanvasRect(gl, 17, 2, 13, 13, [255, 255, 255, 255]);
+
+ // Main diagonal is drawn
+ checkDiagonal(255, 0, 0);
+
+ // Test polygon offset
+ gl.polygonOffset(0, -2);
+ gl.enable(gl.POLYGON_OFFSET_FILL);
+
+ // Depth test must fail because line mode uses its own polygon offset toggle
+ gl.uniform4f(colorLoc, 0, 1, 0, 1);
+ wtu.drawUnitQuad(gl);
+ checkDiagonal(255, 0, 0);
+
+ // Depth test must pass
+ gl.enable(ext.POLYGON_OFFSET_LINE_WEBGL)
+ wtu.drawUnitQuad(gl);
+ checkDiagonal(0, 255, 0);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+}
+
+function runTestExtension() {
+ checkEnums();
+ checkQueries();
+ checkLineMode();
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("WEBGL_polygon_mode");
+
+ wtu.runExtensionSupportedTest(gl, "WEBGL_polygon_mode", ext !== null);
+
+ if (ext !== null) {
+ runTestExtension();
+ } else {
+ testPassed("No WEBGL_polygon_mode support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/reading/read-pixels-test.html b/dom/canvas/test/webgl-conf/checkout/conformance/reading/read-pixels-test.html
index 078b436427..3791be2448 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/reading/read-pixels-test.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/reading/read-pixels-test.html
@@ -39,6 +39,43 @@ function runTest(canvas, antialias) {
gl = wtu.create3DContext(canvas, {antialias: antialias});
var contextVersion = wtu.getDefault3DContextVersion();
+ debug("");
+ debug("Test null pixels");
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "null pixels");
+
+ debug("");
+ debug("Test pixels size");
+ gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0));
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "empty pixels array with 0x0 read data");
+ gl.readPixels(0, 0, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0));
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "empty pixels array with 1x0 read data");
+ gl.readPixels(0, 0, 0, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0));
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "empty pixels array with 0x1 read data");
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(3));
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "small pixels array for 1x1 read data");
+ if (contextVersion >= 2) {
+ gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(0), 1);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE, "offset is greater than array size");
+ gl.readPixels(0, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(1), 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no space left in pixels array with 0x0 read data");
+ gl.readPixels(0, 0, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(1), 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no space left in pixels array with 1x0 read data");
+ gl.readPixels(0, 0, 0, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(1), 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no space left in pixels array with 0x1 read data");
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4), 1);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "no space left in pixels array with 1x1 read data");
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(5), 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "read 1x1 data fits into pixels with offset");
+ }
+
+ debug("");
+ debug("Test combined depth-stencil type");
+ // The combined type is undefined in WebGL 1.0 and never allowed as a read type in WebGL 2.0
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, 0x8DAD /* FLOAT_32_UNSIGNED_INT_24_8_REV */, new Uint8Array(32));
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "FLOAT_32_UNSIGNED_INT_24_8_REV is rejected");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no extra error generated");
+
var width = 2;
var height = 2;
var continueTestFunc = continueTestPart1;
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/exif-orientation.html b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/exif-orientation.html
index 3fd596d445..5a4b88e5b8 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/exif-orientation.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/exif-orientation.html
@@ -56,35 +56,20 @@ function checkPixels(flipY)
}
}
-async function testImageBitmapFromBlobWithFlipY(blob, flipY)
+async function testImageBitmapWithFlipY(source, flipY)
{
- let bitmap;
- // As a concession to Firefox, which doesn't yet implement
- // createImageBitmap with creation options, skip the tests
- // involving flipY=true if ImageBitmap creation throws an
- // exception, and use the single-argument constructor for the
- // flipY=false case.
- if (flipY) {
- try {
- bitmap = await createImageBitmap(blob, {imageOrientation: flipY});
- } catch (e) {
- output(" (createImageBitmap options not supported - skipping flipY=true case)");
- return;
- }
- } else {
- bitmap = await createImageBitmap(blob);
- }
+ const bitmap = await createImageBitmap(source, flipY ? {imageOrientation: flipY} : undefined);
output(" Testing texImage2D, flipY = " + flipY);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]);
- checkPixels(flipY);
+ checkPixels(flipY == "flipY");
output(" Testing texSubImage2D, flipY = " + flipY);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, bitmap.width, bitmap.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]);
- checkPixels(flipY);
+ checkPixels(flipY == "flipY");
}
async function testImageBitmapFromBlob(filename)
@@ -93,8 +78,16 @@ async function testImageBitmapFromBlob(filename)
let blob = await response.blob();
output("----------------------------------------------------------------");
output("Testing " + filename + " via ImageBitmap from Blob");
- await testImageBitmapFromBlobWithFlipY(blob, true);
- await testImageBitmapFromBlobWithFlipY(blob, false);
+ await testImageBitmapWithFlipY(blob, "flipY");
+ await testImageBitmapWithFlipY(blob, "none");
+ await testImageBitmapWithFlipY(blob, undefined);
+}
+
+async function testImageBitmapFromImage(image)
+{
+ await testImageBitmapWithFlipY(image, "flipY");
+ await testImageBitmapWithFlipY(image, "none");
+ await testImageBitmapWithFlipY(image, undefined);
}
async function testImageElementWithFlipY(image, flipY)
@@ -124,6 +117,11 @@ async function testImageElement(filename)
await testImageElementWithFlipY(image, true);
await testImageElementWithFlipY(image, false);
+
+ output("----------------------------------------------------------------");
+ output("Testing " + filename + " via ImageBitmap from HTMLImageElement");
+
+ await testImageBitmapFromImage(image);
}
async function testSingleImage(filename)
@@ -149,9 +147,9 @@ async function run()
"exif-orientation-test-3-rotate-180.jpg",
"exif-orientation-test-4-mirror-vertical.jpg",
"exif-orientation-test-5-mirror-horizontal-90-ccw.jpg",
- "exif-orientation-test-6-90-ccw.jpg",
+ "exif-orientation-test-6-90-cw.jpg",
"exif-orientation-test-7-mirror-horizontal-90-cw.jpg",
- "exif-orientation-test-8-90-cw.jpg",
+ "exif-orientation-test-8-90-ccw.jpg",
];
for (let fn of filenames) {
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html
index ee9bad4341..1acc3a4380 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/tex-video-using-tex-unit-non-zero.html
@@ -96,8 +96,6 @@ found in the LICENSE.txt file.
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"' },
];
var currentVideo = null;
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-npot-video.html b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-npot-video.html
index ef979d4c5a..9a8e3198ba 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-npot-video.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-npot-video.html
@@ -154,7 +154,6 @@ function runTest(videoElement)
<video id="vid" style="display:none;" muted>
<source src="../../../resources/npot-video.mp4" type='video/mp4; codecs="avc1.42E01E"' />
<source src="../../../resources/npot-video.webmvp8.webm" type='video/webm; codecs="vp8"' />
- <source src="../../../resources/npot-video.theora.ogv" type='video/ogg; codecs="theora"' />
</video>
</body>
</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-srgb-upload.html b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-srgb-upload.html
index 3508670563..6f66bfdde6 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-srgb-upload.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-srgb-upload.html
@@ -62,12 +62,22 @@ function invoke(fn) { return fn(); }
invoke(async () => {
const video = document.createElement("video");
video.src = DATA_URL_FOR_720p_png_bt709_bt709_tv_yuv420p_vp9_webm;
- //video.src = "Big_Buck_Bunny_360_10s_1MB.mp4";
- //video.src = "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4";
+ if (!video.canPlayType('video/webm')) {
+ debug('Browser can not play webm videos. Skipping test.');
+ finishTest();
+ return;
+ }
+
video.muted = true;
video.loop = true;
video.crossOrigin = "anonymous";
- await video.play();
+ try {
+ await video.play();
+ } catch (e) {
+ debug('Browser could not play this specific video. Skipping test.');
+ finishTest();
+ return;
+ }
function renderTex(canvas, fn_tex_image) {
const gl = canvas.gl = wtu.create3DContext(canvas);
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html
index a0f30dc89f..b5ba6afed3 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/textures/misc/texture-upload-size.html
@@ -99,7 +99,6 @@ var tests = [
{type: "video", src: "../../../resources/red-green.mp4", videoType: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"'},
{type: "video", src: "../../../resources/red-green.bt601.vp9.webm", videoType: 'video/webm; codecs="vp9"'},
{type: "video", src: "../../../resources/red-green.webmvp8.webm", videoType: 'video/webm; codecs="vp8, vorbis"'},
- {type: "video", src: "../../../resources/red-green.theora.ogv", videoType: 'video/ogg; codecs="theora, vorbis"'},
];
var testIndex = 0;
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance/uniforms/uniform-location.html b/dom/canvas/test/webgl-conf/checkout/conformance/uniforms/uniform-location.html
index 3b1c185caf..6de8114651 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance/uniforms/uniform-location.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance/uniforms/uniform-location.html
@@ -68,6 +68,10 @@ contextA.stencilMask(1);
wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.linkProgram(programS)");
wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1i(locationSx, 3)");
wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.getUniform(programS, locationSx)");
+// Make sure that with no current program, uniform location validation doesn't get confused.
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.useProgram(null)");
+wtu.shouldGenerateGLError(contextA, contextA.INVALID_OPERATION, "contextA.uniform1i(locationSx, 3)");
+wtu.shouldGenerateGLError(contextA, contextA.NO_ERROR, "contextA.useProgram(programS)");
// Retrieve the locations again, and they should be good.
locationSx = contextA.getUniformLocation(programS, "u_struct.x");
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/00_test_list.txt
index e251dc9758..fabc72c1da 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/00_test_list.txt
@@ -18,3 +18,4 @@ textures/00_test_list.txt
transform_feedback/00_test_list.txt
uniforms/00_test_list.txt
vertex_arrays/00_test_list.txt
+--min-version 2.0.1 wasm/00_test_list.txt
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/canvas/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/canvas/00_test_list.txt
index 35e011f3bf..878a2797c4 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/canvas/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/canvas/00_test_list.txt
@@ -1 +1,2 @@
+--min-version 2.0.1 drawingbuffer-storage-test.html
--min-version 2.0.1 to-data-url-with-pack-params.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/canvas/drawingbuffer-storage-test.html b/dom/canvas/test/webgl-conf/checkout/conformance2/canvas/drawingbuffer-storage-test.html
new file mode 100644
index 0000000000..6f18d441c0
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/canvas/drawingbuffer-storage-test.html
@@ -0,0 +1,27 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL2 drawingBufferStorage Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/drawingbuffer-storage-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+runTest(2);
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt
index 559071ff06..2cc4456ecb 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/00_test_list.txt
@@ -1,11 +1,16 @@
ext-color-buffer-float.html
--min-version 2.0.1 ext-color-buffer-half-float.html
+--min-version 2.0.1 ext-conservative-depth.html
ext-disjoint-timer-query-webgl2.html
+--min-version 2.0.1 ext-render-snorm.html
--min-version 2.0.1 ext-texture-filter-anisotropic.html
--min-version 2.0.1 ext-texture-norm16.html
promoted-extensions.html
promoted-extensions-in-shaders.html
+--min-version 2.0.1 nv-shader-noperspective-interpolation.html
--min-version 2.0.1 oes-draw-buffers-indexed.html
+--min-version 2.0.1 oes-sample-variables.html
+--min-version 2.0.1 oes-shader-multisample-interpolation.html
--min-version 2.0.1 ovr_multiview2.html
--min-version 2.0.1 ovr_multiview2_depth.html
--min-version 2.0.1 ovr_multiview2_draw_buffers.html
@@ -16,4 +21,10 @@ promoted-extensions-in-shaders.html
--min-version 2.0.1 ovr_multiview2_timer_query.html
--min-version 2.0.1 ovr_multiview2_transform_feedback.html
--min-version 2.0.1 required-extensions.html
+--min-version 2.0.1 webgl-blend-func-extended.html
+--min-version 2.0.1 webgl-clip-cull-distance.html
--min-version 2.0.1 webgl-multi-draw-instanced-base-vertex-base-instance.html
+--min-version 2.0.1 webgl-provoking-vertex.html
+--min-version 2.0.1 webgl-render-shared-exponent.html
+--min-version 2.0.1 webgl-shader-pixel-local-storage.html
+--min-version 2.0.1 webgl-stencil-texturing.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-conservative-depth.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-conservative-depth.html
new file mode 100644
index 0000000000..c9c9f85bdb
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-conservative-depth.html
@@ -0,0 +1,145 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_conservative_depth Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_conservative_depth extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c", null, 2);
+var ext;
+
+function runShaderTests(extensionEnabled) {
+ debug("");
+ debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
+
+ const macro = `#version 300 es
+ precision mediump float;
+ out vec4 my_FragColor;
+ void main() {
+ #ifdef GL_EXT_conservative_depth
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ #else
+ #error no GL_EXT_conservative_depth;
+ #endif
+ }`;
+
+ const missingExtension = `#version 300 es
+ precision mediump float;
+ out vec4 my_FragColor;
+ layout (depth_any) out float gl_FragDepth;
+ void main() {
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ gl_FragDepth = 1.0;
+ }`;
+
+ const valid = `#version 300 es
+ #extension GL_EXT_conservative_depth : enable
+ precision mediump float;
+ out vec4 my_FragColor;
+ layout (depth_any) out float gl_FragDepth;
+ void main() {
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ gl_FragDepth = 1.0;
+ }`;
+
+ const invalid = `#version 300 es
+ #extension GL_EXT_conservative_depth : enable
+ precision mediump float;
+ out vec4 my_FragColor;
+ layout (depth_unchanged) out float gl_FragDepth;
+ void main() {
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ gl_FragDepth = 1.0;
+ }`;
+
+ // Always expect the shader missing the #extension pragma to fail (whether enabled or not)
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, missingExtension])) {
+ testFailed("Depth layout qualifier allowed without #extension pragma");
+ } else {
+ testPassed("Depth layout qualifier disallowed without #extension pragma");
+ }
+
+ // Expect the macro shader to succeed ONLY if enabled
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, macro])) {
+ 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");
+ }
+ }
+
+ // Try to compile a shader using a layout qualifier that should only succeed if enabled
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, valid])) {
+ if (extensionEnabled) {
+ testPassed("Depth layout qualifier compiled successfully when extension enabled");
+ } else {
+ testFailed("Depth layout qualifier compiled successfully when extension disabled");
+ }
+ } else {
+ if (extensionEnabled) {
+ testFailed("Depth layout qualifier failed to compile when extension enabled");
+ } else {
+ testPassed("Depth layout qualifier failed to compile when extension disabled");
+ }
+ }
+
+ // Try to compile a shader using a disallowed layout qualifier
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, invalid])) {
+ testFailed("Unsupported depth layout qualifier compiled successfully");
+ } else {
+ testPassed("Unsupported depth layout qualifier failed to compile");
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runShaderTests(false);
+
+ ext = gl.getExtension("EXT_conservative_depth");
+ wtu.runExtensionSupportedTest(gl, "EXT_conservative_depth", ext !== null);
+
+ if (!ext) {
+ testPassed("No EXT_conservative_depth support -- this is legal");
+ } else {
+ testPassed("Successfully enabled EXT_conservative_depth extension");
+ runShaderTests(true);
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-disjoint-timer-query-webgl2.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-disjoint-timer-query-webgl2.html
index c051fa36a3..f1e9a82d8a 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-disjoint-timer-query-webgl2.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-disjoint-timer-query-webgl2.html
@@ -25,7 +25,9 @@ description("This test verifies the functionality of the EXT_disjoint_timer_quer
var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas, null, 2);
+var gl2 = null;
var ext = null;
+var ext2 = null;
var query = null;
var query2 = null;
var elapsed_query = null;
@@ -62,6 +64,8 @@ if (!gl) {
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
}
verifyQueryResultsNotAvailable();
+ verifyDeleteQueryBehavior();
+ verifyDeleteQueryErrorBehavior();
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
window.requestAnimationFrame(checkQueryResults);
@@ -249,6 +253,60 @@ function verifyQueryResultsNotAvailable() {
testPassed("Queries' results didn't become available in a spin loop");
}
+function verifyDeleteQueryBehavior() {
+ debug("");
+ debug("Testing deleting an active query should end it.");
+
+ // Use a new context for this test
+ gl2 = wtu.create3DContext(null, null, 2);
+ if (!gl2) return;
+ ext2 = gl2.getExtension("EXT_disjoint_timer_query_webgl2");
+ if (!ext2) return;
+
+ query = gl2.createQuery();
+ gl2.beginQuery(ext.TIME_ELAPSED_EXT, query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE, "The query began successfully");
+ gl2.deleteQuery(query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE, "Deletion of the active query succeeds");
+ shouldBeNull("gl2.getQuery(ext2.TIME_ELAPSED_EXT, gl2.CURRENT_QUERY)");
+ shouldBeFalse("gl2.isQuery(query)");
+ query = gl2.createQuery();
+ gl2.beginQuery(ext2.TIME_ELAPSED_EXT, query);
+ wtu.glErrorShouldBe(gl, gl2.NONE, "Beginning a new query succeeds");
+ gl2.endQuery(gl2.TIME_ELAPSED_EXT);
+ gl2.deleteQuery(query);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ query = null;
+ ext2 = null;
+ gl2 = null;
+}
+
+function verifyDeleteQueryErrorBehavior() {
+ debug("");
+ debug("Testing deleting a query created by another context.");
+
+ // Use new contexts for this test
+ gl2 = wtu.create3DContext(null, null, 2);
+ var gl3 = wtu.create3DContext(null, null, 2);
+ if (!gl2 || !gl3) return;
+ ext2 = gl2.getExtension("EXT_disjoint_timer_query_webgl2");
+ if (!ext2) return;
+
+ query = gl2.createQuery();
+ gl2.beginQuery(ext2.TIME_ELAPSED_EXT, query);
+ gl3.deleteQuery(query);
+ wtu.glErrorShouldBe(gl3, gl3.INVALID_OPERATION);
+ shouldBeTrue("gl2.isQuery(query)");
+ shouldBe("gl2.getQuery(ext2.TIME_ELAPSED_EXT, gl2.CURRENT_QUERY)", "query");
+ gl2.endQuery(ext2.TIME_ELAPSED_EXT);
+ gl2.deleteQuery(query);
+ wtu.glErrorShouldBe(gl2, gl2.NONE);
+ query = null;
+ ext2 = null;
+ gl2 = null;
+ gl3 = null;
+}
+
function checkQueryResults() {
if (availability_retry > 0) {
// Make a reasonable attempt to wait for the queries' results to become available.
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-render-snorm.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-render-snorm.html
new file mode 100644
index 0000000000..723e762773
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-render-snorm.html
@@ -0,0 +1,201 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL EXT_render_snorm Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the EXT_render_snorm extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext(null, null, 2);
+var ext;
+
+function createTypedArray(type) {
+ switch (type) {
+ case gl.BYTE:
+ return new Int8Array(4);
+ case gl.UNSIGNED_BYTE:
+ return new Uint8Array(4);
+ case gl.SHORT:
+ return new Int16Array(4);
+ case gl.UNSIGNED_SHORT:
+ return new Uint16Array(4);
+ default:
+ return null;
+ }
+}
+
+function drawTest(config) {
+ wtu.drawUnitQuad(gl);
+
+ const implementationType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
+ const implementationFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
+
+ // Support for reading signed data with unsigned read type is not required
+ // but implementations may allow such conversions. Do not expect the error
+ // when the type matches the buffer type or when it's explicitly supported.
+ for (const type of [gl.BYTE, gl.UNSIGNED_BYTE, gl.SHORT, gl.UNSIGNED_SHORT]) {
+ if (type == config.type) continue;
+ if (implementationFormat != gl.RGBA || implementationType != type) {
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, type, createTypedArray(type));
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "reading with unsupported type fails");
+ }
+ }
+
+ const defaultPixel = createTypedArray(config.type);
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, config.color,
+ "reading with the RGBA format and matching type", 1,
+ defaultPixel,
+ config.type, gl.RGBA);
+
+ if (implementationFormat == config.format && implementationType == config.type) {
+ const implementationPixel = createTypedArray(implementationType);
+ const color = [config.color[0]];
+ if (config.format != gl.RED) color.push(config.color[1]);
+ if (config.format == gl.RGBA) color.push(config.color[2], config.color[3]);
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, color,
+ "reading with the exact format/type", 1,
+ implementationPixel,
+ implementationType, implementationFormat);
+ }
+}
+
+function renderbufferTest(config, isSupported) {
+ debug("");
+ debug(`${config.name} renderbuffer: ` +
+ `${!isSupported || !config.color ? "NOT " : ""}supported`);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, config.internalFormat, 1, 1);
+ if (!isSupported || !config.color) {
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "renderbuffer allocation failed");
+ return;
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "renderbuffer allocation succeeded");
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ drawTest(config);
+}
+
+function textureTest(config, isRenderable, isTexturable) {
+ debug("");
+ debug(`${config.name} texture: ` +
+ `${!isRenderable || !config.color ? "NOT " : ""}renderable, ` +
+ `${!isTexturable ? "NOT " : ""}texturable`);
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, config.internalFormat, 1, 1, 0, config.format, config.type, null);
+ if (!isTexturable) {
+ wtu.glErrorShouldBe(gl,
+ [gl.INVALID_ENUM, gl.INVALID_VALUE, gl.INVALID_OPERATION],
+ "texture allocation failed");
+ return;
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture allocation succeeded");
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+ if (!isRenderable || !config.color) {
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+ return;
+ }
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ drawTest(config);
+}
+
+function formatTest(isSnormEnabled, isNorm16Enabled) {
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShader,
+ wtu.simpleColorFragmentShader]);
+ gl.useProgram(program);
+ gl.uniform4f(gl.getUniformLocation(program, "u_color"), -0.0625, -0.125, -0.25, -0.5);
+
+ wtu.setupUnitQuad(gl);
+
+ const configs8 = [
+ {name: "R8_SNORM", format: gl.RED, type: gl.BYTE, internalFormat: gl.R8_SNORM, color: [-8, 0, 0, 127]},
+ {name: "RG8_SNORM", format: gl.RG, type: gl.BYTE, internalFormat: gl.RG8_SNORM, color: [-8, -16, 0, 127]},
+ {name: "RGB8_SNORM", format: gl.RGB, type: gl.BYTE, internalFormat: gl.RGB8_SNORM, color: null},
+ {name: "RGBA8_SNORM", format: gl.RGBA, type: gl.BYTE, internalFormat: gl.RGBA8_SNORM, color: [-8, -16, -32, -64]}
+ ];
+
+ const configs16 = [
+ {name: "R16_SNORM", format: gl.RED, type: gl.SHORT, internalFormat: 0x8F98 /* R16_SNORM_EXT */, color: [-2048, 0, 0, 32767]},
+ {name: "RG16_SNORM", format: gl.RG, type: gl.SHORT, internalFormat: 0x8F99 /* RG16_SNORM_EXT */, color: [-2048, -4096, 0, 32767]},
+ {name: "RGB16_SNORM", format: gl.RGB, type: gl.SHORT, internalFormat: 0x8F9A /* RGB16_SNORM_EXT */, color: null},
+ {name: "RGBA16_SNORM", format: gl.RGBA, type: gl.SHORT, internalFormat: 0x8F9B /* RGBA16_SNORM_EXT */, color: [-2048, -4096, -8192, -16384]}
+ ];
+
+ for (const config of configs8) {
+ renderbufferTest(config, isSnormEnabled);
+ textureTest(config, isSnormEnabled, true);
+ }
+
+ for (const config of configs16) {
+ renderbufferTest(config, isSnormEnabled && isNorm16Enabled);
+ textureTest(config, isSnormEnabled && isNorm16Enabled, isNorm16Enabled);
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+
+ testPassed("context exists");
+
+ debug("");
+ debug("Testing signed normalized formats with EXT_render_snorm disabled");
+ formatTest(false, false);
+
+ ext = gl.getExtension("EXT_render_snorm");
+ wtu.runExtensionSupportedTest(gl, "EXT_render_snorm", ext !== null);
+
+ if (ext !== null) {
+ debug("");
+ debug("Testing signed normalized formats with only EXT_render_snorm enabled");
+ formatTest(true, false);
+
+ if (gl.getExtension("EXT_texture_norm16")) {
+ debug("");
+ debug("Testing signed normalized formats with EXT_render_snorm and EXT_texture_norm16 enabled");
+ formatTest(true, true);
+ }
+ } else {
+ testPassed("No EXT_render_snorm support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/nv-shader-noperspective-interpolation.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/nv-shader-noperspective-interpolation.html
new file mode 100644
index 0000000000..2198c17b1a
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/nv-shader-noperspective-interpolation.html
@@ -0,0 +1,251 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL NV_shader_noperspective_interpolation Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="128" height="128" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the NV_shader_noperspective_interpolation extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c", null, 2);
+var ext;
+
+function runShaderTests(extensionEnabled) {
+ debug("");
+ debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
+
+ const macroVertex = `#version 300 es
+ in vec4 vPosition;
+ void main() {
+ #ifdef GL_NV_shader_noperspective_interpolation
+ gl_Position = vPosition;
+ #else
+ #error no GL_NV_shader_noperspective_interpolation;
+ #endif
+ }`;
+
+ const macroFragment = `#version 300 es
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ #ifdef GL_NV_shader_noperspective_interpolation
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ #else
+ #error no GL_NV_shader_noperspective_interpolation;
+ #endif
+ }`;
+
+ for (const shaders of [[wtu.simpleVertexShaderESSL300, macroFragment],
+ [macroVertex, wtu.simpleColorFragmentShaderESSL300]]) {
+ // 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");
+ }
+ }
+ }
+
+ const missingVertex = `#version 300 es
+ noperspective out float interpolant;
+ in vec4 vPosition;
+ void main() {
+ gl_Position = vPosition;
+ }`;
+
+ const missingFragment = `#version 300 es
+ precision highp float;
+ noperspective in float interpolant;
+ out vec4 my_FragColor;
+ void main() {
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ }`;
+
+ // Always expect the shader missing the #extension pragma to fail (whether enabled or not)
+ for (const shaders of [[missingVertex, wtu.simpleColorFragmentShaderESSL300],
+ [wtu.simpleVertexShaderESSL300, missingFragment],
+ [missingVertex, missingFragment]]) {
+ if (wtu.setupProgram(gl, shaders)) {
+ testFailed("Noperspective interpolation qualifier allowed without #extension pragma");
+ } else {
+ testPassed("Noperspective interpolation qualifier disallowed without #extension pragma");
+ }
+ }
+
+ const validVertex = `#version 300 es
+ #extension GL_NV_shader_noperspective_interpolation : enable
+ noperspective out float interpolant;
+ in vec4 vPosition;
+ void main() {
+ gl_Position = vPosition;
+ }`;
+
+ const validFragment = `#version 300 es
+ #extension GL_NV_shader_noperspective_interpolation : enable
+ precision highp float;
+ noperspective in float interpolant;
+ out vec4 my_FragColor;
+ void main() {
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ }`;
+
+ // Try to compile a shader using a noperspective qualifier that should only succeed if enabled
+ if (wtu.setupProgram(gl, [validVertex, validFragment])) {
+ if (extensionEnabled) {
+ testPassed("Noperspective interpolation qualifier compiled successfully when extension enabled");
+ } else {
+ testFailed("Noperspective interpolation qualifier compiled successfully when extension disabled");
+ }
+ } else {
+ if (extensionEnabled) {
+ testFailed("Noperspective interpolation qualifier failed to compile when extension enabled");
+ } else {
+ testPassed("Noperspective interpolation qualifier failed to compile when extension disabled");
+ }
+ }
+
+ debug("");
+}
+
+function runInterpolationTest() {
+ function draw(program, skew) {
+ gl.useProgram(program);
+
+ const posLoc = gl.getAttribLocation(program, "position");
+ const colLoc = gl.getAttribLocation(program, "color");
+
+ const buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ new Float32Array([
+ -1.0, -1.0, 0.0, 1.0,
+ +1.0, -1.0, 0.0, 1.0,
+ 0.0, +1.0 * skew, 0.0, skew,
+
+ 1.0, 0.0, 0.0, 1.0,
+ 0.0, 1.0, 0.0, 1.0,
+ 0.0, 0.0, 1.0, 1.0]),
+ gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(posLoc, 4, gl.FLOAT, false, 0, 0);
+ gl.vertexAttribPointer(colLoc, 4, gl.FLOAT, false, 0, 48);
+ gl.enableVertexAttribArray(posLoc);
+ gl.enableVertexAttribArray(colLoc);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ }
+
+ const vertexSmooth = `#version 300 es
+ in vec4 position;
+ in vec4 color;
+ smooth out vec4 interp_color;
+ void main() {
+ gl_Position = position;
+ interp_color = color;
+ }`;
+
+ const fragmentSmooth = `#version 300 es
+ precision highp float;
+ smooth in vec4 interp_color;
+ out vec4 fragColor;
+ void main() {
+ fragColor = interp_color;
+ }`;
+ const programSmooth = wtu.setupProgram(gl, [vertexSmooth, fragmentSmooth]);
+
+ debug("Get non-skewed value with smooth interpolation");
+ gl.clearColor(0.0, 0.0, 0.0, 1.0);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ draw(programSmooth, 1.0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ const smoothColor = new Uint8Array(4);
+ gl.readPixels(64, 64, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, smoothColor);
+
+ const vertexNoperspective = `#version 300 es
+ #extension GL_NV_shader_noperspective_interpolation : require
+ in vec4 position;
+ in vec4 color;
+ noperspective out vec4 interp_color;
+ void main() {
+ gl_Position = position;
+ interp_color = color;
+ }`;
+
+ const fragmentNoperspective = `#version 300 es
+ #extension GL_NV_shader_noperspective_interpolation : require
+ precision highp float;
+ noperspective in vec4 interp_color;
+ out vec4 fragColor;
+ void main() {
+ fragColor = interp_color;
+ }`;
+ const programNoperspective = wtu.setupProgram(gl, [vertexNoperspective, fragmentNoperspective]);
+
+ debug("");
+ debug("Check non-skewed value with noperspective interpolation");
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ draw(programNoperspective, 1.0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ wtu.checkCanvasRect(gl, 64, 64, 1, 1, smoothColor, "Non-skewed noperspective should match smooth");
+
+ debug("");
+ debug("Check skewed value with noperspective interpolation");
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ draw(programNoperspective, 2.0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ wtu.checkCanvasRect(gl, 64, 64, 1, 1, smoothColor, "Skewed noperspective should match smooth");
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runShaderTests(false);
+
+ ext = gl.getExtension("NV_shader_noperspective_interpolation");
+ wtu.runExtensionSupportedTest(gl, "NV_shader_noperspective_interpolation", ext !== null);
+
+ if (!ext) {
+ testPassed("No NV_shader_noperspective_interpolation support -- this is legal");
+ } else {
+ testPassed("Successfully enabled NV_shader_noperspective_interpolation extension");
+ runShaderTests(true);
+ runInterpolationTest();
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html
new file mode 100644
index 0000000000..41fc8f8242
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html
@@ -0,0 +1,474 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL OES_sample_variables Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the OES_sample_variables extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c", { antialias: false }, 2);
+var ext;
+
+function runShaderTests(extensionEnabled) {
+ debug("");
+ debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
+
+ const macro = `#version 300 es
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ #ifdef GL_OES_sample_variables
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ #else
+ #error no GL_OES_sample_variables;
+ #endif
+ }`;
+
+ // Expect the macro shader to succeed ONLY if enabled
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, macro])) {
+ 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");
+ }
+ }
+
+ const missing = `#version 300 es
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ gl_SampleMask[0] = gl_SampleMaskIn[0] & 0x55555555;
+ my_FragColor = vec4(gl_SamplePosition.yx, float(gl_SampleID), float(gl_MaxSamples + gl_NumSamples));
+ }`;
+
+ // Always expect the shader missing the #extension pragma to fail (whether enabled or not)
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, missing])) {
+ testFailed("Sample variables allowed without #extension pragma");
+ } else {
+ testPassed("Sample variables disallowed without #extension pragma");
+ }
+
+ const valid = `#version 300 es
+ #extension GL_OES_sample_variables : enable
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ gl_SampleMask[0] = gl_SampleMaskIn[0] & 0x55555555;
+ my_FragColor = vec4(gl_SamplePosition.yx, float(gl_SampleID), float(gl_MaxSamples + gl_NumSamples));
+ }`;
+
+ // Try to compile a shader using sample variables that should only succeed if enabled
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, valid])) {
+ if (extensionEnabled) {
+ testPassed("Sample variables compiled successfully when extension enabled");
+ } else {
+ testFailed("Sample variables compiled successfully when extension disabled");
+ }
+ } else {
+ if (extensionEnabled) {
+ testFailed("Sample variables failed to compile when extension enabled");
+ } else {
+ testPassed("Sample variables failed to compile when extension disabled");
+ }
+ }
+
+ debug("");
+}
+
+function runMaxSamplesTest() {
+ debug("");
+ debug("Testing gl_MaxSamples");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ void main() {
+ color = vec4(float(gl_MaxSamples * 4) / 255.0, 0.0, 0.0, 1.0);
+ }`;
+ gl.useProgram(wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]));
+
+ wtu.setupUnitQuad(gl);
+ wtu.drawUnitQuad(gl);
+
+ wtu.checkCanvas(gl, [gl.getParameter(gl.MAX_SAMPLES) * 4, 0, 0, 255], "should match MAX_SAMPLES", 1);
+}
+
+function runNumSamplesTest() {
+ debug("");
+ debug("Testing gl_NumSamples");
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ void main() {
+ if (gl_NumSamples == 4) {
+ color = vec4(0.0, 1.0, 0.0, 1.0);
+ } else {
+ color = vec4(1.0, 0.0, 0.0, 1.0);
+ }
+ }`;
+ gl.useProgram(wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]));
+
+ wtu.setupUnitQuad(gl);
+ wtu.drawUnitQuad(gl);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
+}
+
+function runSampleIDTest() {
+ debug("");
+ debug("Testing gl_SampleID");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ uniform int id;
+ void main() {
+ // Special value when the selected sample is processed, 0.0 otherwise
+ float r = float(gl_SampleID == id ? (1 << gl_SampleID) : 0) * 32.0 / 255.0;
+ // Must always be 0.0
+ float g = float(gl_SampleID < 0 || gl_SampleID >= gl_NumSamples);
+ color = vec4(r, g, 0.0, 1.0);
+ }`;
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ wtu.setupUnitQuad(gl);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ for (let sample = 0; sample < 4; sample++) {
+ debug(`Sample ${sample} is selected`);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.uniform1i(gl.getUniformLocation(program, "id"), sample);
+ wtu.drawUnitQuad(gl);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ wtu.checkCanvas(gl, [(1 << sample) * 8, 0, 0, 255], undefined, 1);
+ }
+}
+
+function runSampleMaskInTest() {
+ debug("");
+ debug("Testing gl_SampleMaskIn");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ uint popcount(uint v) {
+ uint c = 0u;
+ for (; v != 0u; v >>= 1) c += v & 1u;
+ return c;
+ }
+ void main() {
+ float r = float(popcount(uint(gl_SampleMaskIn[0])));
+ color = vec4(r * 4.0 / 255.0, 0, 0, 1);
+ }`;
+
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ // Use a triangle instead of the WTU's quad
+ // to avoid artifacts along the diagonal
+ const vertices = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertices);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+ -1.0, 1.0,
+ 1.0, -1.0,
+ -1.0, -1.0]), gl.STATIC_DRAW);
+ gl.enableVertexAttribArray(0);
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
+
+ function test(sampleCount, sampleCoverageEnabled, coverage) {
+ if (sampleCoverageEnabled) {
+ gl.enable(gl.SAMPLE_COVERAGE);
+ } else {
+ gl.disable(gl.SAMPLE_COVERAGE);
+ }
+
+ gl.sampleCoverage(coverage, false);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ // Shader scales up the number of input samples to increase precision in unorm8 space.
+ let expected = Math.max(sampleCount, 1) * 4;
+
+ // Sample coverage must not affect single sampled buffers
+ if (sampleCoverageEnabled && sampleCount > 0) {
+ // The number of samples in gl_SampleMaskIn must be affected by the sample
+ // coverage GL state and then the resolved value must be scaled down again.
+ expected *= coverage * coverage;
+ }
+
+ // Check only the red channel
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ const pixel = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
+ const message = `Expected: ${expected}, Actual: ${pixel[0]}, ` +
+ `Samples: ${sampleCount}, Sample Coverage: ${sampleCoverageEnabled}, Coverage: ${coverage}`;
+ if (Math.abs(pixel[0] - expected) > 2) {
+ testFailed(message);
+ } else {
+ testPassed(message);
+ }
+ }
+
+ // Include all exposed sample counts and additionally test single-sampled rendering
+ const sampleCounts = [...gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES), 0];
+
+ for (const sampleCount of sampleCounts) {
+ if (sampleCount > 32) {
+ // This test will not work with more than 32 samples.
+ continue;
+ }
+
+ for (const sampleCoverageEnabled of [false, true]) {
+ for (const coverage of [0.0, 0.5, 1.0]) {
+ if (sampleCount == 1 && coverage != 0.0 && coverage != 1.0) {
+ continue;
+ }
+ test(sampleCount, sampleCoverageEnabled, coverage);
+ }
+ }
+ }
+}
+
+function runSampleMaskInPerSampleTest() {
+ debug("");
+ debug("Testing gl_SampleMaskIn with per-sample shading");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ void main() {
+ float r = float(gl_SampleMaskIn[0] == (1 << gl_SampleID));
+ color = vec4(r, 0, 0, 1);
+ }`;
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ wtu.setupUnitQuad(gl);
+
+ // Include all exposed sample counts and additionally test single-sampled rendering
+ const sampleCounts = [...gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES), 0];
+ for (const sampleCount of sampleCounts) {
+ if (sampleCount > 32) {
+ // This test will not work with more than 32 samples.
+ continue;
+ }
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ wtu.drawUnitQuad(gl);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ wtu.checkCanvas(gl, [255, 0, 0, 255], `Samples: ${sampleCount}`, 1);
+ }
+}
+
+function runSampleMaskTest() {
+ debug("");
+ debug("Testing gl_SampleMask");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ uniform highp int sampleMask;
+ out vec4 color;
+ void main() {
+ gl_SampleMask[0] = sampleMask;
+ color = vec4(1, 0, 0, 1);
+ }`;
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ // Use a triangle instead of the WTU's quad
+ // to avoid artifacts along the diagonal
+ const vertices = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertices);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+ -1.0, 1.0,
+ 1.0, -1.0,
+ -1.0, -1.0]), gl.STATIC_DRAW);
+ gl.enableVertexAttribArray(0);
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
+
+ function test(sampleCount, sampleMask) {
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.uniform1i(gl.getUniformLocation(program, "sampleMask"), sampleMask);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ let expected = 1.0;
+ if (sampleCount > 0) {
+ let mask = sampleMask & ((1 << Math.max(sampleCount, 1)) - 1);
+ let bits = 0;
+ for (; mask != 0; mask >>= 1) bits += mask & 1;
+ expected = bits / Math.max(sampleCount, 1);
+ }
+ expected *= 255;
+
+ // Check only the red channel
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ const pixel = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
+ const message = `Samples: ${sampleCount}, `
+ + `gl_SampleMask[0]: 0x${sampleMask.toString(16).padStart(8, "0").toUpperCase()}, `
+ + `Actual: ${pixel[0]}, Expected: ${expected}`;
+ if (Math.abs(pixel[0] - expected) > 2) {
+ testFailed(message);
+ } else {
+ testPassed(message);
+ }
+ }
+
+ // Include all exposed sample counts and additionally test single-sampled rendering
+ const sampleCounts = [...gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES), 0];
+
+ for (const sampleCount of sampleCounts) {
+ if (sampleCount > 31) {
+ // This test will not work with more than 31 samples.
+ continue;
+ }
+
+ for (const sampleMask of [0xFFFFFFFF, 0x55555555, 0xAAAAAAAA, 0x00000000]) {
+ test(sampleCount, sampleMask);
+ }
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runShaderTests(false);
+
+ ext = gl.getExtension("OES_sample_variables");
+ wtu.runExtensionSupportedTest(gl, "OES_sample_variables", ext !== null);
+
+ if (!ext) {
+ testPassed("No OES_sample_variables support -- this is legal");
+ } else {
+ testPassed("Successfully enabled OES_sample_variables extension");
+ runShaderTests(true);
+
+ debug("Testing sample variables");
+ runMaxSamplesTest();
+ runNumSamplesTest();
+ runSampleIDTest();
+ runSampleMaskInTest();
+ runSampleMaskInPerSampleTest();
+ runSampleMaskTest();
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-shader-multisample-interpolation.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-shader-multisample-interpolation.html
new file mode 100644
index 0000000000..dcb272c2f0
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-shader-multisample-interpolation.html
@@ -0,0 +1,313 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL OES_shader_multisample_interpolation Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the OES_shader_multisample_interpolation extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c", { antialias: false }, 2);
+var ext;
+
+function runShaderTests(extensionEnabled) {
+ debug("");
+ debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
+
+ const macroVertex = `#version 300 es
+ in vec4 vPosition;
+ void main() {
+ #ifdef GL_OES_shader_multisample_interpolation
+ gl_Position = vPosition;
+ #else
+ #error no GL_OES_shader_multisample_interpolation;
+ #endif
+ }`;
+
+ const macroFragment = `#version 300 es
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ #ifdef GL_OES_shader_multisample_interpolation
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ #else
+ #error no GL_OES_shader_multisample_interpolation;
+ #endif
+ }`;
+
+ for (const shaders of [[wtu.simpleVertexShaderESSL300, macroFragment],
+ [macroVertex, wtu.simpleColorFragmentShaderESSL300]]) {
+ // 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");
+ }
+ }
+ }
+
+ const missingVertex = `#version 300 es
+ sample out float interpolant;
+ in vec4 vPosition;
+ void main() {
+ gl_Position = vPosition;
+ }`;
+
+ const missingFragment = `#version 300 es
+ precision highp float;
+ sample in float interpolant;
+ out vec4 my_FragColor;
+ void main() {
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ }`;
+
+ // Always expect the shader missing the #extension pragma to fail (whether enabled or not)
+ for (const shaders of [[missingVertex, wtu.simpleColorFragmentShaderESSL300],
+ [wtu.simpleVertexShaderESSL300, missingFragment],
+ [missingVertex, missingFragment]]) {
+ if (wtu.setupProgram(gl, shaders)) {
+ testFailed("Sample interpolation qualifier allowed without #extension pragma");
+ } else {
+ testPassed("Sample interpolation qualifier disallowed without #extension pragma");
+ }
+ }
+
+ const validVertex = `#version 300 es
+ #extension GL_OES_shader_multisample_interpolation : enable
+ sample out float interpolant;
+ in vec4 vPosition;
+ void main() {
+ gl_Position = vPosition;
+ }`;
+
+ const validFragment = `#version 300 es
+ #extension GL_OES_shader_multisample_interpolation : enable
+ precision highp float;
+ sample in float interpolant;
+ out vec4 my_FragColor;
+ void main() {
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ }`;
+
+ // Try to compile a shader using a sample qualifier that should only succeed if enabled
+ if (wtu.setupProgram(gl, [validVertex, validFragment])) {
+ if (extensionEnabled) {
+ testPassed("Sample interpolation qualifier compiled successfully when extension enabled");
+ } else {
+ testFailed("Sample interpolation qualifier compiled successfully when extension disabled");
+ }
+ } else {
+ if (extensionEnabled) {
+ testFailed("Sample interpolation qualifier failed to compile when extension enabled");
+ } else {
+ testPassed("Sample interpolation qualifier failed to compile when extension disabled");
+ }
+ }
+}
+
+function runQueryTests(extensionEnabled) {
+ debug("");
+ debug("Testing parameters with extension " + (extensionEnabled ? "enabled" : "disabled"));
+ if (extensionEnabled) {
+ shouldBeGreaterThanOrEqual("gl.getParameter(ext.FRAGMENT_INTERPOLATION_OFFSET_BITS_OES)", "4");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ const limit = 0.5 - Math.pow(2, -gl.getParameter(ext.FRAGMENT_INTERPOLATION_OFFSET_BITS_OES));
+ shouldBeLessThanOrEqual("gl.getParameter(ext.MIN_FRAGMENT_INTERPOLATION_OFFSET_OES)", `-${limit}`);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBeGreaterThanOrEqual("gl.getParameter(ext.MAX_FRAGMENT_INTERPOLATION_OFFSET_OES)", `${limit}`);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ } else {
+ shouldBeNull("gl.getParameter(0x8E5B /* MIN_FRAGMENT_INTERPOLATION_OFFSET_OES */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBeNull("gl.getParameter(0x8E5C /* MAX_FRAGMENT_INTERPOLATION_OFFSET_OES */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBeNull("gl.getParameter(0x8E5D /* FRAGMENT_INTERPOLATION_OFFSET_BITS_OES */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ }
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.MIN_FRAGMENT_INTERPOLATION_OFFSET_OES", "0x8E5B");
+ shouldBe("ext.MAX_FRAGMENT_INTERPOLATION_OFFSET_OES", "0x8E5C");
+ shouldBe("ext.FRAGMENT_INTERPOLATION_OFFSET_BITS_OES", "0x8E5D");
+}
+
+/*
+ * This test renders a triangle using MSAAx4 and 1x1 viewport
+ * with the following vertex colors.
+ *
+ * | Position | Color |
+ * |==========|===========|
+ * | (-1, -1) | (0, 0, 0) |
+ * | (-1, +1) | (0, 1, 0) |
+ * | (+1, -1) | (1, 0, 0) |
+ *
+ * This triangle cannot cover all four samples.
+ *
+ * When default interpolation is used, the vertex color is interpolated
+ * once, most likely in the pixel center.
+ *
+ * When per-sample interpolation is used, the vertex color is interpolated
+ * several times, producing a distinct value for each covered sample.
+ * Due to the asymmetry of sample positions, the resolved pixel color must
+ * not match the color produced by default interpolation.
+ *
+ * OpenGL specs do not guarantee specific sample positions, so the test
+ * checks only that the resolved colors are different.
+ */
+function runInterpolationTest() {
+ debug("");
+ debug("Testing multisample interpolation");
+
+ function draw(program) {
+ gl.viewport(0, 0, 1, 1);
+ gl.useProgram(program);
+
+ const posLoc = gl.getAttribLocation(program, "position");
+ const buf = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+ gl.bufferData(
+ gl.ARRAY_BUFFER,
+ new Float32Array([
+ -1.0, -1.0,
+ -1.0, +1.0,
+ +1.0, -1.0]),
+ gl.STATIC_DRAW);
+
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
+ gl.enableVertexAttribArray(posLoc);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 1, 1);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ }
+
+ const vertexCenter = `#version 300 es
+ in vec4 position;
+ out vec4 interp_color;
+ void main() {
+ gl_Position = position;
+ interp_color = vec4(position.xy * 0.5 + 0.5, 0.0, 1.0);
+ }`;
+
+ const fragmentCenter = `#version 300 es
+ precision highp float;
+ in vec4 interp_color;
+ out vec4 fragColor;
+ void main() {
+ fragColor = interp_color;
+ }`;
+ const programCenter = wtu.setupProgram(gl, [vertexCenter, fragmentCenter]);
+
+ draw(programCenter);
+ const centerColor = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, centerColor);
+
+ const vertexSample = `#version 300 es
+ #extension GL_OES_shader_multisample_interpolation : require
+ in vec4 position;
+ sample out vec4 interp_color;
+ void main() {
+ gl_Position = position;
+ interp_color = vec4(position.xy * 0.5 + 0.5, 0.0, 1.0);
+ }`;
+
+ const fragmentSample = `#version 300 es
+ #extension GL_OES_shader_multisample_interpolation : require
+ precision highp float;
+ sample in vec4 interp_color;
+ out vec4 fragColor;
+ void main() {
+ fragColor = interp_color;
+ }`;
+ const programSample = wtu.setupProgram(gl, [vertexSample, fragmentSample]);
+
+ draw(programSample);
+ const sampleColor = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, sampleColor);
+
+ const message = `Pixel-center value: ${centerColor}, sample-average value: ${sampleColor}`;
+ if (centerColor[0] == sampleColor[0] && centerColor[1] == sampleColor[1]) {
+ testFailed(message);
+ } else {
+ testPassed(message);
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runQueryTests(false);
+ runShaderTests(false);
+
+ debug("");
+ ext = gl.getExtension("OES_shader_multisample_interpolation");
+ wtu.runExtensionSupportedTest(gl, "OES_shader_multisample_interpolation", ext !== null);
+
+ if (!ext) {
+ testPassed("No OES_shader_multisample_interpolation support -- this is legal");
+ } else {
+ testPassed("Successfully enabled OES_shader_multisample_interpolation extension");
+ runQueryTests(true);
+ runShaderTests(true);
+ runInterpolationTest();
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-blend-func-extended.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-blend-func-extended.html
new file mode 100644
index 0000000000..792e9aafa7
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-blend-func-extended.html
@@ -0,0 +1,26 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL 2.0 WEBGL_blend_func_extended Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+var contextVersion = 2;
+</script>
+<script src="../../js/tests/webgl-blend-func-extended.js"></script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-clip-cull-distance.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-clip-cull-distance.html
new file mode 100644
index 0000000000..cb2253c326
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-clip-cull-distance.html
@@ -0,0 +1,475 @@
+<!--
+Copyright (c) 2022 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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_clip_cull_distance Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the WEBGL_clip_cull_distance extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c", null, 2);
+var ext;
+const w = gl.drawingBufferWidth;
+const h = gl.drawingBufferHeight;
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check parameters and capabilities without the extension");
+
+ shouldBeNull("gl.getParameter(0x0D32 /* MAX_CLIP_DISTANCES_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ shouldBeNull("gl.getParameter(0x82F9 /* MAX_CULL_DISTANCES_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ shouldBeNull("gl.getParameter(0x82FA /* MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ const assertState = (i) => {
+ shouldBeFalse(`gl.isEnabled(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+
+ shouldBeNull(`gl.getParameter(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ };
+
+ for (let i = 0; i < 8; i++) {
+ assertState(i);
+
+ gl.enable(0x3000 + i /* CLIP_DISTANCEi_WEBGL */);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "capability unknown without enabling the extension");
+
+ assertState(i);
+
+ gl.disable(0x3000 + i /* CLIP_DISTANCEi_WEBGL */);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "capability unknown without enabling the extension");
+
+ assertState(i);
+ }
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ debug("");
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.MAX_CLIP_DISTANCES_WEBGL", "0x0D32");
+ shouldBe("ext.MAX_CULL_DISTANCES_WEBGL", "0x82F9");
+ shouldBe("ext.MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL", "0x82FA");
+ shouldBe("ext.CLIP_DISTANCE0_WEBGL", "0x3000");
+ shouldBe("ext.CLIP_DISTANCE1_WEBGL", "0x3001");
+ shouldBe("ext.CLIP_DISTANCE2_WEBGL", "0x3002");
+ shouldBe("ext.CLIP_DISTANCE3_WEBGL", "0x3003");
+ shouldBe("ext.CLIP_DISTANCE4_WEBGL", "0x3004");
+ shouldBe("ext.CLIP_DISTANCE5_WEBGL", "0x3005");
+ shouldBe("ext.CLIP_DISTANCE6_WEBGL", "0x3006");
+ shouldBe("ext.CLIP_DISTANCE7_WEBGL", "0x3007");
+}
+
+function checkQueries() {
+ debug("");
+ debug("Check parameters");
+ shouldBeGreaterThanOrEqual('gl.getParameter(ext.MAX_CLIP_DISTANCES_WEBGL)', '8');
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ const maxCullDistances = gl.getParameter(ext.MAX_CULL_DISTANCES_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ if (maxCullDistances == 0) {
+ testPassed("No cull distance support");
+ shouldBe("gl.getParameter(ext.MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL)", "0");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ } else if (maxCullDistances >= 8) {
+ testPassed("Optional cull distance support");
+ shouldBeGreaterThanOrEqual("gl.getParameter(ext.MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL)", "8");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ } else {
+ testFailed("Invalid number of supported cull distances");
+ }
+
+ debug("");
+ debug("Check clip distance capabilities");
+
+ const assertState = (i, s) => {
+ shouldBe(`gl.isEnabled(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`, s ? "true" : "false");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ shouldBe(`gl.getParameter(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`, s ? "true" : "false");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ };
+
+ for (let i = 0; i < 8; i++) {
+ assertState(i, false);
+
+ gl.enable(ext.CLIP_DISTANCE0_WEBGL + i);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ assertState(i, true);
+
+ gl.disable(ext.CLIP_DISTANCE0_WEBGL + i);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ assertState(i, false);
+ }
+}
+
+function checkClipDistance() {
+ debug("");
+ debug("Check clip distance operation");
+
+ const vs = `#version 300 es
+#extension GL_ANGLE_clip_cull_distance : require
+
+uniform vec4 u_plane;
+in vec2 a_position;
+void main()
+{
+ gl_Position = vec4(a_position, 0.0, 1.0);
+ gl_ClipDistance[0] = dot(gl_Position, u_plane);
+}`;
+
+ const program = wtu.setupProgram(gl, [vs, wtu.simpleColorFragmentShaderESSL300]);
+ gl.useProgram(program);
+ gl.uniform4fv(gl.getUniformLocation(program, 'u_color'), [1.0, 0.0, 0.0, 1.0]);
+
+ gl.enable(ext.CLIP_DISTANCE0_WEBGL);
+
+ // Clear to blue
+ gl.clearColor(0, 0, 1, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ wtu.setupUnitQuad(gl);
+
+ // Draw full screen quad with color red
+ gl.uniform4f(gl.getUniformLocation(program, "u_plane"), 1, 0, 0, 0.5);
+ wtu.drawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ // All pixels on the left of the plane x = -0.5 must be blue
+ let x = 0;
+ let y = 0;
+ let width = w / 4 - 1;
+ let height = h;
+ wtu.checkCanvasRect(gl, x, y, width, height,
+ [0, 0, 255, 255], "should be blue");
+
+ // All pixels on the right of the plane x = -0.5 must be red
+ x = w / 4 + 2;
+ y = 0;
+ width = w - x;
+ height = h;
+ wtu.checkCanvasRect(gl, x, y, width, height,
+ [255, 0, 0, 255], "should be red");
+
+ // Clear to green
+ gl.clearColor(0, 1, 0, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Draw full screen quad with color red
+ gl.uniform4f(gl.getUniformLocation(program, "u_plane"), -1, 0, 0, -0.5);
+ wtu.drawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ // All pixels on the left of the plane x = -0.5 must be red
+ x = 0;
+ y = 0;
+ width = w / 4 - 1;
+ height = h;
+ wtu.checkCanvasRect(gl, x, y, width, height,
+ [255, 0, 0, 255], "should be red");
+
+ // All pixels on the right of the plane x = -0.5 must be green
+ x = w / 4 + 2;
+ y = 0;
+ width = w - x;
+ height = h;
+ wtu.checkCanvasRect(gl, x, y, width, height,
+ [0, 255, 0, 255], "should be green");
+
+ // Disable CLIP_DISTANCE0 and draw again
+ gl.disable(ext.CLIP_DISTANCE0_WEBGL);
+ wtu.drawUnitQuad(gl);
+
+ // All pixels must be red
+ wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red");
+}
+
+function checkClipDistanceInterpolation() {
+ debug("");
+ debug("Check clip distance interpolation");
+
+ const vs = `#version 300 es
+#extension GL_ANGLE_clip_cull_distance : require
+in vec2 a_position;
+void main()
+{
+ gl_Position = vec4(a_position, 0.0, 1.0);
+ gl_ClipDistance[0] = dot(gl_Position, vec4( 1, 0, 0, 0.5));
+ gl_ClipDistance[1] = dot(gl_Position, vec4(-1, 0, 0, 0.5));
+ gl_ClipDistance[2] = dot(gl_Position, vec4( 0, 1, 0, 0.5));
+ gl_ClipDistance[3] = dot(gl_Position, vec4( 0, -1, 0, 0.5));
+ gl_ClipDistance[4] = gl_ClipDistance[0];
+ gl_ClipDistance[5] = gl_ClipDistance[1];
+ gl_ClipDistance[6] = gl_ClipDistance[2];
+ gl_ClipDistance[7] = gl_ClipDistance[3];
+}`;
+
+ const fs = `#version 300 es
+#extension GL_ANGLE_clip_cull_distance : require
+precision highp float;
+out vec4 my_FragColor;
+void main()
+{
+ float r = gl_ClipDistance[0] + gl_ClipDistance[1];
+ float g = gl_ClipDistance[2] + gl_ClipDistance[3];
+ float b = gl_ClipDistance[4] + gl_ClipDistance[5];
+ float a = gl_ClipDistance[6] + gl_ClipDistance[7];
+ my_FragColor = vec4(r, g, b, a) * 0.5;
+}`;
+
+ const program = wtu.setupProgram(gl, [vs, fs]);
+ gl.useProgram(program);
+
+ gl.enable(ext.CLIP_DISTANCE0_WEBGL);
+ gl.enable(ext.CLIP_DISTANCE1_WEBGL);
+ gl.enable(ext.CLIP_DISTANCE2_WEBGL);
+ gl.enable(ext.CLIP_DISTANCE3_WEBGL);
+ gl.enable(ext.CLIP_DISTANCE4_WEBGL);
+ gl.enable(ext.CLIP_DISTANCE5_WEBGL);
+ gl.enable(ext.CLIP_DISTANCE6_WEBGL);
+ gl.enable(ext.CLIP_DISTANCE7_WEBGL);
+
+ wtu.setupUnitQuad(gl);
+
+ // Clear to blue
+ gl.clearColor(0, 0, 1, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Draw full screen quad with color gray
+ wtu.drawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ const data = new Uint8Array(w * h * 4);
+ gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, data);
+ let passed = true;
+ for (let x = 0; x < w; x++) {
+ for (let y = 0; y < h; y++) {
+ const currentPosition = (y * h + x) * 4;
+ const inside = (x >= w / 4 && x < w * 3 / 4 && y >= h / 4 && y < h * 3 / 4);
+ const expected = inside ? [127, 127, 127, 127] : [0, 0, 255, 255];
+ const actual = data.slice(currentPosition, currentPosition + 4);
+ if (Math.abs(actual[0] - expected[0]) > 1 ||
+ Math.abs(actual[1] - expected[1]) > 1 ||
+ Math.abs(actual[2] - expected[2]) > 1 ||
+ Math.abs(actual[3] - expected[3]) > 1) {
+ passed = false;
+ }
+ }
+ }
+ if (passed) {
+ testPassed("Correct clip distance interpolation");
+ } else {
+ testFailed("Incorrect clip distance interpolation");
+ }
+}
+
+function checkCullDistance() {
+ debug("");
+ debug("Check cull distance operation");
+
+ if (gl.getParameter(ext.MAX_CULL_DISTANCES_WEBGL) == 0) {
+ testPassed("No cull distance support");
+ return;
+ }
+
+ const vs = `#version 300 es
+#extension GL_ANGLE_clip_cull_distance : require
+
+uniform vec4 u_plane;
+in vec2 a_position;
+void main()
+{
+ gl_Position = vec4(a_position, 0.0, 1.0);
+ gl_CullDistance[0] = dot(gl_Position, u_plane);
+}`;
+
+ const program = wtu.setupProgram(gl, [vs, wtu.simpleColorFragmentShaderESSL300]);
+ gl.useProgram(program);
+ gl.uniform4fv(gl.getUniformLocation(program, 'u_color'), [1.0, 0.0, 0.0, 1.0]);
+
+ // Clear to blue
+ gl.clearColor(0, 0, 1, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ wtu.setupUnitQuad(gl);
+
+ // Draw full screen quad with color red
+ gl.uniform4f(gl.getUniformLocation(program, "u_plane"), 1, 0, 0, 0.5);
+ wtu.drawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ // All pixels must be red
+ wtu.checkCanvas(gl, [255, 0, 0, 255], "should be red");
+
+ // Clear to green
+ gl.clearColor(0, 1, 0, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ // Draw full screen quad with color red
+ gl.uniform4f(gl.getUniformLocation(program, "u_plane"), -1, 1, 0, -0.5);
+ wtu.drawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ // All pixels above the y > x line must be red
+ const data = new Uint8Array(w * h * 4);
+ gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, data);
+ let passed = true;
+ for (let x = 0; x < w; ++x) {
+ for (let y = 0; y < h; ++y) {
+ if (y <= x + 2 && y >= x - 2) continue; // skip the edge
+ const currentPosition = (y * h + x) * 4;
+ const actual = data.slice(currentPosition, currentPosition + 2);
+ const expected = (y > x) ? [255, 0] : [0, 255];
+ if (actual[0] != expected[0] || actual[1] != expected[1]) {
+ passed = false;
+ }
+ }
+ }
+ if (passed) {
+ testPassed("Correct cull distance operation");
+ } else {
+ testFailed("Incorrect cull distance operation");
+ }
+}
+
+function checkCullDistanceInterpolation() {
+ debug("");
+ debug("Check cull distance interpolation");
+
+ if (gl.getParameter(ext.MAX_CULL_DISTANCES_WEBGL) == 0) {
+ testPassed("No cull distance support");
+ return;
+ }
+
+ const vs = `#version 300 es
+#extension GL_ANGLE_clip_cull_distance : require
+in vec2 a_position;
+void main()
+{
+ gl_Position = vec4(a_position, 0.0, 1.0);
+ gl_CullDistance[0] = dot(gl_Position, vec4( 1, 0, 0, 1));
+ gl_CullDistance[1] = dot(gl_Position, vec4(-1, 0, 0, 1));
+ gl_CullDistance[2] = dot(gl_Position, vec4( 0, 1, 0, 1));
+ gl_CullDistance[3] = dot(gl_Position, vec4( 0, -1, 0, 1));
+ gl_CullDistance[4] = gl_CullDistance[0];
+ gl_CullDistance[5] = gl_CullDistance[1];
+ gl_CullDistance[6] = gl_CullDistance[2];
+ gl_CullDistance[7] = gl_CullDistance[3];
+}`;
+
+ const fs = `#version 300 es
+#extension GL_ANGLE_clip_cull_distance : require
+precision highp float;
+out vec4 my_FragColor;
+void main()
+{
+ float r = gl_CullDistance[0] + gl_CullDistance[1];
+ float g = gl_CullDistance[2] + gl_CullDistance[3];
+ float b = gl_CullDistance[4] + gl_CullDistance[5];
+ float a = gl_CullDistance[6] + gl_CullDistance[7];
+ my_FragColor = vec4(r, g, b, a) * 0.25;
+}`;
+
+ const program = wtu.setupProgram(gl, [vs, fs]);
+ gl.useProgram(program);
+
+ // Clear to blue
+ gl.clearColor(0, 0, 1, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ wtu.setupQuad(gl, {scale: 0.5});
+
+ // Draw a small quad with color gray
+ wtu.drawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+
+ const data = new Uint8Array(w * h * 4);
+ gl.readPixels(0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, data);
+ let passed = true;
+ for (let x = 0; x < w; x++) {
+ for (let y = 0; y < h; y++) {
+ const currentPosition = (y * h + x) * 4;
+ const inside = (x >= w / 4 && x < w * 3 / 4 && y >= h / 4 && y < h * 3 / 4);
+ const expected = inside ? [127, 127, 127, 127] : [0, 0, 255, 255];
+ const actual = data.slice(currentPosition, currentPosition + 4);
+ if (Math.abs(actual[0] - expected[0]) > 1 ||
+ Math.abs(actual[1] - expected[1]) > 1 ||
+ Math.abs(actual[2] - expected[2]) > 1 ||
+ Math.abs(actual[3] - expected[3]) > 1) {
+ passed = false;
+ }
+ }
+ }
+ if (passed) {
+ testPassed("Correct cull distance interpolation");
+ } else {
+ testFailed("Incorrect cull distance interpolation");
+ }
+}
+
+function runTestExtension() {
+ checkEnums();
+ checkQueries();
+
+ checkClipDistance();
+ checkClipDistanceInterpolation();
+
+ checkCullDistance();
+ checkCullDistanceInterpolation();
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("context does not exist");
+ } else {
+ testPassed("context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("WEBGL_clip_cull_distance");
+
+ wtu.runExtensionSupportedTest(gl, "WEBGL_clip_cull_distance", ext !== null);
+
+ if (ext !== null) {
+ runTestExtension();
+ } else {
+ testPassed("No WEBGL_clip_cull_distance support -- this is legal");
+ }
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-provoking-vertex.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-provoking-vertex.html
new file mode 100644
index 0000000000..3737409b3a
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-provoking-vertex.html
@@ -0,0 +1,165 @@
+<!--
+Copyright (c) 2022 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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_provoking_vertex Conformance Tests</title>
+<LINK rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas width="16" height="16" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the WEBGL_provoking_vertex extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c", null, 2);
+var ext;
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check getParameter without the extension");
+ shouldBeNull("gl.getParameter(0x8E4F /* PROVOKING_VERTEX_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ debug("");
+}
+
+function runTestExtension() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.FIRST_VERTEX_CONVENTION_WEBGL", "0x8E4D");
+ shouldBe("ext.LAST_VERTEX_CONVENTION_WEBGL", "0x8E4E");
+ shouldBe("ext.PROVOKING_VERTEX_WEBGL", "0x8E4F");
+
+ debug("");
+ debug("Check default state");
+ shouldBe("gl.getParameter(ext.PROVOKING_VERTEX_WEBGL)", "ext.LAST_VERTEX_CONVENTION_WEBGL");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "parameter known with the extension enabled");
+
+ debug("");
+ debug("Check state updates");
+ ext.provokingVertexWEBGL(ext.FIRST_VERTEX_CONVENTION_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "provokingVertexWEBGL(ext.FIRST_VERTEX_CONVENTION_WEBGL) generates no errors");
+ shouldBe("gl.getParameter(ext.PROVOKING_VERTEX_WEBGL)", "ext.FIRST_VERTEX_CONVENTION_WEBGL");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ ext.provokingVertexWEBGL(ext.LAST_VERTEX_CONVENTION_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "provokingVertexWEBGL(ext.LAST_VERTEX_CONVENTION_WEBGL) generates no errors");
+ shouldBe("gl.getParameter(ext.PROVOKING_VERTEX_WEBGL)", "ext.LAST_VERTEX_CONVENTION_WEBGL");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ debug("");
+ debug("Check invalid provoking vertex mode");
+ ext.provokingVertexWEBGL(ext.FIRST_VERTEX_CONVENTION_WEBGL);
+ ext.provokingVertexWEBGL(ext.PROVOKING_VERTEX_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "invalid provoking mode generates an error");
+ shouldBe("gl.getParameter(ext.PROVOKING_VERTEX_WEBGL)", "ext.FIRST_VERTEX_CONVENTION_WEBGL");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ debug("");
+ debug("Check provoking vertex operation");
+
+ const vs = `#version 300 es
+ in int intAttrib;
+ in vec2 position;
+ flat out int attrib;
+ void main() {
+ gl_Position = vec4(position, 0, 1);
+ attrib = intAttrib;
+ }`;
+
+ const fs = `#version 300 es
+ flat in int attrib;
+ out int fragColor;
+ void main() {
+ fragColor = attrib;
+ }`;
+
+ const program = wtu.setupProgram(gl, [vs, fs]);
+ gl.useProgram(program);
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.R32I, 16, 16);
+
+ const fb = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+ const vb = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vb);
+ const buf = new ArrayBuffer(36);
+ new Float32Array(buf, 0, 6).set([-1.0, -1.0, 3.0, -1.0, -1.0, 3.0]);
+ new Int32Array(buf, 24, 3).set([1, 2, 3]);
+ gl.bufferData(gl.ARRAY_BUFFER, buf, gl.STATIC_DRAW);
+
+ const positionLocation = gl.getAttribLocation(program, "position");
+ gl.enableVertexAttribArray(positionLocation);
+ gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
+
+ const intAttribLocation = gl.getAttribLocation(program, "intAttrib");
+ gl.enableVertexAttribArray(intAttribLocation);
+ gl.vertexAttribIPointer(intAttribLocation, 1, gl.INT, 0, 24);
+
+ const pixel = new Int32Array(4);
+
+ ext.provokingVertexWEBGL(ext.LAST_VERTEX_CONVENTION_WEBGL);
+ gl.clearBufferiv(gl.COLOR, 0, new Int32Array(4));
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA_INTEGER, gl.INT, pixel);
+
+ if (pixel[0] == 3) {
+ testPassed("Correct last provoking vertex");
+ } else {
+ testFailed("Incorrect last provoking vertex");
+ }
+
+ ext.provokingVertexWEBGL(ext.FIRST_VERTEX_CONVENTION_WEBGL);
+ gl.clearBufferiv(gl.COLOR, 0, new Int32Array(4));
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA_INTEGER, gl.INT, pixel);
+
+ if (pixel[0] == 1) {
+ testPassed("Correct first provoking vertex");
+ } else {
+ testFailed("Incorrect first provoking vertex");
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("context does not exist");
+ } else {
+ testPassed("context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("WEBGL_provoking_vertex");
+
+ wtu.runExtensionSupportedTest(gl, "WEBGL_provoking_vertex", ext !== null);
+
+ if (ext !== null) {
+ runTestExtension();
+ } else {
+ testPassed("No WEBGL_provoking_vertex support -- this is legal");
+ }
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-render-shared-exponent.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-render-shared-exponent.html
new file mode 100644
index 0000000000..11d505fcc6
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-render-shared-exponent.html
@@ -0,0 +1,251 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_render_shared_exponent Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the WEBGL_render_shared_exponent extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext(null, null, 2);
+var ext;
+const color = [64.0, 32.0, 16.0, 1.0];
+
+function drawTest() {
+ wtu.clearAndDrawUnitQuad(gl);
+
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, color,
+ "reading with the RGBA format and FLOAT type", 1,
+ new Float32Array(4), gl.FLOAT, gl.RGBA);
+
+ const implementationType = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_TYPE);
+ const implementationFormat = gl.getParameter(gl.IMPLEMENTATION_COLOR_READ_FORMAT);
+ if (implementationFormat == gl.RGB && implementationType == gl.UNSIGNED_INT_5_9_9_9_REV) {
+ // Shared exponent value may be implementation
+ // specific, so compare decoded values.
+ const value = new Uint32Array(1);
+ gl.readPixels(0, 0, 1, 1, gl.RGB, gl.UNSIGNED_INT_5_9_9_9_REV, value);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ let r = (value >> 0) & 0x1FF;
+ let g = (value >> 9) & 0x1FF;
+ let b = (value >> 18) & 0x1FF;
+ let e = (value >> 27) & 0x01F;
+ debug(`Raw value: 0x${value[0].toString(16).toUpperCase()}, ` +
+ `Raw components: R = ${r}, G = ${g}, B = ${b}, E = ${e}`);
+
+ e = Math.pow(2, e - 24);
+ r *= e;
+ g *= e;
+ b *= e;
+ debug(`Decoded color: (${r}, ${g}, ${b})`);
+
+ if (r == color[0] && g == color[1] && b == color[2]) {
+ testPassed("reading with the exact format/type");
+ } else {
+ testFailed("reading with the exact format/type");
+ }
+ }
+}
+
+function renderbufferTest(isSupported) {
+ debug("");
+ debug(`RGB9_E5 renderbuffer: ` +
+ `${!isSupported ? "NOT " : ""}supported`);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB9_E5, 1, 1);
+ if (!isSupported) {
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "renderbuffer allocation failed");
+ return;
+ }
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "renderbuffer allocation succeeded");
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ drawTest();
+}
+
+function textureTest(isRenderable) {
+ debug("");
+ debug(`RGB9_E5 texture: ` +
+ `${!isRenderable ? "NOT " : ""}renderable`);
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB9_E5, 1, 1, 0, gl.RGB, gl.UNSIGNED_INT_5_9_9_9_REV, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture allocation succeeded");
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+ if (!isRenderable) {
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
+ return;
+ }
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ drawTest();
+}
+
+function formatTest(isEnabled) {
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShader,
+ wtu.simpleColorFragmentShader]);
+ gl.useProgram(program);
+ gl.uniform4fv(gl.getUniformLocation(program, "u_color"), color);
+
+ wtu.setupUnitQuad(gl);
+
+ renderbufferTest(isEnabled);
+ textureTest(isEnabled);
+}
+
+function colorMaskTest() {
+ debug("");
+ debug("Test color write masks with shared exponent color buffers");
+
+ const fs = `#version 300 es
+ precision highp float;
+ layout(location = 0) out vec4 color0;
+ layout(location = 1) out vec4 color1;
+ void main() {
+ color0 = vec4(1.0, 0.0, 0.0, 1.0);
+ color1 = vec4(0.0, 1.0, 0.0, 1.0);
+ }`;
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, fs]);
+ gl.useProgram(program);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ const rb0 = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rb0);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB9_E5, 4, 4);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb0);
+
+ const rb1 = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rb1);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, 4, 4);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.RENDERBUFFER, rb1);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ const clearValue = new Float32Array(4);
+ const dbiExt = gl.getExtension("OES_draw_buffers_indexed");
+
+ function expectError(enabled, effectiveMask, operation) {
+ if (!enabled ||
+ effectiveMask == 0x0 /* 0000 */ ||
+ effectiveMask == 0x8 /* 000A */ ||
+ effectiveMask == 0x7 /* RGB0 */ ||
+ effectiveMask == 0xF /* RGBA */ ) {
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, operation);
+ } else {
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, operation);
+ }
+ }
+
+ function runOps(enabled, mask0) {
+ wtu.drawUnitQuad(gl);
+ expectError(enabled, mask0, "draw");
+
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ expectError(enabled, mask0, "clear");
+
+ gl.clearBufferfv(gl.COLOR, 0, clearValue);
+ expectError(enabled, mask0, "clearBufferfv(RGB9_E5)");
+ gl.clearBufferfv(gl.COLOR, 1, clearValue);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "clearBufferfv(RGBA8)");
+ }
+
+ for (let mask = 0; mask < 16; mask++) {
+ for (const enabled of [false, true]) {
+ debug("");
+ debug(`Setting common color mask ` +
+ `${mask & 1 ? "R" : "0"}` +
+ `${mask & 2 ? "G" : "0"}` +
+ `${mask & 4 ? "B" : "0"}` +
+ `${mask & 8 ? "A" : "0"}` +
+ " with RGB9_E5 attachment " +
+ (enabled ? "enabled" : "disabled"));
+ gl.colorMask(mask & 1, mask & 2, mask & 4, mask & 8);
+ gl.drawBuffers([enabled ? gl.COLOR_ATTACHMENT0 : gl.NONE,
+ gl.COLOR_ATTACHMENT1]);
+
+ runOps(enabled, mask);
+
+ if (dbiExt) {
+ debug("Setting incompatible color mask on unused draw buffer")
+ dbiExt.colorMaskiOES(2, true, false, false, false);
+ runOps(enabled, mask); // common mask remains on draw buffer 0
+
+ debug("Setting incompatible color mask on RGBA8 draw buffer")
+ dbiExt.colorMaskiOES(1, true, false, false, false);
+ runOps(enabled, mask); // common mask remains on draw buffer 0
+
+ debug("Setting incompatible color mask on RGB9_E5 draw buffer")
+ dbiExt.colorMaskiOES(0, true, false, false, false);
+ runOps(enabled, 1); // overridden
+
+ debug("Setting compatible color mask on RGB9_E5 draw buffer")
+ dbiExt.colorMaskiOES(0, true, true, true, false);
+ runOps(enabled, 7); // overridden
+ }
+ }
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+ testPassed("context exists");
+
+ debug("");
+ debug("Testing shared exponent rendering with extension disabled");
+ formatTest(false);
+
+ ext = gl.getExtension("WEBGL_render_shared_exponent");
+ wtu.runExtensionSupportedTest(gl, "WEBGL_render_shared_exponent", ext !== null);
+
+ if (ext !== null) {
+ debug("");
+ debug("Testing shared exponent rendering with extension enabled");
+ formatTest(true);
+ colorMaskTest();
+ } else {
+ testPassed("No WEBGL_render_shared_exponent support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-shader-pixel-local-storage.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-shader-pixel-local-storage.html
new file mode 100644
index 0000000000..e548eea46c
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-shader-pixel-local-storage.html
@@ -0,0 +1,445 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_shader_pixel_local_storage Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/desktop-gl-constants.js"></script>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/tests/compositing-test.js"></script>
+<script src="../../js/tests/invalid-vertex-attrib-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" width="128" height="128" style="background-color:#080"> </canvas>
+<canvas id="canvas_no_alpha" width="128" height="128"> </canvas>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the WEBGL_shader_pixel_local_storage " +
+ "extension, if it is available.");
+
+const wtu = WebGLTestUtils;
+const canvas = document.getElementById("canvas");
+const gl = wtu.create3DContext(canvas, {alpha: true}, 2);
+const gl_no_alpha = wtu.create3DContext("canvas_no_alpha", {alpha: false}, 2);
+let pls = null;
+
+// Outputs a fullscreen quad from a 4-vertex triangle strip.
+const fullscreenQuadVertexShader = `#version 300 es
+void main() {
+ gl_Position.x = (gl_VertexID & 1) == 0 ? -1. : 1.;
+ gl_Position.y = (gl_VertexID & 2) == 0 ? -1. : 1.;
+ gl_Position.zw = vec2(0, 1);
+}`;
+
+function arraysEqual(a, b) {
+ if (typeof a !== typeof b)
+ return false;
+ if (a.length != b.length)
+ return false;
+ for (let i = 0; i < a.length; ++i) {
+ if (a[i] !== b[i])
+ return false;
+ }
+ return true;
+}
+
+async function runTest() {
+ if (!gl) {
+ testFailed("WebGL2 context does not exist");
+ finishTest();
+ return;
+ }
+
+ debug("\nCheck the behavior surrounding WEBGL_shader_pixel_local_storage being enabled.");
+ checkExtensionNotSupportedWhenDisabled();
+ checkDependencyExtensionsEnabled(false);
+ debug("Enable WEBGL_shader_pixel_local_storage.");
+ pls = gl.getExtension("WEBGL_shader_pixel_local_storage");
+ wtu.runExtensionSupportedTest(gl, "WEBGL_shader_pixel_local_storage", pls != null);
+ if (!pls) {
+ finishTest();
+ return;
+ }
+ checkDependencyExtensionsEnabled(true);
+
+ checkImplementationDependentLimits();
+ checkInitialValues();
+ checkWebGLNonNormativeBehavior();
+
+ await checkRendering(gl);
+ await checkRendering(gl_no_alpha);
+
+ finishTest();
+}
+
+function checkExtensionNotSupportedWhenDisabled() {
+ debug("\nCheck that a context does not support WEBGL_shader_pixel_local_storage before it is " +
+ "enabled");
+ shouldBeNull("gl.getParameter(0x96E0 /*MAX_PIXEL_LOCAL_STORAGE_PLANES_WEBGL*/)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ shouldBeNull(
+ "gl.getParameter(0x96E1 /*MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE_WEBGL*/)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ shouldBeNull(
+ "gl.getParameter(0x96E2 /*MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES_WEBGL*/)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ shouldBeNull(
+ "gl.getParameter(0x96E3 /*PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL*/)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NONE);
+}
+
+function checkDependencyExtensionsEnabled(enabled) {
+ debug("\nCheck that dependency extensions of WEBGL_shader_pixel_local_storage are " +
+ (enabled ? "enabled" : "disabled"));
+ if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "OES_draw_buffers_indexed") !== undefined) {
+ gl.getIndexedParameter(gl.BLEND_EQUATION_RGB, 1);
+ wtu.glErrorShouldBe(gl, enabled ? gl.NONE : gl.INVALID_ENUM,
+ "OES_draw_buffers_indexed not enabled or disabled as expected");
+ }
+ if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "EXT_color_buffer_float") !== undefined) {
+ gl.bindRenderbuffer(gl.RENDERBUFFER, gl.createRenderbuffer());
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.R32F, 1, 1);
+ wtu.glErrorShouldBe(gl, enabled ? gl.NONE : gl.INVALID_ENUM,
+ "EXT_color_buffer_float not enabled or disabled as expected");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ }
+ if (wtu.getSupportedExtensionWithKnownPrefixes(gl, "EXT_color_buffer_half_float") !== undefined) {
+ gl.bindRenderbuffer(gl.RENDERBUFFER, gl.createRenderbuffer());
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RG16F, 1, 1);
+ wtu.glErrorShouldBe(gl, enabled ? gl.NONE : gl.INVALID_ENUM,
+ "EXT_color_buffer_half_float not enabled or disabled as expected");
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ }
+}
+
+function checkImplementationDependentLimits() {
+ debug("\nVerify conformant implementation-dependent PLS limits.");
+ window.MAX_PIXEL_LOCAL_STORAGE_PLANES =
+ gl.getParameter(pls.MAX_PIXEL_LOCAL_STORAGE_PLANES_WEBGL);
+ window.MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE =
+ gl.getParameter(pls.MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE_WEBGL);
+ window.MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES =
+ gl.getParameter(pls.MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.NONE, "Pixel local storage queries should be supported.");
+
+ window.MAX_COLOR_ATTACHMENTS = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS);
+ window.MAX_DRAW_BUFFERS = gl.getParameter(gl.MAX_DRAW_BUFFERS);
+
+ // Table 6.X: Impementation Dependent Pixel Local Storage Limits.
+ shouldBeTrue("MAX_PIXEL_LOCAL_STORAGE_PLANES >= 4");
+ shouldBeTrue("MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE >= 0");
+ shouldBeTrue("MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES >= 4");
+
+ // Logical deductions based on 6.X.
+ shouldBeTrue(`MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES >=
+ MAX_PIXEL_LOCAL_STORAGE_PLANES`);
+ shouldBeTrue(`MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES >=
+ MAX_COLOR_ATTACHMENTS_WITH_ACTIVE_PIXEL_LOCAL_STORAGE`);
+ shouldBeTrue(`MAX_COLOR_ATTACHMENTS + MAX_PIXEL_LOCAL_STORAGE_PLANES >=
+ MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES`);
+ shouldBeTrue(`MAX_DRAW_BUFFERS + MAX_PIXEL_LOCAL_STORAGE_PLANES >=
+ MAX_COMBINED_DRAW_BUFFERS_AND_PIXEL_LOCAL_STORAGE_PLANES`);
+}
+
+function checkInitialValues() {
+ debug("\nCheck that PLS state has the correct initial values.");
+ shouldBeTrue("gl.getParameter(pls.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
+ wtu.glErrorShouldBe(
+ gl, gl.NONE,
+ "It's valid to query GL_PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL even when fbo 0 is bound.");
+
+ // Table 6.Y: Pixel Local Storage State
+ gl.bindFramebuffer(gl.FRAMEBUFFER, gl.createFramebuffer());
+ shouldBeTrue("gl.getParameter(pls.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
+ debug("Check the initial clear values for each plane.");
+ const MAX_PIXEL_LOCAL_STORAGE_PLANES =
+ gl.getParameter(pls.MAX_PIXEL_LOCAL_STORAGE_PLANES_WEBGL);
+ for (let i = 0; i < MAX_PIXEL_LOCAL_STORAGE_PLANES; ++i)
+ {
+ expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ i, pls.PIXEL_LOCAL_FORMAT_WEBGL) == gl.NONE);
+ expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ i, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) == null);
+ expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ i, pls.PIXEL_LOCAL_TEXTURE_LEVEL_WEBGL) == 0);
+ expectTrue(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ i, pls.PIXEL_LOCAL_TEXTURE_LAYER_WEBGL) == 0);
+ expectTrue(arraysEqual(
+ pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ i, pls.PIXEL_LOCAL_CLEAR_VALUE_FLOAT_WEBGL),
+ new Float32Array([0, 0, 0, 0])));
+ expectTrue(arraysEqual(
+ pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ i, pls.PIXEL_LOCAL_CLEAR_VALUE_INT_WEBGL),
+ new Int32Array([0, 0, 0, 0])));
+ expectTrue(arraysEqual(
+ pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ i, pls.PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_WEBGL),
+ new Uint32Array([0, 0, 0, 0])));
+ }
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+}
+
+function checkWebGLNonNormativeBehavior() {
+ debug("\nCheck the WebGL-specific behavior not found in the " +
+ "ANGLE_shader_pixel_local_storage specification.");
+ gl.bindFramebuffer(gl.FRAMEBUFFER, gl.createFramebuffer());
+
+ debug("If 'texture' has been deleted, generates an INVALID_OPERATION error.");
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 1, 1);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ gl.deleteTexture(tex);
+ pls.framebufferTexturePixelLocalStorageWEBGL(0, tex, 0, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+
+ debug("\nIf 'texture' was generated by a different WebGL2RenderingContext than this one, " +
+ "generates an INVALID_OPERATION error.");
+ const gl2 = wtu.create3DContext(null, null, 2);
+ const tex2 = gl2.createTexture();
+ gl2.bindTexture(gl2.TEXTURE_2D, tex2);
+ gl2.texStorage2D(gl2.TEXTURE_2D, 1, gl2.RGBA8, 1, 1);
+ pls.framebufferTexturePixelLocalStorageWEBGL(0, tex2, 0, 0);
+ wtu.glErrorShouldBe(gl2, gl2.NONE);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+
+ debug("\nIf value has less than srcOffset + 4 elements, generates an INVALID_VALUE error.");
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ pls.framebufferPixelLocalClearValuefvWEBGL(0, new Float32Array(3));
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValuefvWEBGL(1, [0, 0, 0]);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueivWEBGL(2, new Int32Array(3));
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueivWEBGL(3, [0, 0, 0]);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueuivWEBGL(4, new Uint32Array(3));
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueuivWEBGL(3, [0, 0, 0]);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValuefvWEBGL(2, new Float32Array(5), 2);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValuefvWEBGL(1, [0, 0, 0, 0, 0], 2);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueivWEBGL(0, new Int32Array(5), 2);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueivWEBGL(1, [0, 0, 0, 0, 0], 2);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueuivWEBGL(2, new Uint32Array(5), 2);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+ pls.framebufferPixelLocalClearValueuivWEBGL(3, [0, 0, 0, 0, 0], 2);
+ wtu.glErrorShouldBe(gl, gl.INVALID_VALUE);
+
+ debug("\nCheck that srcOffset works properly.");
+ const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+ pls.framebufferPixelLocalClearValuefvWEBGL(0, new Float32Array(arr), 1);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 0, pls.PIXEL_LOCAL_CLEAR_VALUE_FLOAT_WEBGL),
+ new Float32Array([1, 2, 3, 4]))`);
+ pls.framebufferPixelLocalClearValuefvWEBGL(1, arr, 2);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 1, pls.PIXEL_LOCAL_CLEAR_VALUE_FLOAT_WEBGL),
+ [2, 3, 4, 5])`);
+ pls.framebufferPixelLocalClearValueivWEBGL(2, new Int32Array(arr), 3);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 2, pls.PIXEL_LOCAL_CLEAR_VALUE_INT_WEBGL),
+ new Float32Array([3, 4, 5, 6]))`);
+ pls.framebufferPixelLocalClearValueivWEBGL(3, arr, 4);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 3, pls.PIXEL_LOCAL_CLEAR_VALUE_INT_WEBGL),
+ [4, 5, 6, 7])`);
+ pls.framebufferPixelLocalClearValueuivWEBGL(2, new Uint32Array(arr), 5);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 2, pls.PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_WEBGL),
+ new Uint32Array([5, 6, 7, 8]))`);
+ pls.framebufferPixelLocalClearValueuivWEBGL(1, arr, 6);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ shouldBeTrue(`arraysEqual(pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 1, pls.PIXEL_LOCAL_CLEAR_VALUE_UNSIGNED_INT_WEBGL),
+ [6, 7, 8, 9])`);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+
+ debug("\nCheck that PIXEL_LOCAL_TEXTURE_NAME_WEBGL returns a WebGLTexture.");
+ shouldBeTrue(`pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 0, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) === null`);
+ window.validTex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, validTex);
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 1, 1);
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ pls.framebufferTexturePixelLocalStorageWEBGL(0, validTex, 0, 0);
+ shouldBeTrue(`pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 0, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) === validTex`);
+ pls.framebufferTexturePixelLocalStorageWEBGL(0, null, 0, 0);
+ shouldBeTrue(`pls.getFramebufferPixelLocalStorageParameterWEBGL(
+ 0, pls.PIXEL_LOCAL_TEXTURE_NAME_WEBGL) === null`);
+
+ wtu.glErrorShouldBe(gl, gl.NONE);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+}
+
+async function checkRendering(localGL) {
+ const localCanvas = localGL.canvas;
+ const alpha = localGL.getContextAttributes().alpha;
+ debug("\nCheck very simple rendering with {alpha: " + alpha + "}");
+
+ const localPLS = localGL.getExtension("WEBGL_shader_pixel_local_storage");
+ if (!localPLS) {
+ testFailed("localGL doesn't support pixel local storage.");
+ return;
+ }
+
+ const tex = localGL.createTexture();
+ localGL.bindTexture(localGL.TEXTURE_2D, tex);
+ localGL.texStorage2D(localGL.TEXTURE_2D, 1, localGL.RGBA8, localCanvas.width, localCanvas.height);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+
+ const plsFBO = localGL.createFramebuffer();
+ localGL.bindFramebuffer(localGL.FRAMEBUFFER, plsFBO);
+ localPLS.framebufferTexturePixelLocalStorageWEBGL(0, tex, 0, 0);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+
+ localGL.viewport(0, 0, localCanvas.width, localCanvas.height);
+
+ // Adds a uniform color into the existing color in pixel local storage.
+ const fs = `#version 300 es
+ #extension GL_ANGLE_shader_pixel_local_storage : require
+ precision lowp float;
+ uniform vec4 color;
+ layout(binding=0, rgba8) uniform lowp pixelLocalANGLE pls;
+ void main() {
+ vec4 newColor = color + pixelLocalLoadANGLE(pls);
+ pixelLocalStoreANGLE(pls, newColor);
+ }`;
+
+ const program = wtu.setupProgram(localGL, [fullscreenQuadVertexShader, fs]);
+ if (!program) {
+ testFailed("Failed to compile program.");
+ return;
+ }
+
+ localGL.useProgram(program);
+ const colorUniLocation = localGL.getUniformLocation(program, "color");
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+
+ // Disable color mask to ensure PLS and canvas manage their own color masks properly.
+ localGL.colorMask(false, true, false, true);
+
+ // Set global variables for shouldBeTrue().
+ window.localGL = localGL;
+ window.localPLS = localPLS;
+
+ debug("\nCheck that pixel local storage works properly");
+ localGL.disable(localGL.DITHER);
+ localPLS.beginPixelLocalStorageWEBGL([localPLS.LOAD_OP_ZERO_WEBGL]);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+ shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 1");
+
+ localGL.uniform4f(colorUniLocation, 0, 1, 0, 0);
+ localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
+
+ localPLS.pixelLocalStorageBarrierWEBGL();
+
+ localGL.uniform4f(colorUniLocation, 1, 0, 0, 0);
+ localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
+
+ localPLS.endPixelLocalStorageWEBGL([localPLS.STORE_OP_STORE_WEBGL]);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+ shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
+
+ const readFBO = localGL.createFramebuffer();
+ localGL.bindFramebuffer(localGL.READ_FRAMEBUFFER, readFBO);
+ localGL.framebufferTexture2D(localGL.READ_FRAMEBUFFER, localGL.COLOR_ATTACHMENT0,
+ localGL.TEXTURE_2D, tex, 0);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+ wtu.checkCanvas(localGL, [255, 255, 0, 0]);
+
+ debug("\nCheck that alpha is properly handled in the main canvas.");
+ localGL.bindFramebuffer(localGL.DRAW_FRAMEBUFFER, null);
+ localGL.blitFramebuffer(0, 0, localCanvas.width, localCanvas.height, 0, 0, localCanvas.width,
+ localCanvas.height, localGL.COLOR_BUFFER_BIT, localGL.NEAREST);
+ localGL.bindFramebuffer(localGL.FRAMEBUFFER, null);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+ wtu.checkCanvas(localGL, [255, 255, 0, alpha ? 0 : 255]);
+
+ localGL.bindFramebuffer(localGL.FRAMEBUFFER, plsFBO);
+ localPLS.beginPixelLocalStorageWEBGL([localPLS.LOAD_OP_LOAD_WEBGL]);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+ shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 1");
+
+ debug("\nGoing down from composite.");
+
+ // The canvas should get cleared after compositing, even if PLS is active and color mask is
+ // disabled.
+ await new Promise(resolve => wtu.waitForComposite(resolve));
+
+ // Reset global variables for shouldBeTrue() after await.
+ window.localGL = localGL;
+ window.localPLS = localPLS;
+
+ debug("\nBack from composite!");
+ debug("\nPLS should still be active on plsFBO even after being interrupted for compositing.");
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+ shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 1");
+
+ localGL.uniform4f(colorUniLocation, 0, 0, 1, 0);
+ localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
+
+ localPLS.endPixelLocalStorageWEBGL([localPLS.STORE_OP_STORE_WEBGL]);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+ shouldBeTrue("localGL.getParameter(localPLS.PIXEL_LOCAL_STORAGE_ACTIVE_PLANES_WEBGL) == 0");
+
+ debug("\nThe canvas should have gotten cleared while PLS was active.");
+ localGL.bindFramebuffer(localGL.FRAMEBUFFER, null);
+ wtu.checkCanvas(localGL, [0, 0, 0, alpha ? 0 : 255]);
+
+ debug("\nThe additional PLS draw to plsFBO should have still worked after being interrupted " +
+ "for compositing.");
+ localGL.bindFramebuffer(localGL.READ_FRAMEBUFFER, readFBO);
+ wtu.checkCanvas(localGL, [255, 255, 255, 0]);
+ wtu.glErrorShouldBe(localGL, localGL.NONE);
+
+ // Draws 'tex' to the canvas.
+ const fs2 = `#version 300 es
+ uniform lowp sampler2D tex;
+ out lowp vec4 fragColor;
+ void main() {
+ ivec2 pixelCoord = ivec2(floor(gl_FragCoord.xy));
+ fragColor = texelFetch(tex, pixelCoord, 0);
+ }`;
+
+ const program2 = wtu.setupProgram(localGL, [fullscreenQuadVertexShader, fs2]);
+ if (!program2) {
+ testFailed("Failed to compile program2.");
+ return;
+ }
+
+ debug("\nBlue should still be disabled in the color mask. Alpha is not disabled but should be " +
+ "implicitly disabled since the canvas doesn't have alpha.");
+ localGL.useProgram(program2);
+ localGL.uniform1i(localGL.getUniformLocation(program2, "tex"), 0);
+ localGL.bindFramebuffer(localGL.FRAMEBUFFER, null);
+ localGL.drawArrays(localGL.TRIANGLE_STRIP, 0, 4);
+ wtu.checkCanvas(localGL, [0, 255, 0, alpha ? 0 : 255]);
+
+ debug("\nThe client's color mask should have been preserved.");
+ shouldBeTrue(`arraysEqual(localGL.getParameter(localGL.COLOR_WRITEMASK),
+ [false, true, false, true])`);
+}
+
+runTest();
+var successfullyParsed = true;
+</script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-stencil-texturing.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-stencil-texturing.html
new file mode 100644
index 0000000000..729a5bcf8a
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-stencil-texturing.html
@@ -0,0 +1,279 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL WEBGL_stencil_texturing Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the WEBGL_stencil_texturing extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext(null, null, 2);
+var ext;
+
+function runTestNoExtension() {
+ debug("");
+ debug("Check the texture parameter without the extension");
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+
+ shouldBeNull("gl.getTexParameter(gl.TEXTURE_2D, 0x90EA /* DEPTH_STENCIL_TEXTURE_MODE_WEBGL */)");
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+
+ gl.texParameteri(gl.TEXTURE_2D, 0x90EA /* DEPTH_STENCIL_TEXTURE_MODE_WEBGL */, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown for texParameteri without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+
+ gl.texParameterf(gl.TEXTURE_2D, 0x90EA /* DEPTH_STENCIL_TEXTURE_MODE_WEBGL */, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown for texParameterf without enabling the extension");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+
+ const sampler = gl.createSampler();
+ gl.samplerParameteri(sampler, 0x90EA /* DEPTH_STENCIL_TEXTURE_MODE_WEBGL */, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown for samplerParameteri");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+ gl.samplerParameterf(sampler, 0x90EA /* DEPTH_STENCIL_TEXTURE_MODE_WEBGL */, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown for samplerParameterf");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+}
+
+function checkEnums() {
+ debug("");
+ debug("Check enums");
+ shouldBe("ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL", "0x90EA");
+ shouldBe("ext.STENCIL_INDEX_WEBGL", "0x1901");
+}
+
+function checkQueries() {
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+
+ debug("");
+ debug("Check default texture state");
+ shouldBe('gl.getTexParameter(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL)', 'gl.DEPTH_COMPONENT');
+ debug("");
+ debug("Check texture state updates using texParameteri");
+ gl.texParameteri(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, ext.STENCIL_INDEX_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBe('gl.getTexParameter(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL)', 'ext.STENCIL_INDEX_WEBGL');
+ gl.texParameteri(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBe('gl.getTexParameter(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL)', 'gl.DEPTH_COMPONENT');
+ gl.texParameteri(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "invalid depth stencil mode value rejected by texParameteri");
+ shouldBe('gl.getTexParameter(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL)', 'gl.DEPTH_COMPONENT');
+ debug("");
+ debug("Check texture state updates using texParameterf");
+ gl.texParameterf(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, ext.STENCIL_INDEX_WEBGL);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBe('gl.getTexParameter(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL)', 'ext.STENCIL_INDEX_WEBGL');
+ gl.texParameterf(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ shouldBe('gl.getTexParameter(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL)', 'gl.DEPTH_COMPONENT');
+ gl.texParameterf(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, 0);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "invalid depth stencil mode value rejected by texParameterf");
+ shouldBe('gl.getTexParameter(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL)', 'gl.DEPTH_COMPONENT');
+
+ debug("");
+ debug("Check that depth stencil texture mode is not accepted as a sampler state");
+ const sampler = gl.createSampler();
+ gl.samplerParameteri(sampler, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown for samplerParameteri");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+ gl.samplerParameterf(sampler, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, gl.DEPTH_COMPONENT);
+ wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown for samplerParameterf");
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no more errors");
+}
+
+function checkSampling() {
+ const formats = [
+ {name: "DEPTH_COMPONENT16", internalFormat: gl.DEPTH_COMPONENT16,
+ format: gl.DEPTH_COMPONENT, type: gl.UNSIGNED_SHORT},
+ {name: "DEPTH_COMPONENT24", internalFormat: gl.DEPTH_COMPONENT24,
+ format: gl.DEPTH_COMPONENT, type: gl.UNSIGNED_INT},
+ {name: "DEPTH_COMPONENT32F", internalFormat: gl.DEPTH_COMPONENT32F,
+ format: gl.DEPTH_COMPONENT, type: gl.FLOAT},
+ {name: "DEPTH24_STENCIL8", internalFormat: gl.DEPTH24_STENCIL8,
+ format: gl.DEPTH_STENCIL, type: gl.UNSIGNED_INT_24_8},
+ {name: "DEPTH32F_STENCIL8", internalFormat: gl.DEPTH32F_STENCIL8,
+ format: gl.DEPTH_STENCIL, type: gl.FLOAT_32_UNSIGNED_INT_24_8_REV}
+ ];
+
+ gl.enable(gl.DEPTH_TEST);
+ gl.enable(gl.STENCIL_TEST);
+ gl.stencilFunc(gl.ALWAYS, 170, 0xFF);
+ gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);
+
+ wtu.setupUnitQuad(gl);
+
+ const drawProgram = wtu.setupProgram(gl, [wtu.simpleVertexShader,
+ wtu.simpleColorFragmentShader]);
+
+ const readDepthProgram = wtu.setupProgram(gl, [wtu.simpleTextureVertexShaderESSL300,
+ wtu.simpleTextureFragmentShaderESSL300]);
+
+ const readStencilShader = `#version 300 es
+ precision highp float;
+ uniform highp usampler2D tex;
+ in vec2 texCoord;
+ out vec4 out_color;
+ void main() {
+ out_color = vec4(texture(tex, texCoord)) / 255.0;
+ }`;
+ const readStencilProgram = wtu.setupProgram(gl, [wtu.simpleTextureVertexShaderESSL300,
+ readStencilShader]);
+
+ for (const format of formats) {
+ debug("");
+ debug(`Testing depth stencil texture modes with ${format.name}`);
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, 1, 1);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, format.internalFormat, 1, 1, 0, format.format, format.type, null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture created");
+
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, tex, 0);
+ if (format.format == gl.DEPTH_STENCIL) {
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.TEXTURE_2D, tex, 0);
+ }
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.clear(gl.DEPTH_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
+ gl.useProgram(drawProgram);
+ wtu.drawUnitQuad(gl);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors after drawing to the depth or depth stencil texture");
+
+ // Detach the depth or depth stencil texture to avoid feedback loop
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, null);
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ const magFilters = ['NEAREST', 'LINEAR'];
+
+ const minFilters = [
+ 'NEAREST',
+ 'LINEAR',
+ 'NEAREST_MIPMAP_NEAREST',
+ 'LINEAR_MIPMAP_NEAREST',
+ 'NEAREST_MIPMAP_LINEAR',
+ 'LINEAR_MIPMAP_LINEAR'
+ ];
+
+ const modes = [
+ [gl.DEPTH_COMPONENT, 'DEPTH_COMPONENT'],
+ [ext.STENCIL_INDEX_WEBGL, 'STENCIL_INDEX_WEBGL']
+ ];
+
+ const programs = [
+ [readDepthProgram, 'depth'],
+ [readStencilProgram, 'stencil']
+ ];
+
+ function validFilters(magFilter, minFilter) {
+ return magFilter == gl.NEAREST &&
+ (minFilter == gl.NEAREST || minFilter == gl.NEAREST_MIPMAP_NEAREST);
+ }
+
+ for (const program of programs) {
+ gl.useProgram(program[0]);
+ for (const mode of modes) {
+ gl.texParameteri(gl.TEXTURE_2D, ext.DEPTH_STENCIL_TEXTURE_MODE_WEBGL, mode[0]);
+ for (const magFilter of magFilters) {
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl[magFilter]);
+ for (const minFilter of minFilters) {
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl[minFilter]);
+ debug(`Program: ${program[1]}, mode: ${mode[1]}, mag: ${magFilter}, min: ${minFilter}`);
+
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ wtu.drawUnitQuad(gl);
+
+ if (format.format == gl.DEPTH_COMPONENT || mode[0] == gl.DEPTH_COMPONENT) {
+ if (program[1] == 'depth') {
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ if (validFilters(gl[magFilter], gl[minFilter])) {
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, [128, 0, 0, 255], "sampling depth from complete texture", 1);
+ } else {
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, [0, 0, 0, 255], "sampling depth from incomplete texture", 1);
+ }
+ } else {
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "sampling depth using incompatible program");
+ }
+ } else {
+ if (program[1] == 'stencil') {
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors");
+ if (validFilters(gl[magFilter], gl[minFilter])) {
+ wtu.checkCanvasRect(gl, 0, 0, 1, 1, [170, 0, 0, 1], "sampling stencil from complete texture", 1);
+ } else {
+ // Incomplete textures may produce [0, 0, 0, 1] or [0, 0, 0, 255].
+ const value = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, value);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ const msg = "sampling stencil from incomplete texture";
+ if (value[0] == 0 && value[1] == 0 && value[2] == 0 && (value[3] == 1 || value[3] == 255)) {
+ testPassed(msg);
+ } else {
+ testFailed(`${msg}: ${value}`);
+ }
+ }
+ } else {
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "sampling stencil using incompatible program");
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("context does not exist");
+ return;
+ }
+ testPassed("context exists");
+
+ runTestNoExtension();
+
+ ext = gl.getExtension("WEBGL_stencil_texturing");
+ wtu.runExtensionSupportedTest(gl, "WEBGL_stencil_texturing", ext !== null);
+
+ if (ext !== null) {
+ checkEnums();
+ checkQueries();
+ checkSampling();
+ } else {
+ testPassed("No WEBGL_stencil_texturing support -- this is legal");
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/00_test_list.txt
index 5a47d470f9..9e8bc87a62 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/00_test_list.txt
@@ -41,6 +41,7 @@ shader-with-invalid-characters.html
shader-with-mis-matching-uniform-block.html
short-circuiting-in-loop-condition.html
--min-version 2.0.1 switch-case.html
+--min-version 2.0.1 texture-bias.html
--min-version 2.0.1 texture-offset-non-constant-offset.html
texture-offset-out-of-range.html
--min-version 2.0.1 texture-offset-uniform-texture-coordinate.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/texture-bias.html b/dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/texture-bias.html
new file mode 100644
index 0000000000..0c30eb7129
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/texture-bias.html
@@ -0,0 +1,146 @@
+<!--
+Copyright (c) 2022 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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>GLSL texture bias test</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+<script src="../../js/glsl-conformance-test.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("Texture bias should both function and respect limits.");
+
+function runTest(gl) {
+ // no if idea any drivers have a giant limit like 2^32 so just in case.
+ const kMaxMaxTextureSize = 256 * 1024 * 1024;
+ const maxTextureSize = Math.min(kMaxMaxTextureSize, gl.getParameter(gl.MAX_TEXTURE_SIZE));
+ const maxLODs = (Math.log2(maxTextureSize) | 0) + 1;
+ const maxTextureLODBias = gl.getParameter(gl.MAX_TEXTURE_LOD_BIAS);
+
+ debug(`maxTextureSize: ${maxTextureSize}`);
+ debug(`maxLODs: ${maxLODs}`);
+ debug(`maxTextureLODBias: ${maxTextureLODBias}`);
+
+ const vs = `#version 300 es
+ uniform float uvMult;
+ out vec2 v_uv;
+ void main() {
+ vec2 xy = vec2(
+ gl_VertexID % 2,
+ (gl_VertexID / 2 + gl_VertexID / 3) % 2);
+
+ gl_Position = vec4(xy * 2. - 1.0, 0, 1);
+ v_uv = xy * uvMult;
+ }
+ `;
+ const fs = `#version 300 es
+ precision highp float;
+ uniform sampler2D tex;
+ uniform float biasMult;
+ in vec2 v_uv;
+ out vec4 fragColor;
+ void main() {
+ vec4 texColor = texture(tex, v_uv, (gl_FragCoord.x - 0.5) * biasMult); // the color we care about
+ vec4 texelColor = texelFetch(tex, ivec2(0), int(gl_FragCoord)); // just a sanity check
+ vec4 coordColor = vec4((100.0 + gl_FragCoord.x - 0.5) / 255.0); // another sanity check
+ fragColor = mix(texColor, coordColor, step(1.0, gl_FragCoord.y)); // line < 1 = texColor, line >= 1 = coordColor
+ fragColor = mix(fragColor, texelColor, step(2.0, gl_FragCoord.y)); // line < 2 = fragColor, line >= 2 = texelColor
+ }
+ `;
+ const program = wtu.setupProgram(gl, [vs, fs]);
+ const uvMultLoc = gl.getUniformLocation(program, 'uvMult');
+ const biasLoc = gl.getUniformLocation(program, 'biasMult');
+
+ gl.canvas.width = maxLODs;
+ gl.canvas.height = 3;
+ gl.viewport(0, 0, maxLODs, 3);
+
+ // create a texture where each mip is a different color (1, 2, 3, ...)
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST_MIPMAP_NEAREST);
+ gl.texStorage2D(gl.TEXTURE_2D, maxLODs, gl.RGBA8, maxTextureSize, 1);
+ {
+ let level = 0;
+ for (let width = maxTextureSize; width > 0; width = width / 2 | 0) {
+ const pixels = new Uint8Array(width * 1 * 4);
+ pixels.fill(level + 1);
+ debug(`fill mip level: ${level}, width: ${width}`);
+ gl.texSubImage2D(gl.TEXTURE_2D, level, 0, 0, width, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ ++level;
+ }
+ }
+
+ // Draw each mip. Result should be [mip0, mip1, mip2, ...]
+ debug("");
+ debug("check positive bias");
+ // set the UVs so we'd get mip level 0 for every pixel
+ gl.uniform1f(uvMultLoc, maxLODs / maxTextureSize);
+ gl.uniform1f(biasLoc, 1);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ const clampPlusMinus = (v, limit) => Math.min(limit, Math.max(-limit, v));
+
+ const checkResults = (gl, biasMult) => {
+ const base = biasMult > 0 ? 1 : maxLODs;
+ for (let i = 0; i < maxLODs; ++i) {
+ {
+ const expected = new Array(4).fill(clampPlusMinus(i * biasMult, maxTextureLODBias) + base);
+ wtu.checkCanvasRect(gl, i, 0, 1, 1, expected, `should be: ${expected}`);
+ }
+ {
+ const expected = new Array(4).fill(100 + i);
+ wtu.checkCanvasRect(gl, i, 1, 1, 1, expected, `should be: ${expected}`);
+ }
+ {
+ const expected = new Array(4).fill(i + 1);
+ wtu.checkCanvasRect(gl, i, 2, 1, 1, expected, `should be: ${expected}`);
+ }
+ }
+ }
+
+ checkResults(gl, 1);
+
+ // Draw each mip. Result should be [mipMax, mipMax - 1, mipMax - 2, ...]
+ debug("");
+ debug("check negative bias");
+ // set the UVs so we'd get highest mip level (the 1x1 level mip) for every pixel
+ gl.uniform1f(uvMultLoc, maxLODs);
+ gl.uniform1f(biasLoc, -1);
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
+
+ checkResults(gl, -1);
+
+ finishTest();
+}
+
+const wtu = WebGLTestUtils;
+
+const gl = wtu.create3DContext(undefined, undefined, 2);
+
+var successfullyParsed = true;
+
+if (!gl) {
+ testFailed("Unable to initialize WebGL 2.0 context.");
+} else {
+ runTest(gl);
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/query/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/query/00_test_list.txt
index c40921bf88..a2e21b5ad7 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/query/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/query/00_test_list.txt
@@ -1,2 +1,3 @@
occlusion-query.html
+--min-version 2.0.1 occlusion-query-scissor.html
query.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/query/occlusion-query-scissor.html b/dom/canvas/test/webgl-conf/checkout/conformance2/query/occlusion-query-scissor.html
new file mode 100644
index 0000000000..dec88e56d3
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/query/occlusion-query-scissor.html
@@ -0,0 +1,117 @@
+<!--
+Copyright (c) 2022 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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>WebGL Occlusion Query w/Scissor Conformance Tests</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<div id="description"></div>
+<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of occlusion query objects with the scissor test.");
+debug('Regression test for <a href="http://anglebug.com/7157">http://anglebug.com/7157</a>');
+
+debug("");
+
+const wtu = WebGLTestUtils;
+
+const wait = () => new Promise(resolve => setTimeout(resolve));
+
+async function runOcclusionQueryTest(gl) {
+ const kSize = 4;
+ const colors = [
+ [0, 0, 0, 255],
+ [255, 0, 0, 255],
+ [0, 255, 0, 255],
+ [255, 255, 0, 255],
+ ];
+ const framebuffers = colors.map((color, index) => {
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, kSize, kSize, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
+
+ const fb = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+ gl.clearColor(index & 1, (index >> 1) & 1, (index >> 2) & 1, 1);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+
+ wtu.checkCanvasRect(gl, 0, 0, kSize, kSize, color);
+
+ return fb;
+ });
+
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "setup should succeed");
+
+ gl.viewport(0, 0, kSize, kSize);
+
+ const program = wtu.setupSimpleColorProgram(gl, 0);
+ gl.uniform4f(gl.getUniformLocation(program, "u_color"), 0, 1, 0, 1);
+ wtu.setupUnitQuad(gl, 0);
+
+ const query = gl.createQuery();;
+
+ for (let i = 0; i < 256; ++i)
+ {
+ gl.beginQuery(gl.ANY_SAMPLES_PASSED, query);
+ let drawn = false;
+
+ framebuffers.forEach((fb, index) => {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+ const scissor = (i >> 4 >> index) & 1;
+ if (i & (1 << index))
+ {
+ if (scissor)
+ {
+ gl.enable(gl.SCISSOR_TEST);
+ gl.scissor(0, 0, 0, 0);
+ }
+ wtu.drawUnitQuad(gl);
+ drawn ||= !scissor;
+ if (scissor)
+ {
+ gl.disable(gl.SCISSOR_TEST);
+ gl.scissor(0, 0, kSize, kSize);
+ }
+ }
+ });
+
+ gl.endQuery(gl.ANY_SAMPLES_PASSED);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should draw");
+
+ do {
+ await wait();
+ } while (!gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE));
+
+ const result = gl.getQueryParameter(query, gl.QUERY_RESULT);
+
+ const expected = drawn ? 1 : 0;
+ assertMsg(result === expected, `pass ${i}, result: ${result} === expected: ${expected}`);
+ }
+ finishTest();
+}
+
+const canvas = document.getElementById("canvas");
+const gl = wtu.create3DContext(canvas, null, 2);
+
+if (!gl) {
+ testFailed("WebGL context does not exist");
+} else {
+ testPassed("WebGL context exists");
+ runOcclusionQueryTest(gl);
+}
+</script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html
index 754ff2cc73..bc4d623446 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-object-attachment.html
@@ -111,9 +111,10 @@ function testFramebufferWebGL1RequiredCombinations() {
gl.deleteFramebuffer(fbo);
}
-function testDepthStencilAttachmentBehaviors() {
+function testDepthStencilAttachmentBehaviors(testOrphanedRenderbuffers) {
+ let suffix = testOrphanedRenderbuffers ? " with deleted renderbuffer" : "";
debug("");
- debug("Checking ES3 DEPTH_STENCIL_ATTACHMENT behaviors are implemented for WebGL 2");
+ debug("Checking ES3 DEPTH_STENCIL_ATTACHMENT behaviors are implemented for WebGL 2" + suffix);
// DEPTH_STENCIL_ATTACHMENT is treated as an independent attachment point in WebGL 1;
// however, in WebGL 2, it is treated as an alias for DEPTH_ATTACHMENT + STENCIL_ATTACHMENT.
var size = 16;
@@ -127,24 +128,45 @@ function testDepthStencilAttachmentBehaviors() {
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA8, size, size);
checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
- var depthBuffer = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
+ function createDepthBuffer() {
+ let buffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, size, size);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ return buffer;
+ }
+
+ function createStencilBuffer() {
+ let buffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, size, size);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ return buffer;
+ }
- var stencilBuffer = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, stencilBuffer);
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, size, size);
+ function createDepthStencilBuffer() {
+ let buffer = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, buffer);
+ gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
+ gl.bindRenderbuffer(gl.RENDERBUFFER, null);
+ return buffer;
+ }
- var depthStencilBuffer = gl.createRenderbuffer();
- gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
- gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, size, size);
+ function orphan(renderbuffer) {
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
+ gl.deleteRenderbuffer(renderbuffer);
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ }
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
debug("");
- debug("color + depth");
+ debug("color + depth" + suffix);
+ var depthBuffer = createDepthBuffer();
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
@@ -153,9 +175,12 @@ function testDepthStencilAttachmentBehaviors() {
checkBufferBits(gl.DEPTH_ATTACHMENT);
debug("");
- debug("color + depth + stencil: depth != stencil");
+ debug("color + depth + stencil: depth != stencil" + suffix);
+ var stencilBuffer = createStencilBuffer();
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencilBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(stencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", stencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
@@ -170,9 +195,12 @@ function testDepthStencilAttachmentBehaviors() {
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
debug("");
- debug("color + depth: DEPTH_STENCIL for DEPTH_ATTACHMENT");
+ debug("color + depth: DEPTH_STENCIL for DEPTH_ATTACHMENT" + suffix);
+ var depthStencilBuffer = createDepthStencilBuffer();
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
@@ -181,9 +209,16 @@ function testDepthStencilAttachmentBehaviors() {
checkBufferBits(gl.DEPTH_ATTACHMENT);
debug("");
- debug("color + depth + stencil: DEPTH_STENCIL for DEPTH_ATTACHMENT and STENCIL_ATTACHMENT");
+ debug("color + depth + stencil: DEPTH_STENCIL for DEPTH_ATTACHMENT and STENCIL_ATTACHMENT" + suffix);
+ if (testOrphanedRenderbuffers) {
+ depthStencilBuffer = createDepthStencilBuffer();
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ }
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
@@ -192,7 +227,7 @@ function testDepthStencilAttachmentBehaviors() {
checkBufferBits(gl.DEPTH_STENCIL_ATTACHMENT);
debug("");
- debug("color + depth_stencil");
+ debug("color + depth_stencil" + suffix);
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH24_STENCIL8, size, size, 0, gl.DEPTH_STENCIL, gl.UNSIGNED_INT_24_8, null);
@@ -209,8 +244,12 @@ function testDepthStencilAttachmentBehaviors() {
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+ if (testOrphanedRenderbuffers)
+ depthStencilBuffer = createDepthStencilBuffer();
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
@@ -219,16 +258,24 @@ function testDepthStencilAttachmentBehaviors() {
checkBufferBits(gl.DEPTH_STENCIL_ATTACHMENT);
debug("");
- debug("DEPTH_STENCIL_ATTACHMENT overwrites DEPTH_ATTACHMENT/STENCIL_ATTACHMENT")
+ debug("DEPTH_STENCIL_ATTACHMENT overwrites DEPTH_ATTACHMENT/STENCIL_ATTACHMENT" + suffix);
+ if (testOrphanedRenderbuffers)
+ depthBuffer = createDepthBuffer();
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ if (testOrphanedRenderbuffers)
+ depthStencilBuffer = createDepthStencilBuffer();
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
@@ -244,9 +291,13 @@ function testDepthStencilAttachmentBehaviors() {
checkBufferBits();
debug("");
- debug("STENCIL_ATTACHMENT overwrites stencil set by DEPTH_STENCIL_ATTACHMENT")
+ debug("STENCIL_ATTACHMENT overwrites stencil set by DEPTH_STENCIL_ATTACHMENT" + suffix);
+ if (testOrphanedRenderbuffers)
+ depthStencilBuffer = createDepthStencilBuffer();
gl.framebufferRenderbuffer(
gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
@@ -260,6 +311,28 @@ function testDepthStencilAttachmentBehaviors() {
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
checkBufferBits(gl.DEPTH_ATTACHMENT);
+
+ debug("");
+ debug("DEPTH_ATTACHMENT overwrites depth set by DEPTH_STENCIL_ATTACHMENT" + suffix);
+ if (testOrphanedRenderbuffers)
+ depthStencilBuffer = createDepthStencilBuffer();
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
+ if (testOrphanedRenderbuffers)
+ orphan(depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+ gl.framebufferRenderbuffer(
+ gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", depthStencilBuffer);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", null);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ checkBufferBits(gl.STENCIL_ATTACHMENT);
}
function testFramebufferIncompleteAttachment() {
@@ -469,7 +542,8 @@ description("Test framebuffer object attachment behaviors");
shouldBeNonNull("gl = wtu.create3DContext(undefined, undefined, 2)");
testFramebufferWebGL1RequiredCombinations();
-testDepthStencilAttachmentBehaviors();
+testDepthStencilAttachmentBehaviors(false);
+testDepthStencilAttachmentBehaviors(true);
testFramebufferIncompleteAttachment();
testFramebufferIncompleteMissingAttachment();
testFramebufferWithImagesOfDifferentSizes();
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html
index 0e435d6a2e..13e5d790e8 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/renderbuffers/framebuffer-texture-layer.html
@@ -121,6 +121,37 @@ function testFramebufferTextureLayer() {
"attaching a depth_stencil texture to a framebuffer should succeed.");
checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ var texDepthStencil = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, texDepthStencil);
+
+ var texDepthStencilMany = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, texDepthStencilMany);
+ var fbDepthStencilMany = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbDepthStencilMany);
+ gl.texImage3D(gl.TEXTURE_2D_ARRAY,
+ 0, // level
+ gl.DEPTH24_STENCIL8, // internalFormat
+ 1, // width
+ 1, // height
+ 2, // depth
+ 0, // border
+ gl.DEPTH_STENCIL, // format
+ gl.UNSIGNED_INT_24_8, // type
+ new Uint32Array([0, 1])); // data
+
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, texDepthStencilMany, 0, 0);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, texDepthStencilMany, 0, 0);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "attaching a depth_stencil 2d array texture level 0 to a framebuffer should succeed.");
+ checkFramebuffer([gl.FRAMEBUFFER_COMPLETE]);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", texDepthStencilMany);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.STENCIL_ATTACHMENT, texDepthStencilMany, 0, 1);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR,
+ "attaching a 2d array texture level 0 to depth and layer 1 to stencil attachment of a framebuffer should succeed.");
+ // "Depth and stencil attachments, if present, are the same image." If not, then "FRAMEBUFFER_UNSUPPORTED".
+ checkFramebuffer([gl.FRAMEBUFFER_UNSUPPORTED]);
+ shouldEvaluateTo("gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_NAME)", texDepthStencilMany);
+
// Clean up
gl.deleteTexture(tex3d);
gl.deleteTexture(texDepthStencil);
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt
index 92ce232ee2..c4a1bbe9e8 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/00_test_list.txt
@@ -11,7 +11,6 @@ blitframebuffer-size-overflow.html
--min-version 2.0.1 blitframebuffer-stencil-only.html
blitframebuffer-test.html
--min-version 2.0.1 blitframebuffer-unaffected-by-colormask.html
---min-version 2.0.1 builtin-vert-attribs.html
canvas-resizing-with-pbo-bound.html
--min-version 2.0.1 clearbuffer-sub-source.html
--min-version 2.0.1 clearbufferfv-with-alpha-false.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html b/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html
deleted file mode 100644
index cc64c9034b..0000000000
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html
+++ /dev/null
@@ -1,408 +0,0 @@
-<!--
-Copyright (c) 2022 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.
--->
-
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset=utf-8>
-<link rel=stylesheet href="../../resources/js-test-style.css"/>
-<script src="../../js/js-test-pre.js"></script>
-<script src="../../js/webgl-test-utils.js"></script>
-</head>
-<body>
-<canvas id=e_canvas width=1 height=1 style="width: 100px; height: 100px;"></canvas>
-<div id=description></div>
-<div id=console></div>
-<script>
-"use strict";
-description('gl_VertexID and gl_InstanceID should behave as per spec.');
-
-//
-
-/*
-So what are gl_VertexID and gl_InstanceID supposed to do?
-In ES 3.0 and GL 4.1 (Core), this is all we get:
-
-# ES 3.0
-
-> (p78) gl_VertexID holds the integer index i implicitly passed by DrawArrays or
-> one of the other drawing commands defined in section 2.9.3. The value of
-> gl_VertexID is defined if and only if all enabled vertex arrays have non-zero buffer
-> object bindings.
-> gl_InstanceID holds the integer instance number of the current primitive in
-> an instanced draw call (see section 2.9.3).
-
-
-# GL 4.1 (Core)
-
-> (p102) gl_VertexID holds the integer index i implicitly passed by DrawArrays or
-> one of the other drawing commands defined in section 2.8.3.
-> gl_InstanceID holds the integer index of the current primitive in an
-> instanced draw call (see section 2.8.3).
-
-
-# ES 3.1
-
-ES 3.1 retains the wording from ES 3.0, but adds the following clarifications:
-
-gl_VertexID:
-> (p252) The index of any element transferred to the GL by DrawArraysOneInstance
-> is referred to as its vertex ID, and may be read by a vertex shader as gl_VertexID.
-> The vertex ID of the ith element transferred is first + i.
-
-> (p254) The index of any element transferred to the GL by
-> DrawElementsOneInstance is referred to as its vertex ID, and may be read by a vertex shader as
-> gl_VertexID. If no element array buffer is bound, the vertex ID of the ith element
-> transferred is indices[i] + basevertex. Otherwise, the vertex ID of the ith
-> element transferred is the sum of basevertex and the value stored in the currently
-> bound element array buffer at offset indices + i.
-
-gl_InstanceID
-> (p255) If an enabled vertex attribute array is instanced (it has a non-zero divisor as
-> specified by VertexAttribDivisor), the element index that is transferred to the GL,
-> for all vertices, is given by
-> `floor(instance / divisor) + baseinstance`
-
-
-# Errata
-
-Drivers generally do implement the ES 3.1 behavior.
-A notable exception is Mac's legacy GL (4.1) driver which has two bugs here.
-(Both ANGLE-on-Metal and the system M1+ GL driver seem correct though)
-
-## gl_InstanceID random for DrawArrays calls
-Use ERRATA.IGNORE_GL_INSTANCE_ID to cause these tests to pass.
-
-## Adds `first` to user-attrib instanced fetch ids in DrawArrays calls.
-Use ERRATA.FIRST_ADDS_TO_INSTANCE to cause these tests to pass.
-*/
-
-const wtu = WebGLTestUtils;
-const gl = wtu.create3DContext('e_canvas');
-
-const ERRATA = {};
-//ERRATA.IGNORE_GL_INSTANCE_ID = true; // Chrome on ANGLE-on-Mac-GL needs this.
-//ERRATA.FIRST_ADDS_TO_INSTANCE = true; // Firefox with MOZ_WEBGL_WORKAROUND_FIRST_AFFECTS_INSTANCE_ID=0 would need this.
-
-debug(`ERRATA: ${JSON.stringify(ERRATA)}`);
-
-function make_vs_point(vid, iid) {
- return `\
- #version 300 es
-
- ${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name};
- ${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name};
- out vec4 v_color;
-
- void main() {
- gl_PointSize = 1.0;
- gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
- v_color = vec4(1.0, float(${vid.name}) / 255.0, float(${iid.name}) / 255.0, 1.0);
-#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0}
- v_color.b = 0.0;
-#endif
- }`;
-}
-
-function make_vs_tri(vid, iid) {
- return `\
- #version 300 es
-
- ${vid.name == 'gl_VertexID' ? '// ' : ''}layout(location=${vid.loc}) in highp int ${vid.name};
- ${iid.name == 'gl_InstanceID' ? '// ' :''}layout(location=${iid.loc}) in highp int ${iid.name};
- out vec4 v_color;
-
- void main() {
- int prim_vert_id = ${vid.name} % 3;
- int flat_vert_id = ${vid.name} - prim_vert_id + 2;
- gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
- gl_Position.x = (prim_vert_id == 1) ? 2.0 : -1.0;
- gl_Position.y = (prim_vert_id == 2) ? 2.0 : -1.0;
- v_color = vec4(1.0, float(flat_vert_id) / 255.0, float(${iid.name}) / 255.0, 1.0);
-#if ${(iid.name == 'gl_InstanceID' && ERRATA.IGNORE_GL_INSTANCE_ID)|0}
- v_color.b = 0.0;
-#endif
- }`;
-}
-
-const FS = `\
- #version 300 es
- precision mediump float;
-
- in vec4 v_color;
- out vec4 o_color;
-
- void main() {
- o_color = v_color;
- }
-`;
-
-
-function crossCombine(...args) {
- function crossCombine2(listA, listB) {
- const listC = [];
- for (const a of listA) {
- for (const b of listB) {
- const c = Object.assign({}, a, b);
- listC.push(c);
- }
- }
- return listC;
- }
-
- let res = [{}];
- while (args.length) {
- const next = args.shift();
- next[0].defined;
- res = crossCombine2(res, next);
- }
- return res;
-}
-
-/// makeCombiner('foo', [5, 3]) -> [{foo: 5}, {foo: 3}]
-function makeCombiner(key, vals) {
- const ret = [];
- for (const val of vals) {
- const cur = {};
- cur[key] = val;
- ret.push(cur);
- }
- return ret;
-}
-
-debug('Draw a point with a shader that takes no attributes and verify it fills the whole canvas.');
-
-
-let TESTS = [
- makeCombiner('vid', [
- {name: 'a_VertexID', loc:0},
- {name: 'a_VertexID', loc:2}, // Test 2, so that we're not only testing 0.
- {name: 'gl_VertexID', loc:-1},
- {name: 'gl_VertexID', loc:0}, // Enable a vertex array, despite not using it.
- {name: 'gl_VertexID', loc:2}, // Enable a vertex array, despite not using it.
- ]),
- makeCombiner('iid', [
- {name: 'a_InstanceID', loc:1},
- {name: 'gl_InstanceID', loc:-1},
- {name: 'gl_InstanceID', loc:1}, // Enable a vertex array, despite not using it.
- ]),
- makeCombiner('separate_vbufs', [true, false]),
-];
-//console.log('a', {TESTS});
-TESTS = crossCombine(...TESTS);
-//console.log('b', {TESTS});
-
-
-let vdata = new Int32Array(1000);
-vdata = vdata.map((v,i) => i);
-const vbuf = gl.createBuffer();
-gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
-gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW);
-
-
-const vbuf2 = gl.createBuffer();
-gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2);
-gl.bufferData(gl.ARRAY_BUFFER, vdata, gl.STATIC_DRAW);
-
-
-let index_data = new Uint32Array(1000);
-index_data = index_data.map((x,i) => 10+i);
-const index_buffer = gl.createBuffer();
-gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
-gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_data, gl.STATIC_DRAW);
-
-
-gl.disable(gl.DEPTH_TEST);
-
-(async () => {
- for (const desc of TESTS) {
- await wtu.dispatchPromise(); // Yield, for responsiveness.
- debug('');
- debug('---------------------');
- debug(`desc: ${JSON.stringify(desc)}`);
-
- let fn = (vs) => {
- //console.log({vs});
- const prog = wtu.setupProgram(gl, [vs, FS]);
-
- {
- const WEBGL_debug_shaders = gl.getExtension('WEBGL_debug_shaders');
- let i = -1;
- for (const s of gl.getAttachedShaders(prog)) {
- i += 1;
- debug('');
- debug(`shader[${i}] getShaderSource() -> `);
- debug(gl.getShaderSource(s));
- if (WEBGL_debug_shaders) {
- debug(`shader[${i}] getTranslatedShaderSource() -> `);
- debug(WEBGL_debug_shaders.getTranslatedShaderSource(s));
- }
- }
- }
- return prog;
- };
- const point_prog = fn(make_vs_point(desc.vid, desc.iid));
- const tri_prog = fn(make_vs_tri(desc.vid, desc.iid));
-
- // -
-
- gl.bindBuffer(gl.ARRAY_BUFFER, null);
- for (let i = 0; i <= 2; i++) {
- gl.disableVertexAttribArray(i);
- gl.vertexAttribPointer(i, 4, gl.FLOAT, false, 0, 0);
- gl.vertexAttribDivisor(i, 0);
- }
-
- gl.bindBuffer(gl.ARRAY_BUFFER, vbuf);
- let loc = desc.vid.loc;
- if (loc != -1) {
- gl.enableVertexAttribArray(loc);
- gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0);
- };
-
- if (desc.separate_vbufs) {
- gl.bindBuffer(gl.ARRAY_BUFFER, vbuf2);
- }
- loc = desc.iid.loc;
- if (loc != -1) {
- gl.enableVertexAttribArray(loc);
- gl.vertexAttribIPointer(loc, 1, gl.INT, 0, 0);
- gl.vertexAttribDivisor(loc, 1);
- };
-
- {
- const err = gl.getError();
- if (err) throw err; // Broken init.
- }
-
- // -
-
- fn = (eval_str, expected_arr) => {
- if (ERRATA.IGNORE_GL_INSTANCE_ID) {
- if (desc.iid.name == 'gl_InstanceID') {
- expected_arr = expected_arr.map(x => x);
- expected_arr[2] = 0;
- }
- }
-
- debug('');
- //debug(`${eval_str} -> [${expected_arr.join(', ')}]`);
- eval(eval_str);
-
- const err = gl.getError();
- if (err) throw err; // Broken subtest.
-
- wtu.checkCanvas(gl, expected_arr, eval_str);
- }
-
- gl.useProgram(point_prog);
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawArrays(gl.POINTS, 0, 0)`, [0, 0, 0, 0]);
- fn(`gl.drawArrays(gl.POINTS, 0, 1)`, [255, 0, 0, 255]);
- fn(`gl.drawArrays(gl.POINTS, 0, 2)`, [255, 1, 0, 255]);
- if (ERRATA.FIRST_ADDS_TO_INSTANCE) {
- fn(`gl.drawArrays(gl.POINTS, 100, 2)`, [255, 100+2-1, 100, 255]);
- } else {
- fn(`gl.drawArrays(gl.POINTS, 100, 2)`, [255, 100+2-1, 0, 255]);
- }
- fn(`gl.drawArrays(gl.POINTS, 0, 255)`, [255, 254, 0, 255]);
- fn(`gl.drawArrays(gl.POINTS, 0, 256)`, [255, 255, 0, 255]);
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawArraysInstanced(gl.POINTS, 0, 0, 1)`, [0, 0, 0, 0]);
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 0)`, [0, 0, 0, 0]);
-
- fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 1)`, [255, 0, 0, 255]);
- fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 1)`, [255, 1, 0, 255]);
- fn(`gl.drawArraysInstanced(gl.POINTS, 0, 1, 2)`, [255, 0, 1, 255]);
- fn(`gl.drawArraysInstanced(gl.POINTS, 0, 2, 2)`, [255, 1, 1, 255]);
- if (ERRATA.FIRST_ADDS_TO_INSTANCE) {
- fn(`gl.drawArraysInstanced(gl.POINTS, 100, 2, 2)`, [255, 100+2-1, 101, 255]);
- } else {
- fn(`gl.drawArraysInstanced(gl.POINTS, 100, 2, 2)`, [255, 100+2-1, 1, 255]);
- }
- fn(`gl.drawArraysInstanced(gl.POINTS, 0, 255, 255)`, [255, 254, 254, 255]);
-
- // -
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawElements(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]);
- fn(`gl.drawElements(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0)`, [255, 10+0, 0, 255]);
- fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0)`, [255, 10+1, 0, 255]);
- fn(`gl.drawElements(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+1, 0, 255]);
- fn(`gl.drawElements(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0)`, [255, 10+244, 0, 255]);
- fn(`gl.drawElements(gl.POINTS, 246, gl.UNSIGNED_INT, 4*0)`, [255, 10+245, 0, 255]);
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawElementsInstanced(gl.POINTS, 0, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]);
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]);
-
- fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+0, 0, 255]);
- fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1, 0, 255]);
- fn(`gl.drawElementsInstanced(gl.POINTS, 1, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+0, 1, 255]);
- fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1, 1, 255]);
- fn(`gl.drawElementsInstanced(gl.POINTS, 2, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+1, 1, 255]);
- fn(`gl.drawElementsInstanced(gl.POINTS, 245, gl.UNSIGNED_INT, 4*0, 255)`, [255, 10+244, 254, 255]);
-
- // -
-
- gl.useProgram(tri_prog);
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawArrays(gl.TRIANGLES, 0, 0*3)`, [0, 0, 0, 0]);
- fn(`gl.drawArrays(gl.TRIANGLES, 0, 1*3)`, [255, 1*3-1, 0, 255]);
- fn(`gl.drawArrays(gl.TRIANGLES, 0, 2*3)`, [255, 2*3-1, 0, 255]);
- if (ERRATA.FIRST_ADDS_TO_INSTANCE) {
- fn(`gl.drawArrays(gl.TRIANGLES, 90, 2*3)`, [255, 90+2*3-1, 90, 255]);
- } else {
- fn(`gl.drawArrays(gl.TRIANGLES, 90, 2*3)`, [255, 90+2*3-1, 0, 255]);
- }
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 0, 1)`, [0, 0, 0, 0]);
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 0)`, [0, 0, 0, 0]);
-
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 1)`, [255, 1*3-1, 0, 255]);
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 1)`, [255, 2*3-1, 0, 255]);
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 1*3, 2)`, [255, 1*3-1, 1, 255]);
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 0, 2*3, 2)`, [255, 2*3-1, 1, 255]);
- if (ERRATA.FIRST_ADDS_TO_INSTANCE) {
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 90, 2*3, 2)`, [255, 90+2*3-1, 91, 255]);
- } else {
- fn(`gl.drawArraysInstanced(gl.TRIANGLES, 90, 2*3, 2)`, [255, 90+2*3-1, 1, 255]);
- }
-
- // -
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawElements(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0)`, [0, 0, 0, 0]);
- fn(`gl.drawElements(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+1*3-1, 0, 255]);
- fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0)`, [255, 10+2*3-1, 0, 255]);
- fn(`gl.drawElements(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100)`, [255, 100+10+2*3-1, 0, 255]);
-
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawElementsInstanced(gl.TRIANGLES, 0*3, gl.UNSIGNED_INT, 4*0, 1)`, [0, 0, 0, 0]);
- gl.clear(gl.COLOR_BUFFER_BIT);
- fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 0)`, [0, 0, 0, 0]);
-
- fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+1*3-1, 0, 255]);
- fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 1)`, [255, 10+2*3-1, 0, 255]);
- fn(`gl.drawElementsInstanced(gl.TRIANGLES, 1*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+1*3-1, 1, 255]);
- fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*0, 2)`, [255, 10+2*3-1, 1, 255]);
- fn(`gl.drawElementsInstanced(gl.TRIANGLES, 2*3, gl.UNSIGNED_INT, 4*100, 2)`, [255, 100+10+2*3-1, 1, 255]);
- }
-
- finishTest();
-})();
-
-var successfullyParsed = true;
-</script>
-</body>
-</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/00_test_list.txt
index 17f8312e58..3639c10547 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/00_test_list.txt
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/00_test_list.txt
@@ -20,6 +20,7 @@ mipmap-fbo.html
--min-version 2.0.1 origin-clean-conformance-offscreencanvas.html
tex-3d-size-limit.html
--min-version 2.0.1 tex-base-level-bug.html
+--min-version 2.0.1 tex-image-10bpc.html
tex-image-and-sub-image-with-array-buffer-view-sub-source.html
tex-image-with-bad-args.html
tex-image-with-bad-args-from-dom-elements.html
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-10bpc.html b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-10bpc.html
new file mode 100644
index 0000000000..973a0ec0f5
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-10bpc.html
@@ -0,0 +1,66 @@
+<!--
+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.
+-->
+
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>Ensure 10bpc image is not crushed to 8bpc in texImage2D</title>
+<link rel="stylesheet" href="../../../resources/js-test-style.css" />
+<script src="../../../js/js-test-pre.js"></script>
+<script src="../../../js/webgl-test-utils.js"></script>
+</head>
+<body>
+<canvas id="example" width="24" height="24"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("example", undefined, 2);
+var uniquePixels;
+
+// This is an 8x1, 10-bit-per-channel PNG (encoded as 16bpc).
+// The first pixel is black, and each next pixel is one brighter; approximately:
+// (0/1023,0,0), (1/1023,0,0), (2/1023,0,0), ..., (7/1023,0,0)
+const imgW = 8, imgH = 1;
+const imgURL = "../../../resources/red-gradient-8x1-10bit-untagged.png";
+
+const img = document.createElement("img");
+img.onload = () => {
+ const tex = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex);
+ const level = 0;
+ const internalformat = gl.RGB10_A2;
+ const border = 0;
+ const format = gl.RGBA;
+ const type = gl.UNSIGNED_INT_2_10_10_10_REV;
+ gl.texImage2D(gl.TEXTURE_2D, level, internalformat, imgW, imgH, border, format, type, img);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
+
+ shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
+
+ const pixels = new Uint32Array(imgW * imgH);
+ gl.readPixels(0, 0, imgW, imgH, format, type, pixels);
+ uniquePixels = new Set(pixels);
+ // If the image was crushed to 8bpc, there will be 2-3 distinct values:
+ // (0/255,0,0), (1/255,0,0), and maybe (2/255,0,0) (depending on truncation vs rounding).
+ // If it wasn't, there should be 7-8.
+ // At time of writing, on Mac M1, Chrome gets 2 if it's crushed, and 7 if it's not.
+ shouldBeGreaterThanOrEqual("uniquePixels.size", "7", "there should be at least 7 distinct color values");
+
+ finishTest();
+};
+img.src = imgURL;
+
+var successfullyParsed = true;
+</script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html
index f1616e81d7..34ece05699 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-with-bad-args-from-dom-elements.html
@@ -106,7 +106,6 @@ var tests = [
{ type: "video", src: "../../../resources/red-green.mp4", videoType: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', run: testVideo },
{ type: "video", src: "../../../resources/red-green.bt601.vp9.webm", videoType: 'video/webm; codecs="vp9"', run: testVideo },
{ type: "video", src: "../../../resources/red-green.webmvp8.webm", videoType: 'video/webm; codecs="vp8, vorbis"', run: testVideo },
- { type: "video", src: "../../../resources/red-green.theora.ogv", videoType: 'video/ogg; codecs="theora, vorbis"', run: testVideo },
];
var testIndex = 0;
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-new-formats.html b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-new-formats.html
index df10edb4d3..760bc6bd19 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-new-formats.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-new-formats.html
@@ -37,6 +37,7 @@ if (!gl) {
testPassed("WebGL context exists");
runTexFormatsTest();
+ runDepthStencilFormatTest();
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
}
@@ -556,6 +557,35 @@ function runTexFormatsTest()
});
}
+function runDepthStencilFormatTest() {
+ debug("");
+ debug("Testing FLOAT_32_UNSIGNED_INT_24_8_REV with data");
+ const fb = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
+
+ const tex2D = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D, tex2D);
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH32F_STENCIL8, 1, 1, 0, gl.DEPTH_STENCIL, gl.FLOAT_32_UNSIGNED_INT_24_8_REV, new Uint8Array(8));
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.TEXTURE_2D, tex2D, 0);
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
+ testFailed("2D texture with invalid type was created");
+ } else {
+ testPassed("2D texture with invalid type was not created")
+ }
+
+ const tex3D = gl.createTexture();
+ gl.bindTexture(gl.TEXTURE_2D_ARRAY, tex3D);
+ gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, gl.DEPTH32F_STENCIL8, 1, 1, 1, 0, gl.DEPTH_STENCIL, gl.FLOAT_32_UNSIGNED_INT_24_8_REV, new Uint8Array(8));
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION);
+ gl.framebufferTextureLayer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, tex3D, 0, 0);
+ if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
+ testFailed("2D array texture with invalid type was created");
+ } else {
+ testPassed("2D array texture with invalid type was not created")
+ }
+}
+
debug("");
var successfullyParsed = true;
</script>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html
index 228b4ab5cf..11d1eaa829 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/simultaneous_binding.html
@@ -304,6 +304,17 @@ for (let genericBindPointValue of genericBindPointValues) {
gl.copyBufferSubData(gl.COPY_READ_BUFFER, gl.COPY_WRITE_BUFFER, 0, 0, 1);
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "copyBufferSubData with double bound buffer");
+ debug("<hr/>Test that rejected operations do not change the bound buffer size");
+
+ gl.bindBuffer(gl.ARRAY_BUFFER, tfBuffer);
+ gl.bufferData(gl.ARRAY_BUFFER, 8, gl.STATIC_DRAW);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "bufferData with double bound buffer");
+
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
+ gl.bufferSubData(gl.ARRAY_BUFFER, 0, new Uint8Array(16));
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "bufferSubData should succeed");
+ gl.bindBuffer(gl.ARRAY_BUFFER, null);
+
debug("<hr/>Test bufferData family with tf object unbound");
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null);
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html
index 20256c6ace..16855453f0 100644
--- a/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/transform_feedback/transform_feedback.html
@@ -64,6 +64,8 @@ if (!gl) {
runUnboundDeleteTest();
runBoundDeleteTest();
runOneOutputFeedbackTest();
+ runUnchangedBufferBindingsTest();
+ runNoOutputsTest();
// Must be the last test, since it's asynchronous and calls finishTest().
runTwoOutputFeedbackTest();
}
@@ -638,6 +640,70 @@ function runContextLostOneOutputFeedbackTest() {
finishTest();
}
+function runUnchangedBufferBindingsTest() {
+ debug("");
+ debug("Testing that buffer bindings cannot be changed while transform feedback is active");
+
+ program = wtu.setupTransformFeedbackProgram(
+ gl, [wtu.simpleVertexShader, wtu.simpleColorFragmentShader], ['gl_Position'], gl.INTERLEAVED_ATTRIBS);
+
+ tf = gl.createTransformFeedback();
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+
+ buf = gl.createBuffer();
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buf);
+
+ gl.beginTransformFeedback(gl.POINTS);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Transform feedback is active");
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, gl.createBuffer());
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Cannot change the bound buffer while transform feedback is active");
+ shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buf");
+
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buf);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Cannot rebind the same buffer while transform feedback is active");
+
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, gl.createBuffer(), 0, 64);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Cannot change the bound buffer while transform feedback is active");
+ shouldBe("gl.getIndexedParameter(gl.TRANSFORM_FEEDBACK_BUFFER_BINDING, 0)", "buf");
+
+ gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buf, 0, 64);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "Cannot rebind the same buffer while transform feedback is active");
+
+ gl.endTransformFeedback();
+ gl.deleteTransformFeedback(tf);
+ gl.deleteBuffer(buf);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No extra errors after the test");
+}
+
+function runNoOutputsTest() {
+ debug("");
+ debug("Testing transform feedback with no varyings to record");
+
+ tf = gl.createTransformFeedback();
+ gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
+
+ buf = gl.createBuffer();
+ gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0, buf);
+
+ for (const mode of ['SEPARATE_ATTRIBS', 'INTERLEAVED_ATTRIBS']) {
+ program = wtu.setupTransformFeedbackProgram(
+ gl, [wtu.simpleVertexShader, wtu.simpleColorFragmentShader], [], gl[mode]);
+
+ debug(`Testing with ${mode}`);
+ gl.beginTransformFeedback(gl.POINTS);
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "beginTransformFeedback: No varyings to record");
+ gl.useProgram(null);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "useProgram: Transform feedback is not active");
+ gl.endTransformFeedback();
+ wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, "endTransformFeedback: Transform feedback is not active");
+ }
+
+ gl.deleteTransformFeedback(tf);
+ gl.deleteBuffer(buf);
+ wtu.glErrorShouldBe(gl, gl.NO_ERROR, "No extra errors after the test");
+}
+
debug("");
</script>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/00_test_list.txt b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/00_test_list.txt
new file mode 100644
index 0000000000..d188fc30a6
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/00_test_list.txt
@@ -0,0 +1,12 @@
+--min-version 2.0.1 readpixels-16gb-wasm-memory.html
+--min-version 2.0.1 readpixels-4gb-wasm-memory.html
+--min-version 2.0.1 teximage2d-16gb-wasm-memory.html
+--min-version 2.0.1 teximage2d-4gb-wasm-memory.html
+--min-version 2.0.1 texsubimage2d-16gb-wasm-memory.html
+--min-version 2.0.1 texsubimage2d-4gb-wasm-memory.html
+--min-version 2.0.1 bufferdata-16gb-wasm-memory.html
+--min-version 2.0.1 bufferdata-4gb-wasm-memory.html
+--min-version 2.0.1 buffersubdata-16gb-wasm-memory.html
+--min-version 2.0.1 buffersubdata-4gb-wasm-memory.html
+--min-version 2.0.1 getbuffersubdata-16gb-wasm-memory.html
+--min-version 2.0.1 getbuffersubdata-4gb-wasm-memory.html \ No newline at end of file
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-16gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-16gb-wasm-memory.html
new file mode 100644
index 0000000000..0dd21bf64f
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-16gb-wasm-memory.html
@@ -0,0 +1,51 @@
+<!--
+Copyright (c) 2024 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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>bufferData test to Wasm Memory 16GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that bufferData can be called on WebAssembly Memory of 16GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 16 * 1024 * 1024 * 1024;
+let view = new Uint8Array(new WebAssembly.Memory({ index: 'i64', initial: SIZE / PAGE }).buffer);
+let expectedData = new Uint8Array([1, 2, 3, 4]);
+const length = expectedData.length;
+const offset = SIZE - length;
+view.set(expectedData, offset);
+
+let buf = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, offset, length);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+let actualData = new Uint8Array(length);
+gl.getBufferSubData(gl.ARRAY_BUFFER, 0, actualData);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+for (let i = 0; i < length; i++) {
+ shouldBe(`actualData[${i}]`, `expectedData[${i}]`);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-4gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-4gb-wasm-memory.html
new file mode 100644
index 0000000000..2296c3a5ea
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-4gb-wasm-memory.html
@@ -0,0 +1,51 @@
+<!--
+Copyright (c) 2024 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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>bufferData test to Wasm Memory 4GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that bufferData can be called on WebAssembly Memory of 4GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 4 * 1024 * 1024 * 1024 - PAGE;
+let view = new Uint8Array(new WebAssembly.Memory({ initial: SIZE / PAGE }).buffer);
+let expectedData = new Uint8Array([1, 2, 3, 4]);
+const length = expectedData.length;
+const offset = SIZE - length;
+view.set(expectedData, offset);
+
+let buf = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+gl.bufferData(gl.ARRAY_BUFFER, view, gl.STATIC_DRAW, offset, length);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+let actualData = new Uint8Array(length);
+gl.getBufferSubData(gl.ARRAY_BUFFER, 0, actualData);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+for (let i = 0; i < length; i++) {
+ shouldBe(`actualData[${i}]`, `expectedData[${i}]`);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-16gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-16gb-wasm-memory.html
new file mode 100644
index 0000000000..08d6d1df50
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-16gb-wasm-memory.html
@@ -0,0 +1,52 @@
+<!--
+Copyright (c) 2024 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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>bufferSubData test to Wasm Memory 16GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that bufferSubData can be called on WebAssembly Memory of 16GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 16 * 1024 * 1024 * 1024;
+let view = new Uint8Array(new WebAssembly.Memory({ index: 'i64', initial: SIZE / PAGE }).buffer);
+let expectedData = new Uint8Array([1, 2]);
+const length = expectedData.length;
+let srcOffset = SIZE - length;
+view.set(expectedData, srcOffset);
+const dstByteOffset = 4;
+
+let buf = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+gl.bufferData(gl.ARRAY_BUFFER, 8, gl.STATIC_DRAW);
+gl.bufferSubData(gl.ARRAY_BUFFER, dstByteOffset, view, srcOffset, length);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+let actualData = new Uint8Array(length);
+gl.getBufferSubData(gl.ARRAY_BUFFER, dstByteOffset, actualData);
+for (let i = 0; i < length; i++) {
+ shouldBe(`actualData[${i}]`, `expectedData[${i}]`);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-4gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-4gb-wasm-memory.html
new file mode 100644
index 0000000000..2834a6901b
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-4gb-wasm-memory.html
@@ -0,0 +1,52 @@
+<!--
+Copyright (c) 2024 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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>bufferSubData test to Wasm Memory 4GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that bufferSubData can be called on WebAssembly Memory of 4GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 4 * 1024 * 1024 * 1024 - PAGE;
+let view = new Uint8Array(new WebAssembly.Memory({ initial: SIZE / PAGE }).buffer);
+let expectedData = new Uint8Array([1, 2]);
+const length = expectedData.length;
+let srcOffset = SIZE - length;
+view.set(expectedData, srcOffset);
+const dstByteOffset = 4;
+
+let buf = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+gl.bufferData(gl.ARRAY_BUFFER, 8, gl.STATIC_DRAW);
+gl.bufferSubData(gl.ARRAY_BUFFER, dstByteOffset, view, srcOffset, length);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+let actualData = new Uint8Array(length);
+gl.getBufferSubData(gl.ARRAY_BUFFER, dstByteOffset, actualData);
+for (let i = 0; i < length; i++) {
+ shouldBe(`actualData[${i}]`, `expectedData[${i}]`);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html
new file mode 100644
index 0000000000..8390957b02
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html
@@ -0,0 +1,48 @@
+<!--
+Copyright (c) 2024 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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>getBufferSubData test to Wasm Memory 16GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that getBufferSubData can be called on WebAssembly Memory of 16GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 16 * 1024 * 1024 * 1024;
+let view = new Uint8Array(new WebAssembly.Memory({ index: 'i64', initial: SIZE / PAGE }).buffer);
+let expectedData = new Uint8Array([1, 2, 3, 4]);
+
+let buf = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+gl.bufferData(gl.ARRAY_BUFFER, expectedData, gl.STATIC_DRAW);
+
+const length = expectedData.length;
+const offset = SIZE - length;
+gl.getBufferSubData(gl.ARRAY_BUFFER, 0, view, offset, length);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+for (let i = 0; i < length; i++) {
+ shouldBe(`view[${i + offset}]`, `expectedData[${i}]`);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-4gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-4gb-wasm-memory.html
new file mode 100644
index 0000000000..09a336b753
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-4gb-wasm-memory.html
@@ -0,0 +1,48 @@
+<!--
+Copyright (c) 2024 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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>getBufferSubData test to Wasm Memory 4GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that getBufferSubData can be called on WebAssembly Memory of 4GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 4 * 1024 * 1024 * 1024 - PAGE;
+let view = new Uint8Array(new WebAssembly.Memory({ initial: SIZE / PAGE }).buffer);
+let expectedData = new Uint8Array([1, 2, 3, 4]);
+
+let buf = gl.createBuffer();
+gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+gl.bufferData(gl.ARRAY_BUFFER, expectedData, gl.STATIC_DRAW);
+
+const length = expectedData.length;
+const offset = SIZE - length;
+gl.getBufferSubData(gl.ARRAY_BUFFER, 0, view, offset, length);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+for (let i = 0; i < length; i++) {
+ shouldBe(`view[${i + offset}]`, `expectedData[${i}]`);
+}
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-16gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-16gb-wasm-memory.html
new file mode 100644
index 0000000000..af74678dda
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-16gb-wasm-memory.html
@@ -0,0 +1,51 @@
+<!--
+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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>gl.readPixels() test to Wasm Memory 16GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("");
+debug("Tests that gl.readPixels() can be called on WebAssembly Memory of 16GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 16*1024*1024*1024;
+let view = new Uint8Array(new WebAssembly.Memory({ index: 'i64', initial: SIZE/PAGE }).buffer);
+
+// Clear the canvas to a specific color
+const expectedColor = [42, 84, 128, 255];
+gl.clearColor(expectedColor[0]/255.0, expectedColor[1]/255.0, expectedColor[2]/255.0, expectedColor[3]/255.0);
+gl.clear(gl.COLOR_BUFFER_BIT);
+
+// Test that gl.readPixels() can be called with a high offset to Memory
+const offset = SIZE - 4;
+view.set([0,0,0,0], offset); // For good measure, clear data at offset before reading
+gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, view, offset);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+let obtainedColor = view.subarray(offset, offset+4);
+shouldBe('obtainedColor[0]', 'expectedColor[0]');
+shouldBe('obtainedColor[1]', 'expectedColor[1]');
+shouldBe('obtainedColor[2]', 'expectedColor[2]');
+shouldBe('obtainedColor[3]', 'expectedColor[3]');
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-4gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-4gb-wasm-memory.html
new file mode 100644
index 0000000000..f97a3ccba0
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-4gb-wasm-memory.html
@@ -0,0 +1,50 @@
+<!--
+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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>gl.readPixels() test to Wasm Memory 4GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that gl.readPixels() can be called on WebAssembly Memory of 4GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 4*1024*1024*1024 - PAGE; // when uint32_t size is max, we can only reach one page short of full 4GB
+let view = new Uint8Array(new WebAssembly.Memory({ initial: SIZE/PAGE }).buffer);
+
+// Clear the canvas to a specific color
+const expectedColor = [42, 84, 128, 255];
+gl.clearColor(expectedColor[0]/255.0, expectedColor[1]/255.0, expectedColor[2]/255.0, expectedColor[3]/255.0);
+gl.clear(gl.COLOR_BUFFER_BIT);
+
+// Test that gl.readPixels() can be called with a high offset to Memory
+const offset = SIZE - 4;
+view.set([0,0,0,0], offset); // For good measure, clear data at offset before reading
+gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, view, offset);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+let obtainedColor = view.subarray(offset, offset+4);
+shouldBe('obtainedColor[0]', 'expectedColor[0]');
+shouldBe('obtainedColor[1]', 'expectedColor[1]');
+shouldBe('obtainedColor[2]', 'expectedColor[2]');
+shouldBe('obtainedColor[3]', 'expectedColor[3]');
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-16gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-16gb-wasm-memory.html
new file mode 100644
index 0000000000..8ce5111953
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-16gb-wasm-memory.html
@@ -0,0 +1,85 @@
+<!--
+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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>gl.texImage2D() test to Wasm Memory 16GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that gl.texImage2D() can be called on WebAssembly Memory 16GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 16*1024*1024*1024;
+let view = new Uint8Array(new WebAssembly.Memory({ index: 'i64', initial: SIZE/PAGE }).buffer);
+
+function compileShader(type, src) {
+ let shader = gl.createShader(type);
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
+ let log = gl.getShaderInfoLog(shader);
+ if (log) debug(log);
+ return shader;
+}
+
+function createProgram(vs, fs) {
+ let program = gl.createProgram();
+ gl.attachShader(program, vs);
+ gl.attachShader(program, fs);
+ gl.bindAttribLocation(program, 0, 'pos');
+ gl.linkProgram(program);
+ gl.useProgram(program);
+ return program;
+}
+
+let program = createProgram(
+ compileShader(gl.VERTEX_SHADER, `
+ varying vec2 uv;
+ attribute vec2 pos;
+ void main() { uv = pos; gl_Position = vec4(pos*2.0-vec2(1.0,1.0),0,1); }`),
+ compileShader(gl.FRAGMENT_SHADER, `
+ precision lowp float;
+ uniform sampler2D tex;
+ varying vec2 uv;
+ void main() { gl_FragColor = texture2D(tex,uv); }`));
+
+gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), gl.STATIC_DRAW);
+gl.vertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 0, 0);
+gl.enableVertexAttribArray(0);
+
+let texture = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, texture);
+
+// Test uploading an image
+const expectedColor = [42, 84, 128, 255];
+const offset = SIZE - 4;
+view.set(expectedColor, offset);
+gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, view, offset);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+// Test rendering with that image
+gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+// Verify that we rendered what we expected
+wtu.checkCanvasRect(gl, 0, 0, 1, 1, expectedColor, "texImage2D produced expected color");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-4gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-4gb-wasm-memory.html
new file mode 100644
index 0000000000..5d6897347d
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-4gb-wasm-memory.html
@@ -0,0 +1,85 @@
+<!--
+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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>gl.texImage2D() test to Wasm Memory 4GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that gl.texImage2D() can be called on WebAssembly Memory 4GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 4*1024*1024*1024 - PAGE; // when uint32_t size is max, we can only reach one page short of full 4GB
+let view = new Uint8Array(new WebAssembly.Memory({ initial: SIZE/PAGE }).buffer);
+
+function compileShader(type, src) {
+ let shader = gl.createShader(type);
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
+ let log = gl.getShaderInfoLog(shader);
+ if (log) debug(log);
+ return shader;
+}
+
+function createProgram(vs, fs) {
+ let program = gl.createProgram();
+ gl.attachShader(program, vs);
+ gl.attachShader(program, fs);
+ gl.bindAttribLocation(program, 0, 'pos');
+ gl.linkProgram(program);
+ gl.useProgram(program);
+ return program;
+}
+
+let program = createProgram(
+ compileShader(gl.VERTEX_SHADER, `
+ varying vec2 uv;
+ attribute vec2 pos;
+ void main() { uv = pos; gl_Position = vec4(pos*2.0-vec2(1.0,1.0),0,1); }`),
+ compileShader(gl.FRAGMENT_SHADER, `
+ precision lowp float;
+ uniform sampler2D tex;
+ varying vec2 uv;
+ void main() { gl_FragColor = texture2D(tex,uv); }`));
+
+gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), gl.STATIC_DRAW);
+gl.vertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 0, 0);
+gl.enableVertexAttribArray(0);
+
+let texture = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, texture);
+
+// Test uploading an image
+const expectedColor = [42, 84, 128, 255];
+const offset = SIZE - 4;
+view.set(expectedColor, offset);
+gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, view, offset);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+// Test rendering with that image
+gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+// Verify that we rendered what we expected
+wtu.checkCanvasRect(gl, 0, 0, 1, 1, expectedColor, "texImage2D produced expected color");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-16gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-16gb-wasm-memory.html
new file mode 100644
index 0000000000..328d42ec49
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-16gb-wasm-memory.html
@@ -0,0 +1,86 @@
+<!--
+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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>gl.texSubImage2D() test to Wasm Memory 16GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that gl.texSubImage2D() can be called on WebAssembly Memory 16GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 16*1024*1024*1024;
+let view = new Uint8Array(new WebAssembly.Memory({ index: 'i64', initial: SIZE/PAGE }).buffer);
+
+function compileShader(type, src) {
+ let shader = gl.createShader(type);
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
+ let log = gl.getShaderInfoLog(shader);
+ if (log) debug(log);
+ return shader;
+}
+
+function createProgram(vs, fs) {
+ let program = gl.createProgram();
+ gl.attachShader(program, vs);
+ gl.attachShader(program, fs);
+ gl.bindAttribLocation(program, 0, 'pos');
+ gl.linkProgram(program);
+ gl.useProgram(program);
+ return program;
+}
+
+let program = createProgram(
+ compileShader(gl.VERTEX_SHADER, `
+ varying vec2 uv;
+ attribute vec2 pos;
+ void main() { uv = pos; gl_Position = vec4(pos*2.0-vec2(1.0,1.0),0,1); }`),
+ compileShader(gl.FRAGMENT_SHADER, `
+ precision lowp float;
+ uniform sampler2D tex;
+ varying vec2 uv;
+ void main() { gl_FragColor = texture2D(tex,uv); }`));
+
+gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), gl.STATIC_DRAW);
+gl.vertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 0, 0);
+gl.enableVertexAttribArray(0);
+
+let texture = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, texture);
+
+// Test uploading an image
+const expectedColor = [42, 84, 128, 255];
+const offset = SIZE - 4;
+view.set(expectedColor, offset);
+gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 1, 1);
+gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, view, offset);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+// Test rendering with that image
+gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+// Verify that we rendered what we expected
+wtu.checkCanvasRect(gl, 0, 0, 1, 1, expectedColor, "texSubImage2D produced expected color");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-4gb-wasm-memory.html b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-4gb-wasm-memory.html
new file mode 100644
index 0000000000..d7756629a0
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-4gb-wasm-memory.html
@@ -0,0 +1,86 @@
+<!--
+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.
+-->
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>gl.texSubImage2D() test to Wasm Memory 4GB in size.</title>
+<link rel="stylesheet" href="../../resources/js-test-style.css"/>
+<script src="../../js/js-test-pre.js"></script>
+<script src="../../js/webgl-test-utils.js"> </script>
+</head>
+<body>
+<canvas id="canvas" width="2" height="2" style="width: 40px; height: 40px;"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description(document.title);
+debug("Tests that gl.texSubImage2D() can be called on WebAssembly Memory 4GB in size.");
+debug("");
+let wtu = WebGLTestUtils;
+let gl = wtu.create3DContext("canvas", undefined, 2);
+
+const PAGE = 65536;
+const SIZE = 4*1024*1024*1024 - PAGE; // when uint32_t size is max, we can only reach one page short of full 4GB
+let view = new Uint8Array(new WebAssembly.Memory({ initial: SIZE/PAGE }).buffer);
+
+function compileShader(type, src) {
+ let shader = gl.createShader(type);
+ gl.shaderSource(shader, src);
+ gl.compileShader(shader);
+ let log = gl.getShaderInfoLog(shader);
+ if (log) debug(log);
+ return shader;
+}
+
+function createProgram(vs, fs) {
+ let program = gl.createProgram();
+ gl.attachShader(program, vs);
+ gl.attachShader(program, fs);
+ gl.bindAttribLocation(program, 0, 'pos');
+ gl.linkProgram(program);
+ gl.useProgram(program);
+ return program;
+}
+
+let program = createProgram(
+ compileShader(gl.VERTEX_SHADER, `
+ varying vec2 uv;
+ attribute vec2 pos;
+ void main() { uv = pos; gl_Position = vec4(pos*2.0-vec2(1.0,1.0),0,1); }`),
+ compileShader(gl.FRAGMENT_SHADER, `
+ precision lowp float;
+ uniform sampler2D tex;
+ varying vec2 uv;
+ void main() { gl_FragColor = texture2D(tex,uv); }`));
+
+gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
+gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]), gl.STATIC_DRAW);
+gl.vertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 0, 0);
+gl.enableVertexAttribArray(0);
+
+let texture = gl.createTexture();
+gl.bindTexture(gl.TEXTURE_2D, texture);
+
+// Test uploading an image
+const expectedColor = [42, 84, 128, 255];
+const offset = SIZE - 4;
+view.set(expectedColor, offset);
+gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, 1, 1);
+gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, view, offset);
+wtu.glErrorShouldBe(gl, gl.NO_ERROR);
+
+// Test rendering with that image
+gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
+
+// Verify that we rendered what we expected
+wtu.checkCanvasRect(gl, 0, 0, 1, 1, expectedColor, "texSubImage2D produced expected color");
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>
diff --git a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js
index ec08eea5ca..55913f4366 100644
--- a/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js
+++ b/dom/canvas/test/webgl-conf/checkout/deqp/framework/common/tcuTestCase.js
@@ -453,7 +453,32 @@ goog.scope(function() {
if (inited) {
// Run the test, save the result.
+
+ const debug = tcuTestCase._debug = tcuTestCase._debug || (() => {
+ function LapStopwatch() {
+ this.lap = function() {
+ const now = performance.now();
+ const ret = now - this.last;
+ this.last = now;
+ return ret;
+ };
+ this.lap();
+ }
+ return {
+ stopwatch: new LapStopwatch(),
+ testDoneCount: 0,
+ };
+ })();
+ const overheadDur = debug.stopwatch.lap();
+
tcuTestCase.lastResult = state.currentTest.iterate();
+
+ const testDur = debug.stopwatch.lap();
+ debug.testDoneCount += 1;
+ console.log(
+ `[test ${debug.testDoneCount}] Ran in ${testDur}ms`,
+ `(+ ${overheadDur}ms overhead)`,
+ );
} else {
// Skip uninitialized test.
tcuTestCase.lastResult = tcuTestCase.IterateResult.STOP;
@@ -484,8 +509,8 @@ goog.scope(function() {
}
tcuTestCase.runner.runCallback(tcuTestCase.runTestCases);
- } else
+ } else {
tcuTestCase.runner.terminate();
+ }
};
-
});
diff --git a/dom/canvas/test/webgl-conf/checkout/extra/tex-image-with-video-test.js b/dom/canvas/test/webgl-conf/checkout/extra/tex-image-with-video-test.js
index 01f56b56c3..d2d6be0e28 100644
--- a/dom/canvas/test/webgl-conf/checkout/extra/tex-image-with-video-test.js
+++ b/dom/canvas/test/webgl-conf/checkout/extra/tex-image-with-video-test.js
@@ -27,7 +27,6 @@ function generateTest(pixelFormat, pixelType, prologue) {
{ src: "../resources/red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', },
{ src: "../resources/red-green.webmvp8.webm", type: 'video/webm; codecs="vp8, vorbis"', },
{ src: "../resources/red-green.webmvp9.webm", type: 'video/webm; codecs="vp9"', },
- { src: "../resources/red-green.theora.ogv", type: 'video/ogg; codecs="theora, vorbis"', },
];
var videoNdx = 0;
diff --git a/dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js b/dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js
index e1cb9f749c..adc1f8a5aa 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/js-test-pre.js
@@ -93,20 +93,25 @@ const RESULTS = {
fail: 0,
};
+// We cache these values since they will potentially be accessed many (100k+)
+// times and accessing window can be significantly slower than a local variable.
+const locationPathname = window.location.pathname;
+const webglTestHarness = window.parent.webglTestHarness;
+
function reportTestResultsToHarness(success, msg) {
if (success) {
RESULTS.pass += 1;
} else {
RESULTS.fail += 1;
}
- if (window.parent.webglTestHarness) {
- window.parent.webglTestHarness.reportResults(window.location.pathname, success, msg);
+ if (webglTestHarness) {
+ webglTestHarness.reportResults(locationPathname, success, msg);
}
}
function reportSkippedTestResultsToHarness(success, msg) {
- if (window.parent.webglTestHarness) {
- window.parent.webglTestHarness.reportResults(window.location.pathname, success, msg, true);
+ if (webglTestHarness) {
+ webglTestHarness.reportResults(locationPathname, success, msg, true);
}
}
@@ -116,8 +121,8 @@ function notifyFinishedToHarness() {
}
window._didNotifyFinishedToHarness = true;
- if (window.parent.webglTestHarness) {
- window.parent.webglTestHarness.notifyFinished(window.location.pathname);
+ if (webglTestHarness) {
+ webglTestHarness.notifyFinished(locationPathname);
}
if (window.nonKhronosFrameworkNotifyDone) {
window.nonKhronosFrameworkNotifyDone();
@@ -268,9 +273,9 @@ function getCurrentTestName()
*/
function testPassedOptions(msg, addSpan)
{
+ reportTestResultsToHarness(true, _currentTestName + ": " + msg);
if (addSpan && !quietMode())
{
- reportTestResultsToHarness(true, _currentTestName + ": " + msg);
_addSpan('<span><span class="pass">PASS</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>');
}
if (_jsTestPreVerboseLogging) {
@@ -285,9 +290,9 @@ function testPassedOptions(msg, addSpan)
*/
function testSkippedOptions(msg, addSpan)
{
+ reportSkippedTestResultsToHarness(true, _currentTestName + ": " + msg);
if (addSpan && !quietMode())
{
- reportSkippedTestResultsToHarness(true, _currentTestName + ": " + msg);
_addSpan('<span><span class="warn">SKIP</span> ' + escapeHTML(_currentTestName) + ": " + escapeHTML(msg) + '</span>');
}
if (_jsTestPreVerboseLogging) {
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;
diff --git a/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js
index f48d9d2ad7..ca6cbfcd36 100644
--- a/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js
+++ b/dom/canvas/test/webgl-conf/checkout/js/webgl-test-harness.js
@@ -538,15 +538,36 @@ TestHarness.prototype.runTests = function(opt_options) {
this.startNextTest();
};
-TestHarness.prototype.setTimeout = function(test) {
- var that = this;
- test.timeoutId = this.window.setTimeout(function() {
- that.timeout(test);
- }, this.timeoutDelay);
+TestHarness.prototype._bumpTimeout = function(test) {
+ const newTimeoutAt = performance.now() + this.timeoutDelay;
+ if (test.timeoutAt) {
+ test.timeoutAt = newTimeoutAt;
+ return;
+ }
+ test.timeoutAt = newTimeoutAt;
+
+ const harness = this;
+
+ function enqueueWatchdog() {
+ const remaining = test.timeoutAt - performance.now();
+ //console.log(`watchdog started at ${performance.now()}, ${test.timeoutAt} requested`);
+ this.window.setTimeout(() => {
+ if (!test.timeoutAt) return; // Timeout was cleared.
+ const remainingAtCheckTime = test.timeoutAt - performance.now();
+ if (performance.now() >= test.timeoutAt) {
+ //console.log(`watchdog won at ${performance.now()}, ${test.timeoutAt} requested`);
+ harness.timeout(test);
+ return;
+ }
+ //console.log(`watchdog lost at ${performance.now()}, as ${test.timeoutAt} is now requested`);
+ enqueueWatchdog();
+ }, remaining);
+ }
+ enqueueWatchdog();
};
TestHarness.prototype.clearTimeout = function(test) {
- this.window.clearTimeout(test.timeoutId);
+ test.timeoutAt = null;
};
TestHarness.prototype.startNextTest = function() {
@@ -577,7 +598,7 @@ TestHarness.prototype.startTest = function(iframe, testFile, webglVersion) {
"dumpShaders": this.dumpShaders,
"quiet": this.quiet
});
- this.setTimeout(test);
+ this._bumpTimeout(test);
} else {
this.reportResults(url, !!this.allowSkip, "skipped", true);
this.notifyFinished(url);
@@ -595,11 +616,15 @@ TestHarness.prototype.getTest = function(url) {
TestHarness.prototype.reportResults = function(url, success, msg, skipped) {
url = FilterURL(url);
var test = this.getTest(url);
- this.clearTimeout(test);
- log((success ? "PASS" : "FAIL") + ": " + msg);
+ if (0) {
+ // This is too slow to leave on for tests like
+ // deqp/functional/gles3/vertexarrays/multiple_attributes.output.html
+ // which has 33013505 calls to reportResults.
+ log((success ? "PASS" : "FAIL") + ": " + msg);
+ }
this.reportFunc(TestHarness.reportType.TEST_RESULT, url, msg, success, skipped);
// For each result we get, reset the timeout
- this.setTimeout(test);
+ this._bumpTimeout(test);
};
TestHarness.prototype.dequeTest = function(test) {
diff --git a/dom/canvas/test/webgl-conf/checkout/py/lint/lint.allowlist b/dom/canvas/test/webgl-conf/checkout/py/lint/lint.allowlist
index eb4efce09e..b610bfa4e1 100644
--- a/dom/canvas/test/webgl-conf/checkout/py/lint/lint.allowlist
+++ b/dom/canvas/test/webgl-conf/checkout/py/lint/lint.allowlist
@@ -21,7 +21,7 @@ UNNECESSARY EXECUTABLE PERMISSION:specs/1.0
UNNECESSARY EXECUTABLE PERMISSION:specs/2.0
## Ignore INDENT TABS ##
-
+
INDENT TABS:*.frag
INDENT TABS:*.vert
#The original dEQP tests used tabs throughout.
@@ -46,7 +46,6 @@ INVALID UNICODE:conformance/glsl/misc/non-ascii.vert.html
*:*.mp3
*:*.m4a
*:*.oga
-*:*.ogv
*:*.webm
*:*.mp4
*:*.m4v
diff --git a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-cw.jpg b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-cw.jpg
index d4e75fac7d..d4e75fac7d 100644
--- a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-cw.jpg
+++ b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-cw.jpg
Binary files differ
diff --git a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-ccw.jpg b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-ccw.jpg
index b4679aedd9..b4679aedd9 100644
--- a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-ccw.jpg
+++ b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-ccw.jpg
Binary files differ
diff --git a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-ccw.jpg b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-cw.jpg
index f8c9a6b0cc..f8c9a6b0cc 100644
--- a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-ccw.jpg
+++ b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-cw.jpg
Binary files differ
diff --git a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-cw.jpg b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-ccw.jpg
index 594c86ff4e..594c86ff4e 100644
--- a/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-cw.jpg
+++ b/dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-ccw.jpg
Binary files differ
diff --git a/dom/canvas/test/webgl-conf/checkout/resources/npot-video.theora.ogv b/dom/canvas/test/webgl-conf/checkout/resources/npot-video.theora.ogv
deleted file mode 100644
index 4458678fbf..0000000000
--- a/dom/canvas/test/webgl-conf/checkout/resources/npot-video.theora.ogv
+++ /dev/null
Binary files differ
diff --git a/dom/canvas/test/webgl-conf/checkout/resources/red-gradient-8x1-10bit-untagged.png b/dom/canvas/test/webgl-conf/checkout/resources/red-gradient-8x1-10bit-untagged.png
new file mode 100644
index 0000000000..d01209e9b5
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/resources/red-gradient-8x1-10bit-untagged.png
Binary files differ
diff --git a/dom/canvas/test/webgl-conf/checkout/resources/red-green.theora.ogv b/dom/canvas/test/webgl-conf/checkout/resources/red-green.theora.ogv
deleted file mode 100644
index 1543915a10..0000000000
--- a/dom/canvas/test/webgl-conf/checkout/resources/red-green.theora.ogv
+++ /dev/null
Binary files differ
diff --git a/dom/canvas/test/webgl-conf/checkout/test-guidelines.md b/dom/canvas/test/webgl-conf/checkout/test-guidelines.md
index 679892b1f0..b02738726e 100644
--- a/dom/canvas/test/webgl-conf/checkout/test-guidelines.md
+++ b/dom/canvas/test/webgl-conf/checkout/test-guidelines.md
@@ -18,7 +18,7 @@ the WebGL Working Group when "official" snapshots are taken.
These lines must appear in a comment at the top of every code file under sdk/tests/conformance
```
-Copyright (c) 2019 The Khronos Group Inc.
+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.
```
@@ -102,7 +102,7 @@ found in the LICENSE.txt file.
* Tests that are short and run synchronously end with
- <script src="../../resources/js-test-post.js"></script>
+ <script src="../../js/js-test-post.js"></script>
* Tests that take a long time use setTimeout so as not to freeze the browser.
@@ -144,7 +144,7 @@ found in the LICENSE.txt file.
* Vendors may place test harness specific code in the testing infrastructure.
- resources/js-test-pre.js
+ js/js-test-pre.js
conformance/more/unit.js
* Indent with spaces not tabs. (not everyone uses your tab settings).