diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /dom/webgpu/mochitest | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/webgpu/mochitest')
24 files changed, 1195 insertions, 0 deletions
diff --git a/dom/webgpu/mochitest/mochitest-no-pref.toml b/dom/webgpu/mochitest/mochitest-no-pref.toml new file mode 100644 index 0000000000..511b840c0b --- /dev/null +++ b/dom/webgpu/mochitest/mochitest-no-pref.toml @@ -0,0 +1,10 @@ +[DEFAULT] +subsuite = "webgpu" +run-if = ["release_or_beta"] + +# Even if the pref were enabled, WebGPU is only available in secure contexts. +# +# See spec WebIDL, like this: https://www.w3.org/TR/webgpu/#navigatorgpu +scheme = "https" + +["test_disabled.html"] diff --git a/dom/webgpu/mochitest/mochitest.toml b/dom/webgpu/mochitest/mochitest.toml new file mode 100644 index 0000000000..f32d7a9a86 --- /dev/null +++ b/dom/webgpu/mochitest/mochitest.toml @@ -0,0 +1,116 @@ +[DEFAULT] +subsuite = "webgpu" +run-if = ["!release_or_beta"] +prefs = [ + "dom.webgpu.enabled=true", + "dom.webgpu.workers.enabled=true", + "gfx.offscreencanvas.enabled=true", +] +support-files = [ + "worker_wrapper.js", + "test_basic_canvas.worker.js", + "test_submit_render_empty.worker.js", +] + +# WebGPU is only available in secure contexts. +# +# See spec WebIDL, like this: https://www.w3.org/TR/webgpu/#navigatorgpu +scheme = "https" + +["test_basic_canvas.worker.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_buffer_mapping.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_buffer_mapping_invalid_device.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_command_buffer_creation.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_context_configure.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_device_creation.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_device_lost.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_double_encoder_finish.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_enabled.html"] + +["test_error_scope.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_insecure_context.html"] +# This test checks that WebGPU is not available in insecure contexts. +scheme = "http" + +["test_navigator_gpu_not_replaceable.html"] + +["test_queue_copyExternalImageToTexture.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_queue_write.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_queue_write_invalid_device.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_submit_compute_empty.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_submit_render_empty.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] + +["test_submit_render_empty.worker.html"] +fail-if = [ + "os == 'linux' && os_version == '18.04'", + "os == 'mac'", +] diff --git a/dom/webgpu/mochitest/test_basic_canvas.worker.html b/dom/webgpu/mochitest/test_basic_canvas.worker.html new file mode 100644 index 0000000000..a23ee9fc70 --- /dev/null +++ b/dom/webgpu/mochitest/test_basic_canvas.worker.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="worker_wrapper.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <canvas id="canvas"></canvas> + <script> + const canvas = document.getElementById("canvas"); + const offscreen = canvas.transferControlToOffscreen(); + + runWorkerTest("test_basic_canvas.worker.js", { offscreen }, [offscreen]); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_basic_canvas.worker.js b/dom/webgpu/mochitest/test_basic_canvas.worker.js new file mode 100644 index 0000000000..cec6d6f210 --- /dev/null +++ b/dom/webgpu/mochitest/test_basic_canvas.worker.js @@ -0,0 +1,32 @@ +self.addEventListener("message", async function (event) { + try { + const offscreen = event.data.offscreen; + const context = offscreen.getContext("webgpu"); + + const swapChainFormat = navigator.gpu.getPreferredCanvasFormat(); + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + + context.configure({ + device, + format: swapChainFormat, + size: { width: 100, height: 100, depth: 1 }, + }); + + const texture = context.getCurrentTexture(); + + self.postMessage([ + { + value: texture !== undefined, + message: "texture !== undefined", + }, + ]); + } catch (e) { + self.postMessage([ + { + value: false, + message: "Unhandled exception " + e, + }, + ]); + } +}); diff --git a/dom/webgpu/mochitest/test_buffer_mapping.html b/dom/webgpu/mochitest/test_buffer_mapping.html new file mode 100644 index 0000000000..01dfbf893e --- /dev/null +++ b/dom/webgpu/mochitest/test_buffer_mapping.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + async function testBody() { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + + const bufferRead = device.createBuffer({ + size: 4, + usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, + }); + const bufferWrite = device.createBuffer({ + size: 4, + usage: GPUBufferUsage.COPY_SRC, + mappedAtCreation: true, + }); + new Float32Array(bufferWrite.getMappedRange()).set([1.0]); + bufferWrite.unmap(); + + const encoder = device.createCommandEncoder(); + encoder.copyBufferToBuffer(bufferWrite, 0, bufferRead, 0, 4); + device.queue.submit([encoder.finish()]); + + await bufferRead.mapAsync(GPUMapMode.READ); + + try { + bufferRead.getMappedRange(0, 5); + ok(false, "mapped with size outside buffer should throw"); + } catch (e) { + ok( + true, + "mapped with size outside buffer should throw OperationError" + ); + } + + try { + bufferRead.getMappedRange(4, 1); + ok(false, "mapped with offset outside buffer should throw"); + } catch (e) { + ok( + true, + "mapped with offset outside buffer should throw OperationError" + ); + } + + const data = bufferRead.getMappedRange(); + is(data.byteLength, 4, "array should be 4 bytes long"); + + const value = new Float32Array(data)[0]; + ok(value == 1.0, "value == 1.0"); + + bufferRead.unmap(); + is(data.byteLength, 0, "array should be detached after explicit unmap"); + } + + SimpleTest.waitForExplicitFinish(); + testBody() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_buffer_mapping_invalid_device.html b/dom/webgpu/mochitest/test_buffer_mapping_invalid_device.html new file mode 100644 index 0000000000..6b7e7fafad --- /dev/null +++ b/dom/webgpu/mochitest/test_buffer_mapping_invalid_device.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + async function testBody() { + const adapter = await navigator.gpu.requestAdapter({}); + const device = await adapter.requestDevice({}); + const bindGroupLayout = device.createBindGroupLayout({ + entries: [ + { + binding: 256, + storageTexture: { format: "bc1-rgba-unorm-srgb" }, + visibility: GPUShaderStage.FRAGMENT, + }, + ], + }); + const buffer1 = device.createBuffer({ + size: 32, + usage: GPUBufferUsage.MAP_READ, + }); + + // Call device.destroy, which makes the device invalid. Further object creation + // on device will create objects that are also invalid. + device.destroy(); + + // Create an invalid buffer2. + const buffer2 = device.createBuffer({ + size: 32, + usage: GPUBufferUsage.MAP_WRITE, + }); + + // Create an invalid bind group, referencing invalid buffer2. + const bindGroup = device.createBindGroup({ + layout: bindGroupLayout, + entries: [ + { binding: 1, resource: { buffer: buffer1 } }, + { binding: 2, resource: { buffer: buffer2 } }, + ], + }); + + ok(bindGroup, "Created a bind group referencing an invalid buffer."); + } + + SimpleTest.waitForExplicitFinish(); + testBody() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_command_buffer_creation.html b/dom/webgpu/mochitest/test_command_buffer_creation.html new file mode 100644 index 0000000000..a92c038afd --- /dev/null +++ b/dom/webgpu/mochitest/test_command_buffer_creation.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + const encoder = device.createCommandEncoder(); + const command_buffer = encoder.finish(); + ok(command_buffer !== undefined, "command_buffer !== undefined"); + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_context_configure.html b/dom/webgpu/mochitest/test_context_configure.html new file mode 100644 index 0000000000..7ca96d0afe --- /dev/null +++ b/dom/webgpu/mochitest/test_context_configure.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + async function testBody() { + const adapter = await navigator.gpu.requestAdapter({}); + const device = await adapter.requestDevice({}); + const canvas = document.createElement("canvas"); + const context = canvas.getContext("webgpu"); + const format = navigator.gpu.getPreferredCanvasFormat(adapter); + + // Attempt to configure with a too-large canvas, which should + // fail due to device texture limits. + canvas.width = 1970696937; + let expectedError; + try { + context.configure({ + device, + format, + }); + } catch (error) { + expectedError = error; + } + // Bug 1864904: This should become an "is". + todo_is( + typeof expectedError, + "TypeError", + "Failed configure should generate a TypeError." + ); + } + + SimpleTest.waitForExplicitFinish(); + testBody() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_device_creation.html b/dom/webgpu/mochitest/test_device_creation.html new file mode 100644 index 0000000000..678359c323 --- /dev/null +++ b/dom/webgpu/mochitest/test_device_creation.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const limits = adapter.limits; + const features = adapter.features; + const device = await adapter.requestDevice(); + ok(device !== undefined, "device !== undefined"); + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_device_lost.html b/dom/webgpu/mochitest/test_device_lost.html new file mode 100644 index 0000000000..bc6614f0ef --- /dev/null +++ b/dom/webgpu/mochitest/test_device_lost.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const destroy_causes_lost = async function () { + const adapter = await navigator.gpu.requestAdapter(); + ok(adapter !== undefined, "adapter !== undefined"); + const device = await adapter.requestDevice(); + ok(device !== undefined, "device !== undefined"); + + const lostPromise = device.lost; + device.destroy(); + const deviceLostReason = await lostPromise; + + is( + deviceLostReason.reason, + "destroyed", + "Destroy reason should correspond to GPUDeviceLostReason.destroyed" + ); + is(deviceLostReason.message, "", "Destroy message should be blank"); + }; + + const drop_causes_lost_is_unobservable = async function () { + const adapter = await navigator.gpu.requestAdapter(); + ok(adapter !== undefined, "adapter !== undefined"); + + let lostPromise; + // Create a scope with a device that will go out of scope + // and then be dropped. + { + const device = await adapter.requestDevice(); + ok(device !== undefined, "device !== undefined"); + + lostPromise = device.lost; + } + + SimpleTest.requestFlakyTimeout( + "Racing against promise that should never resolve." + ); + const TIMEOUT_MS = 5000; + let timeoutPromise = new Promise(resolve => { + let timeoutValue = { reason: "timeout" }; + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + setTimeout(() => resolve(timeoutValue), TIMEOUT_MS); + }); + + const firstPromise = await Promise.race([lostPromise, timeoutPromise]); + is( + firstPromise.reason, + "timeout", + "timeoutPromise should return before lostPromise." + ); + }; + + SimpleTest.waitForExplicitFinish(); + + destroy_causes_lost() + .then(() => drop_causes_lost_is_unobservable()) + .catch(e => ok(false, `Unhandled exception ${e}`)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_disabled.html b/dom/webgpu/mochitest/test_disabled.html new file mode 100644 index 0000000000..12eb01e465 --- /dev/null +++ b/dom/webgpu/mochitest/test_disabled.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + !SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be disabled." + ); + ok(navigator.gpu === undefined, "navigator.gpu === undefined"); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_double_encoder_finish.html b/dom/webgpu/mochitest/test_double_encoder_finish.html new file mode 100644 index 0000000000..24c91c3165 --- /dev/null +++ b/dom/webgpu/mochitest/test_double_encoder_finish.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + const encoder = device.createCommandEncoder(); + + const command_buffer = encoder.finish(); + ok(command_buffer !== undefined, "command_buffer !== undefined"); + + const invalid_command_buffer = encoder.finish(); + ok( + invalid_command_buffer !== undefined, + "invalid_command_buffer !== undefined" + ); + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_enabled.html b/dom/webgpu/mochitest/test_enabled.html new file mode 100644 index 0000000000..318788bf1e --- /dev/null +++ b/dom/webgpu/mochitest/test_enabled.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + ok(navigator.gpu !== undefined, "navigator.gpu !== undefined"); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_error_scope.html b/dom/webgpu/mochitest/test_error_scope.html new file mode 100644 index 0000000000..f89b4b6e78 --- /dev/null +++ b/dom/webgpu/mochitest/test_error_scope.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + + device.pushErrorScope("validation"); + const buffer = device.createBuffer({ size: 0, usage: 0 }); + const error = await device.popErrorScope(); + + isnot( + error, + null, + "Attempt to createBuffer with size 0 and usage 0 should generate an error." + ); + + try { + await device.popErrorScope(); + ok(false, "Should have thrown"); + } catch (ex) { + ok(ex.name == "OperationError", "Should throw an OperationError"); + } + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_insecure_context.html b/dom/webgpu/mochitest/test_insecure_context.html new file mode 100644 index 0000000000..dcc4a313b9 --- /dev/null +++ b/dom/webgpu/mochitest/test_insecure_context.html @@ -0,0 +1,22 @@ +<!-- This is somewhat redundant with + dom/tests/mochitest/general/test_interfaces.js, but I think it's good to + have something here as well. --> + +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + ok(!isSecureContext, "test should not run in a secure context"); + ok(navigator.gpu === undefined, "navigator.gpu === undefined"); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_navigator_gpu_not_replaceable.html b/dom/webgpu/mochitest/test_navigator_gpu_not_replaceable.html new file mode 100644 index 0000000000..6b6b7b9715 --- /dev/null +++ b/dom/webgpu/mochitest/test_navigator_gpu_not_replaceable.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + const originalNavigatorGpu = navigator.gpu; + + const BAD_GPU_VAL = "ohcrapthisisbad"; + ok( + navigator.gpu !== BAD_GPU_VAL, + "`navigator.gpu` should never equal `BAD_GPU_VAL` (before replacement attempt)" + ); + navigator.gpu = BAD_GPU_VAL; + ok( + navigator.gpu !== BAD_GPU_VAL, + "`navigator.gpu` should never equal `BAD_GPU_VAL` (after replacement attempt)" + ); + ok( + navigator.gpu === originalNavigatorGpu, + "`navigator.gpu` should equal originally observed value after attempted replacement" + ); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_queue_copyExternalImageToTexture.html b/dom/webgpu/mochitest/test_queue_copyExternalImageToTexture.html new file mode 100644 index 0000000000..279b4a52b4 --- /dev/null +++ b/dom/webgpu/mochitest/test_queue_copyExternalImageToTexture.html @@ -0,0 +1,261 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + + <body> + <script type="text/javascript"> + "use strict"; + + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "WebGPU pref should be enabled." + ); + ok( + SpecialPowers.getBoolPref("gfx.offscreencanvas.enabled"), + "OffscreenCanvas pref should be enabled." + ); + + SimpleTest.waitForExplicitFinish(); + + function requestAnimationFramePromise() { + return new Promise(requestAnimationFrame); + } + + function createSourceCanvasWebgl() { + const offscreenCanvas = new OffscreenCanvas(200, 200); + const gl = offscreenCanvas.getContext("webgl"); + + const COLOR_VALUE = 127.0 / 255.0; + const ALPHA_VALUE = 127.0 / 255.0; + + gl.enable(gl.SCISSOR_TEST); + + gl.scissor(0, 0, 100, 100); + gl.clearColor(COLOR_VALUE, 0.0, 0.0, ALPHA_VALUE); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.scissor(100, 0, 100, 100); + gl.clearColor(0.0, COLOR_VALUE, 0.0, ALPHA_VALUE); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.scissor(0, 100, 100, 100); + gl.clearColor(0.0, 0.0, COLOR_VALUE, ALPHA_VALUE); + gl.clear(gl.COLOR_BUFFER_BIT); + + gl.scissor(100, 100, 100, 100); + gl.clearColor(0.0, 0.0, 0.0, ALPHA_VALUE); + gl.clear(gl.COLOR_BUFFER_BIT); + + return { + source: offscreenCanvas, + origin: { x: 0, y: 0 }, + flipY: true, + }; + } + + function createSourceCanvas2d() { + const offscreenCanvas = new OffscreenCanvas(200, 200); + const context = offscreenCanvas.getContext("2d"); + + context.fillStyle = "rgba(255,0,0,0.498)"; + context.fillRect(0, 0, 100, 100); + + context.fillStyle = "rgba(0,255,0,0.498)"; + context.fillRect(100, 0, 100, 100); + + context.fillStyle = "rgba(0,0,255,0.498)"; + context.fillRect(0, 100, 100, 100); + + context.fillStyle = "rgba(0,0,0,0.498)"; + context.fillRect(100, 100, 100, 100); + + return { + source: offscreenCanvas, + origin: { x: 0, y: 0 }, + flipY: false, + }; + } + + function createSourceImageBitmap() { + const sourceCanvas = createSourceCanvas2d(); + return { + source: sourceCanvas.source.transferToImageBitmap(), + origin: { x: 0, y: 0 }, + flipY: false, + }; + } + + async function mapDestTexture( + device, + source, + destFormat, + premultiply, + copySize + ) { + const bytesPerRow = 256 * 4; // 256 aligned for 200 pixels + const texture = device.createTexture({ + format: destFormat, + size: copySize, + usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST, + }); + + device.queue.copyExternalImageToTexture( + source, + { texture, premultipliedAlpha: premultiply }, + copySize + ); + + const buffer = device.createBuffer({ + size: 1024 * 200, + usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST, + }); + + const encoder = device.createCommandEncoder(); + encoder.copyTextureToBuffer( + { texture }, + { buffer, bytesPerRow }, + copySize + ); + device.queue.submit([encoder.finish()]); + + await buffer.mapAsync(GPUMapMode.READ); + return buffer; + } + + async function verifyBuffer( + test, + device, + source, + format, + premultiply, + copyDim, + topLeftPixelData + ) { + try { + const buffer = await mapDestTexture( + device, + source, + format, + premultiply, + copyDim + ); + const arrayBuffer = buffer.getMappedRange(); + const view = new Uint8Array(arrayBuffer); + for (let i = 0; i < topLeftPixelData.length; ++i) { + is( + view[i], + topLeftPixelData[i], + test + + " " + + format + + " (" + + source.origin.x + + "," + + source.origin.y + + ") channel " + + i + ); + } + } catch (e) { + ok(false, "WebGPU exception: " + e); + } + } + + async function verifySourceCanvas(test, device, source) { + await verifyBuffer( + test, + device, + source, + "rgba8unorm", + /* premultiply */ true, + { width: 200, height: 200 }, + [127, 0, 0, 127] + ); + await verifyBuffer( + test, + device, + source, + "bgra8unorm", + /* premultiply */ true, + { width: 200, height: 200 }, + [0, 0, 127, 127] + ); + await verifyBuffer( + test, + device, + source, + "rgba8unorm", + /* premultiply */ false, + { width: 200, height: 200 }, + [255, 0, 0, 127] + ); + await verifyBuffer( + test, + device, + source, + "bgra8unorm", + /* premultiply */ false, + { width: 200, height: 200 }, + [0, 0, 255, 127] + ); + + // The copy is flipped but the origin is relative to the original source data, + // so we need to invert for WebGL. + const topRightPixelData = + test === "webgl" ? [0, 0, 0, 127] : [0, 127, 0, 127]; + const topRightOrigin = { origin: { x: 100, y: 0 } }; + await verifyBuffer( + test, + device, + { ...source, ...topRightOrigin }, + "bgra8unorm", + /* premultiply */ true, + { width: 100, height: 100 }, + topRightPixelData + ); + + const bottomLeftPixelData = + test === "webgl" ? [0, 0, 127, 127] : [127, 0, 0, 127]; + const bottomLeftOrigin = { origin: { x: 0, y: 100 } }; + await verifyBuffer( + test, + device, + { ...source, ...bottomLeftOrigin }, + "bgra8unorm", + /* premultiply */ true, + { width: 100, height: 100 }, + bottomLeftPixelData + ); + } + + async function writeDestCanvas(source2d, sourceWebgl, sourceImageBitmap) { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + await verifySourceCanvas("2d", device, source2d); + await verifySourceCanvas("imageBitmap", device, sourceImageBitmap); + await verifySourceCanvas("webgl", device, sourceWebgl); + } + + async function runTest() { + try { + const source2d = createSourceCanvas2d(); + const sourceWebgl = createSourceCanvasWebgl(); + const sourceImageBitmap = createSourceImageBitmap(); + await requestAnimationFramePromise(); + await requestAnimationFramePromise(); + await writeDestCanvas(source2d, sourceWebgl, sourceImageBitmap); + } catch (e) { + ok(false, "Uncaught exception: " + e); + } finally { + SimpleTest.finish(); + } + } + + runTest(); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_queue_write.html b/dom/webgpu/mochitest/test_queue_write.html new file mode 100644 index 0000000000..585c1617cd --- /dev/null +++ b/dom/webgpu/mochitest/test_queue_write.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + const buffer = device.createBuffer({ + size: 16, + usage: + GPUBufferUsage.COPY_DST | + GPUBufferUsage.COPY_SRC | + GPUBufferUsage.VERTEX, + }); + const arrayBuf = new ArrayBuffer(16); + new Int32Array(arrayBuf).fill(5); + device.queue.writeBuffer(buffer, 0, arrayBuf, 0); + const texture = device.createTexture({ + size: [2, 2, 1], + dimension: "2d", + format: "rgba8unorm", + usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.COPY_SRC, + }); + device.queue.writeTexture( + { texture }, + arrayBuf, + { bytesPerRow: 8 }, + [2, 2, 1] + ); + // this isn't a process check, we need to read back the contents and verify the writes happened + ok(device !== undefined, ""); + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_queue_write_invalid_device.html b/dom/webgpu/mochitest/test_queue_write_invalid_device.html new file mode 100644 index 0000000000..09180dee7f --- /dev/null +++ b/dom/webgpu/mochitest/test_queue_write_invalid_device.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + + // Destroy the device, making it invalid. + device.destroy(); + + // Creating a buffer on an invalid device will create an invalid + // buffer. + const buffer = device.createBuffer({ + size: 16, + usage: + GPUBufferUsage.COPY_DST | + GPUBufferUsage.COPY_SRC | + GPUBufferUsage.VERTEX, + }); + const arrayBuf = new ArrayBuffer(16); + new Int32Array(arrayBuf).fill(5); + + // Writing to an invalid buffer should not throw an error. + device.queue.writeBuffer(buffer, 0, arrayBuf, 0); + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_submit_compute_empty.html b/dom/webgpu/mochitest/test_submit_compute_empty.html new file mode 100644 index 0000000000..82cb9473c5 --- /dev/null +++ b/dom/webgpu/mochitest/test_submit_compute_empty.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + const encoder = device.createCommandEncoder(); + const pass = encoder.beginComputePass(); + pass.end(); + const command_buffer = encoder.finish(); + device.queue.submit([command_buffer]); + ok(command_buffer !== undefined, "command_buffer !== undefined"); + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_submit_render_empty.html b/dom/webgpu/mochitest/test_submit_render_empty.html new file mode 100644 index 0000000000..bac0d1ede7 --- /dev/null +++ b/dom/webgpu/mochitest/test_submit_render_empty.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "Pref should be enabled." + ); + + const func = async function () { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + + const swapChainFormat = "rgba8unorm"; + const bundleEncoder = device.createRenderBundleEncoder({ + colorFormats: [swapChainFormat], + }); + const bundle = bundleEncoder.finish({}); + + const texture = device.createTexture({ + size: { width: 100, height: 100, depth: 1 }, + format: swapChainFormat, + usage: GPUTextureUsage.RENDER_ATTACHMENT, + }); + const view = texture.createView(); + + const encoder = device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view, + clearValue: { r: 0, g: 0, b: 0, a: 0 }, + loadOp: "clear", + storeOp: "store", + }, + ], + }); + pass.executeBundles([bundle]); + pass.end(); + const command_buffer = encoder.finish(); + + device.queue.submit([command_buffer]); + ok(command_buffer !== undefined, "command_buffer !== undefined"); + }; + + SimpleTest.waitForExplicitFinish(); + func() + .catch(e => ok(false, "Unhandled exception " + e)) + .finally(() => SimpleTest.finish()); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_submit_render_empty.worker.html b/dom/webgpu/mochitest/test_submit_render_empty.worker.html new file mode 100644 index 0000000000..8db3168be0 --- /dev/null +++ b/dom/webgpu/mochitest/test_submit_render_empty.worker.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8" /> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="worker_wrapper.js"></script> + <link rel="stylesheet" href="/tests/SimpleTest/test.css" /> + </head> + <body> + <script> + runWorkerTest("test_submit_render_empty.worker.js", {}, []); + </script> + </body> +</html> diff --git a/dom/webgpu/mochitest/test_submit_render_empty.worker.js b/dom/webgpu/mochitest/test_submit_render_empty.worker.js new file mode 100644 index 0000000000..334230c3c1 --- /dev/null +++ b/dom/webgpu/mochitest/test_submit_render_empty.worker.js @@ -0,0 +1,49 @@ +self.addEventListener("message", async function (event) { + try { + const adapter = await navigator.gpu.requestAdapter(); + const device = await adapter.requestDevice(); + + const swapChainFormat = "rgba8unorm"; + const bundleEncoder = device.createRenderBundleEncoder({ + colorFormats: [swapChainFormat], + }); + const bundle = bundleEncoder.finish({}); + + const texture = device.createTexture({ + size: { width: 100, height: 100, depth: 1 }, + format: swapChainFormat, + usage: GPUTextureUsage.RENDER_ATTACHMENT, + }); + const view = texture.createView(); + + const encoder = device.createCommandEncoder(); + const pass = encoder.beginRenderPass({ + colorAttachments: [ + { + view, + clearValue: { r: 0, g: 0, b: 0, a: 0 }, + loadOp: "clear", + storeOp: "store", + }, + ], + }); + pass.executeBundles([bundle]); + pass.end(); + const command_buffer = encoder.finish(); + + device.queue.submit([command_buffer]); + self.postMessage([ + { + value: command_buffer !== undefined, + message: "command_buffer !== undefined", + }, + ]); + } catch (e) { + self.postMessage([ + { + value: false, + message: "Unhandled exception " + e, + }, + ]); + } +}); diff --git a/dom/webgpu/mochitest/worker_wrapper.js b/dom/webgpu/mochitest/worker_wrapper.js new file mode 100644 index 0000000000..6f6de9002d --- /dev/null +++ b/dom/webgpu/mochitest/worker_wrapper.js @@ -0,0 +1,33 @@ +ok( + SpecialPowers.getBoolPref("dom.webgpu.enabled"), + "WebGPU pref should be enabled." +); +ok( + SpecialPowers.getBoolPref("gfx.offscreencanvas.enabled"), + "OffscreenCanvas pref should be enabled." +); +SimpleTest.waitForExplicitFinish(); + +const workerWrapperFunc = async function (worker_path, data, transfer) { + const worker = new Worker(worker_path); + + const results = new Promise((resolve, reject) => { + worker.addEventListener("message", event => { + resolve(event.data); + }); + }); + + worker.postMessage(data, transfer); + for (const result of await results) { + ok(result.value, result.message); + } +}; + +async function runWorkerTest(worker_path, data, transfer) { + try { + await workerWrapperFunc(worker_path, data, transfer); + } catch (e) { + ok(false, "Unhandled exception " + e); + } + SimpleTest.finish(); +} |