diff options
Diffstat (limited to 'dom/canvas/test/webgl-conf/checkout')
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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differindex 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 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 Binary files differdeleted file mode 100644 index 4458678fbf..0000000000 --- a/dom/canvas/test/webgl-conf/checkout/resources/npot-video.theora.ogv +++ /dev/null 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 Binary files differnew file mode 100644 index 0000000000..d01209e9b5 --- /dev/null +++ b/dom/canvas/test/webgl-conf/checkout/resources/red-gradient-8x1-10bit-untagged.png 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 Binary files differdeleted file mode 100644 index 1543915a10..0000000000 --- a/dom/canvas/test/webgl-conf/checkout/resources/red-green.theora.ogv +++ /dev/null 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). |