summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/mochitest
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/mochitest')
-rw-r--r--dom/webgpu/mochitest/mochitest-no-pref.toml10
-rw-r--r--dom/webgpu/mochitest/mochitest.toml116
-rw-r--r--dom/webgpu/mochitest/test_basic_canvas.worker.html18
-rw-r--r--dom/webgpu/mochitest/test_basic_canvas.worker.js32
-rw-r--r--dom/webgpu/mochitest/test_buffer_mapping.html73
-rw-r--r--dom/webgpu/mochitest/test_buffer_mapping_invalid_device.html60
-rw-r--r--dom/webgpu/mochitest/test_command_buffer_creation.html29
-rw-r--r--dom/webgpu/mochitest/test_context_configure.html48
-rw-r--r--dom/webgpu/mochitest/test_device_creation.html29
-rw-r--r--dom/webgpu/mochitest/test_device_lost.html73
-rw-r--r--dom/webgpu/mochitest/test_disabled.html17
-rw-r--r--dom/webgpu/mochitest/test_double_encoder_finish.html36
-rw-r--r--dom/webgpu/mochitest/test_enabled.html17
-rw-r--r--dom/webgpu/mochitest/test_error_scope.html43
-rw-r--r--dom/webgpu/mochitest/test_insecure_context.html22
-rw-r--r--dom/webgpu/mochitest/test_navigator_gpu_not_replaceable.html32
-rw-r--r--dom/webgpu/mochitest/test_queue_copyExternalImageToTexture.html261
-rw-r--r--dom/webgpu/mochitest/test_queue_write.html50
-rw-r--r--dom/webgpu/mochitest/test_queue_write_invalid_device.html44
-rw-r--r--dom/webgpu/mochitest/test_submit_compute_empty.html32
-rw-r--r--dom/webgpu/mochitest/test_submit_render_empty.html57
-rw-r--r--dom/webgpu/mochitest/test_submit_render_empty.worker.html14
-rw-r--r--dom/webgpu/mochitest/test_submit_render_empty.worker.js49
-rw-r--r--dom/webgpu/mochitest/worker_wrapper.js33
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();
+}