summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Kettmeir <crowlkats@toaxl.com>2023-12-09 01:19:16 +0100
committerGitHub <noreply@github.com>2023-12-09 01:19:16 +0100
commit393abed3873d83019feb5bcebb10a6929133862a (patch)
treec346e6d628e6b037fb8f881a70ca2ae6f70692b6
parent123d9ea047a2e10803e260ebf00f31fcc98463ee (diff)
feat: bring back WebGPU (#20812)
Signed-off-by: Leo Kettmeir <crowlkats@toaxl.com> Co-authored-by: Kenta Moriuchi <moriken@kimamass.com> Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
-rw-r--r--.cargo/config.toml11
-rw-r--r--Cargo.lock622
-rw-r--r--Cargo.toml8
-rw-r--r--cli/args/flags.rs1
-rw-r--r--cli/build.rs9
-rw-r--r--cli/js/40_testing.js5
-rw-r--r--cli/main.rs7
-rw-r--r--cli/tests/integration/js_unit_tests.rs1
-rw-r--r--cli/tests/integration/run_tests.rs11
-rw-r--r--cli/tests/integration/shared_library_tests.rs27
-rw-r--r--cli/tests/testdata/run/unstable_webgpu.disabled.out2
-rw-r--r--cli/tests/testdata/run/unstable_webgpu.enabled.out2
-rw-r--r--cli/tests/testdata/run/unstable_webgpu.js10
-rw-r--r--cli/tests/testdata/webgpu/computepass_shader.wgsl38
-rw-r--r--cli/tests/testdata/webgpu/hellotriangle.outbin0 -> 204800 bytes
-rw-r--r--cli/tests/testdata/webgpu/hellotriangle_shader.wgsl11
-rw-r--r--cli/tests/unit/webgpu_test.ts242
-rw-r--r--cli/tsc/dts/lib.deno.window.d.ts2
-rw-r--r--cli/tsc/dts/lib.deno.worker.d.ts1
-rw-r--r--cli/tsc/dts/lib.deno_webgpu.d.ts1315
-rw-r--r--cli/tsc/mod.rs1
-rw-r--r--ext/webgpu/01_webgpu.js7087
-rw-r--r--ext/webgpu/02_surface.js235
-rw-r--r--ext/webgpu/Cargo.toml49
-rw-r--r--ext/webgpu/LICENSE.md20
-rw-r--r--ext/webgpu/README.md35
-rw-r--r--ext/webgpu/binding.rs340
-rw-r--r--ext/webgpu/buffer.rs205
-rw-r--r--ext/webgpu/bundle.rs405
-rw-r--r--ext/webgpu/command_encoder.rs633
-rw-r--r--ext/webgpu/compute_pass.rs225
-rw-r--r--ext/webgpu/error.rs316
-rw-r--r--ext/webgpu/lib.rs768
-rw-r--r--ext/webgpu/pipeline.rs453
-rw-r--r--ext/webgpu/queue.rs131
-rw-r--r--ext/webgpu/render_pass.rs519
-rw-r--r--ext/webgpu/sampler.rs80
-rw-r--r--ext/webgpu/shader.rs55
-rw-r--r--ext/webgpu/surface.rs133
-rw-r--r--ext/webgpu/texture.rs134
-rw-r--r--ext/webgpu/webgpu.idl1233
-rw-r--r--runtime/Cargo.toml2
-rw-r--r--runtime/build.rs1
-rw-r--r--runtime/errors.rs1
-rw-r--r--runtime/js/90_deno_ns.js5
-rw-r--r--runtime/js/98_global_scope.js90
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/snapshot.rs1
-rw-r--r--runtime/web_worker.rs1
-rw-r--r--runtime/worker.rs1
-rw-r--r--tools/README.md13
-rw-r--r--tools/wgpu_sync.js92
52 files changed, 15487 insertions, 103 deletions
diff --git a/.cargo/config.toml b/.cargo/config.toml
index 590f03358..f5b2f124b 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -11,8 +11,17 @@ rustflags = [
"link-arg=/STACK:4194304",
]
+[target.x86_64-apple-darwin]
+rustflags = [
+ "-C",
+ "link-args=-weak_framework Metal -weak_framework MetalPerformanceShaders -weak_framework QuartzCore -weak_framework CoreGraphics",
+]
+
[target.aarch64-apple-darwin]
-rustflags = ["-C", "link-arg=-fuse-ld=lld"]
+rustflags = [
+ "-C",
+ "link-args=-fuse-ld=lld -weak_framework Metal -weak_framework MetalPerformanceShaders -weak_framework QuartzCore -weak_framework CoreGraphics",
+]
[target.'cfg(all())']
rustflags = [
diff --git a/Cargo.lock b/Cargo.lock
index 7f84bf380..ee1be7d03 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -128,6 +128,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -150,20 +159,20 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
-version = "0.2.2"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -187,6 +196,18 @@ name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "ash"
+version = "0.37.3+1.3.251"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a"
+dependencies = [
+ "libloading 0.7.4",
+]
[[package]]
name = "asn1-rs"
@@ -423,6 +444,15 @@ name = "bitflags"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
[[package]]
name = "block-buffer"
@@ -598,6 +628,16 @@ dependencies = [
]
[[package]]
+name = "codespan-reporting"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
+dependencies = [
+ "termcolor",
+ "unicode-width",
+]
+
+[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -643,9 +683,9 @@ checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "core-foundation"
-version = "0.9.3"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
@@ -653,9 +693,20 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
[[package]]
name = "cpufeatures"
@@ -779,6 +830,17 @@ dependencies = [
]
[[package]]
+name = "d3d12"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20"
+dependencies = [
+ "bitflags 2.4.1",
+ "libloading 0.8.1",
+ "winapi",
+]
+
+[[package]]
name = "darling"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -820,7 +882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
dependencies = [
"cfg-if",
- "hashbrown 0.14.2",
+ "hashbrown 0.14.3",
"lock_api",
"once_cell",
"parking_lot_core 0.9.9",
@@ -828,9 +890,9 @@ dependencies = [
[[package]]
name = "data-encoding"
-version = "2.4.0"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
+checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5"
[[package]]
name = "data-url"
@@ -1379,7 +1441,7 @@ name = "deno_napi"
version = "0.57.0"
dependencies = [
"deno_core",
- "libloading",
+ "libloading 0.7.4",
]
[[package]]
@@ -1531,6 +1593,7 @@ dependencies = [
"deno_tls",
"deno_url",
"deno_web",
+ "deno_webgpu",
"deno_webidl",
"deno_websocket",
"deno_webstorage",
@@ -1659,6 +1722,19 @@ dependencies = [
]
[[package]]
+name = "deno_webgpu"
+version = "0.94.0"
+dependencies = [
+ "deno_core",
+ "raw-window-handle",
+ "serde",
+ "tokio",
+ "wgpu-core",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
name = "deno_webidl"
version = "0.127.0"
dependencies = [
@@ -1796,9 +1872,9 @@ dependencies = [
[[package]]
name = "deranged"
-version = "0.3.9"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3"
+checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc"
dependencies = [
"powerfmt",
]
@@ -2163,12 +2239,12 @@ dependencies = [
[[package]]
name = "errno"
-version = "0.3.7"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
"libc",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2291,14 +2367,14 @@ checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
[[package]]
name = "filetime"
-version = "0.2.22"
+version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
+checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.3.5",
- "windows-sys 0.48.0",
+ "redox_syscall 0.4.1",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2347,10 +2423,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
name = "form_urlencoded"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
@@ -2530,9 +2633,20 @@ dependencies = [
[[package]]
name = "gimli"
-version = "0.28.0"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
+
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
[[package]]
name = "glibc_version"
@@ -2550,6 +2664,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
+name = "glow"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "886c2a30b160c4c6fec8f987430c26b526b7988ca71f664e6a699ddf6f9601e4"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171"
+dependencies = [
+ "bitflags 2.4.1",
+ "gpu-alloc-types",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4"
+dependencies = [
+ "bitflags 2.4.1",
+]
+
+[[package]]
+name = "gpu-allocator"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40fe17c8a05d60c38c0a4e5a3c802f2f1ceb66b76c67d96ffb34bef0475a7fad"
+dependencies = [
+ "backtrace",
+ "log",
+ "presser",
+ "thiserror",
+ "winapi",
+ "windows",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c"
+dependencies = [
+ "bitflags 2.4.1",
+ "gpu-descriptor-types",
+ "hashbrown 0.14.3",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c"
+dependencies = [
+ "bitflags 2.4.1",
+]
+
+[[package]]
name = "group"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2587,9 +2775,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
-version = "0.14.2"
+version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
"ahash",
"allocator-api2",
@@ -2601,7 +2789,7 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
- "hashbrown 0.14.2",
+ "hashbrown 0.14.3",
]
[[package]]
@@ -2623,6 +2811,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
+name = "hexf-parse"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
+
+[[package]]
name = "hkdf"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2878,7 +3072,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
- "hashbrown 0.14.2",
+ "hashbrown 0.14.3",
"serde",
]
@@ -2941,9 +3135,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "is-macro"
-version = "0.3.0"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4467ed1321b310c2625c5aa6c1b1ffc5de4d9e42668cf697a08fb033ee8265e"
+checksum = "bc74b7abae208af9314a406bd7dcc65091230b6e749c09e07a645885fecf34f9"
dependencies = [
"Inflector",
"pmutil",
@@ -2989,9 +3183,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.65"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
@@ -3030,6 +3224,23 @@ dependencies = [
]
[[package]]
+name = "khronos-egl"
+version = "6.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76"
+dependencies = [
+ "libc",
+ "libloading 0.8.1",
+ "pkg-config",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
name = "kqueue"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3117,6 +3328,16 @@ dependencies = [
]
[[package]]
+name = "libloading"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
+dependencies = [
+ "cfg-if",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
name = "libm"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3154,9 +3375,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
-version = "0.4.11"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
+checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "lock_api"
@@ -3200,6 +3421,15 @@ dependencies = [
]
[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "match_cfg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3261,6 +3491,21 @@ dependencies = [
]
[[package]]
+name = "metal"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25"
+dependencies = [
+ "bitflags 2.4.1",
+ "block",
+ "core-graphics-types",
+ "foreign-types",
+ "log",
+ "objc",
+ "paste",
+]
+
+[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3283,9 +3528,9 @@ dependencies = [
[[package]]
name = "mio"
-version = "0.8.9"
+version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0"
+checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09"
dependencies = [
"libc",
"log",
@@ -3306,6 +3551,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a"
[[package]]
+name = "naga"
+version = "0.14.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae585df4b6514cf8842ac0f1ab4992edc975892704835b549cf818dc0191249e"
+dependencies = [
+ "bit-set",
+ "bitflags 2.4.1",
+ "codespan-reporting",
+ "hexf-parse",
+ "indexmap 2.1.0",
+ "log",
+ "num-traits",
+ "rustc-hash",
+ "serde",
+ "spirv",
+ "termcolor",
+ "thiserror",
+ "unicode-xid",
+]
+
+[[package]]
name = "napi-build"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3317,7 +3583,7 @@ version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "529671ebfae679f2ce9630b62dd53c72c56b3eb8b2c852e7e2fa91704ff93d67"
dependencies = [
- "libloading",
+ "libloading 0.7.4",
]
[[package]]
@@ -3507,6 +3773,25 @@ dependencies = [
]
[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "object"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3526,9 +3811,9 @@ dependencies = [
[[package]]
name = "once_cell"
-version = "1.18.0"
+version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
+checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "opaque-debug"
@@ -3663,6 +3948,12 @@ dependencies = [
]
[[package]]
+name = "paste"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
+
+[[package]]
name = "path-clean"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3861,6 +4152,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
+name = "presser"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
+
+[[package]]
name = "pretty_assertions"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3938,14 +4235,20 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.69"
+version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
+checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
[[package]]
+name = "profiling"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f89dff0959d98c9758c88826cc002e2c3d0b9dfac4139711d1f30de442f1139b"
+
+[[package]]
name = "prost"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4105,19 +4408,22 @@ dependencies = [
]
[[package]]
-name = "redox_syscall"
-version = "0.2.16"
+name = "range-alloc"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
-dependencies = [
- "bitflags 1.3.2",
-]
+checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
[[package]]
name = "redox_syscall"
-version = "0.3.5"
+version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags 1.3.2",
]
@@ -4231,9 +4537,9 @@ dependencies = [
[[package]]
name = "ring"
-version = "0.17.5"
+version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b"
+checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom",
@@ -4253,10 +4559,22 @@ dependencies = [
]
[[package]]
+name = "ron"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
+dependencies = [
+ "base64 0.21.5",
+ "bitflags 2.4.1",
+ "serde",
+ "serde_derive",
+]
+
+[[package]]
name = "rsa"
-version = "0.9.3"
+version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d"
+checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc"
dependencies = [
"const-oid",
"digest",
@@ -4327,22 +4645,22 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.25"
+version = "0.38.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e"
+checksum = "bfeae074e687625746172d639330f1de242a178bf3189b51e35a7a21573513ac"
dependencies = [
"bitflags 2.4.1",
- "errno 0.3.7",
+ "errno 0.3.8",
"libc",
"linux-raw-sys",
- "windows-sys 0.48.0",
+ "windows-sys 0.52.0",
]
[[package]]
name = "rustls"
-version = "0.21.9"
+version = "0.21.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
+checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
dependencies = [
"log",
"ring",
@@ -4573,9 +4891,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
-version = "1.0.192"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
@@ -4601,9 +4919,9 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.192"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
@@ -4740,6 +5058,15 @@ dependencies = [
]
[[package]]
+name = "slotmap"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
name = "slug"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4831,10 +5158,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
[[package]]
+name = "spirv"
+version = "0.2.0+1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830"
+dependencies = [
+ "bitflags 1.3.2",
+ "num-traits",
+]
+
+[[package]]
name = "spki"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
@@ -5842,9 +6179,9 @@ dependencies = [
[[package]]
name = "try-lock"
-version = "0.2.4"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
+checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "twox-hash"
@@ -5921,9 +6258,9 @@ dependencies = [
[[package]]
name = "unicode-bidi"
-version = "0.3.13"
+version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416"
[[package]]
name = "unicode-id"
@@ -6118,9 +6455,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce"
+checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -6128,9 +6465,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217"
+checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
@@ -6143,9 +6480,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.38"
+version = "0.4.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02"
+checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
dependencies = [
"cfg-if",
"js-sys",
@@ -6155,9 +6492,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2"
+checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -6165,9 +6502,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907"
+checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
@@ -6178,9 +6515,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.88"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b"
+checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "wasm-streams"
@@ -6197,9 +6534,9 @@ dependencies = [
[[package]]
name = "web-sys"
-version = "0.3.65"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85"
+checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -6207,9 +6544,87 @@ dependencies = [
[[package]]
name = "webpki-roots"
-version = "0.25.2"
+version = "0.25.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
+
+[[package]]
+name = "wgpu-core"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef91c1d62d1e9e81c79e600131a258edf75c9531cbdbde09c44a011a47312726"
+dependencies = [
+ "arrayvec",
+ "bit-vec",
+ "bitflags 2.4.1",
+ "codespan-reporting",
+ "log",
+ "naga",
+ "parking_lot 0.12.1",
+ "profiling",
+ "raw-window-handle",
+ "ron",
+ "rustc-hash",
+ "serde",
+ "smallvec",
+ "thiserror",
+ "web-sys",
+ "wgpu-hal",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-hal"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b84ecc802da3eb67b4cf3dd9ea6fe45bbb47ef13e6c49c5c3240868a9cc6cdd9"
+dependencies = [
+ "android_system_properties",
+ "arrayvec",
+ "ash",
+ "bit-set",
+ "bitflags 2.4.1",
+ "block",
+ "core-graphics-types",
+ "d3d12",
+ "glow",
+ "glutin_wgl_sys",
+ "gpu-alloc",
+ "gpu-allocator",
+ "gpu-descriptor",
+ "js-sys",
+ "khronos-egl",
+ "libc",
+ "libloading 0.8.1",
+ "log",
+ "metal",
+ "naga",
+ "objc",
+ "once_cell",
+ "parking_lot 0.12.1",
+ "profiling",
+ "range-alloc",
+ "raw-window-handle",
+ "rustc-hash",
+ "smallvec",
+ "thiserror",
+ "wasm-bindgen",
+ "web-sys",
+ "wgpu-types",
+ "winapi",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
+checksum = "0d5ed5f0edf0de351fe311c53304986315ce866f394a2e6df0c4b3c70774bcdd"
+dependencies = [
+ "bitflags 2.4.1",
+ "js-sys",
+ "serde",
+ "web-sys",
+]
[[package]]
name = "which"
@@ -6271,6 +6686,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
+name = "windows"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9"
+dependencies = [
+ "windows-core",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.51.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
+[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6460,6 +6894,12 @@ dependencies = [
]
[[package]]
+name = "xml-rs"
+version = "0.8.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fcb9cbac069e033553e8bb871be2fbdffcab578eb25bd0f7c508cedc6dcd75a"
+
+[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6467,18 +6907,18 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zerocopy"
-version = "0.7.26"
+version = "0.7.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0"
+checksum = "5d075cf85bbb114e933343e087b92f2146bac0d55b534cbb8188becf0039948e"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.7.26"
+version = "0.7.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f"
+checksum = "86cd5ca076997b97ef09d3ad65efe811fa68c9e874cb636ccb211223a813b0c2"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index f3a02cd5e..555fdf7cb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -25,6 +25,7 @@ members = [
"ext/node",
"ext/url",
"ext/web",
+ "ext/webgpu",
"ext/webidl",
"ext/websocket",
"ext/webstorage",
@@ -71,6 +72,7 @@ deno_kv = { version = "0.35.0", path = "./ext/kv" }
deno_tls = { version = "0.114.0", path = "./ext/tls" }
deno_url = { version = "0.127.0", path = "./ext/url" }
deno_web = { version = "0.158.0", path = "./ext/web" }
+deno_webgpu = { version = "0.94.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.127.0", path = "./ext/webidl" }
deno_websocket = { version = "0.132.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.122.0", path = "./ext/webstorage" }
@@ -164,6 +166,12 @@ p384 = { version = "0.13.0", features = ["ecdh"] }
rsa = { version = "0.9.3", default-features = false, features = ["std", "pem", "hazmat"] } # hazmat needed for PrehashSigner in ext/node
hkdf = "0.12.3"
+# webgpu
+raw-window-handle = "0.5.0"
+wgpu-core = "=0.18"
+wgpu-types = "=0.18"
+wgpu-hal = "=0.18"
+
# macros
proc-macro2 = "1"
quote = "1"
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index 9d7e513f7..8b4fa445e 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -815,6 +815,7 @@ static ENV_VARIABLES_HELP: &str = r#"ENVIRONMENT VARIABLES:
DENO_NO_UPDATE_CHECK Set to disable checking if a newer Deno version is
available
DENO_V8_FLAGS Set V8 command line options
+ DENO_WEBGPU_TRACE Directory to use for wgpu traces
DENO_JOBS Number of parallel workers used for the --parallel
flag with the test subcommand. Defaults to number
of available CPUs.
diff --git a/cli/build.rs b/cli/build.rs
index d7f373b99..4adeba944 100644
--- a/cli/build.rs
+++ b/cli/build.rs
@@ -149,6 +149,7 @@ mod ts {
op_crate_libs.insert("deno.url", deno_url::get_declaration());
op_crate_libs.insert("deno.web", deno_web::get_declaration());
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
+ op_crate_libs.insert("deno.webgpu", deno_webgpu_get_declaration());
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
op_crate_libs.insert("deno.webstorage", deno_webstorage::get_declaration());
op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration());
@@ -458,3 +459,11 @@ fn main() {
res.compile().unwrap();
}
}
+
+fn deno_webgpu_get_declaration() -> PathBuf {
+ let manifest_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"));
+ manifest_dir
+ .join("tsc")
+ .join("dts")
+ .join("lib.deno_webgpu.d.ts")
+}
diff --git a/cli/js/40_testing.js b/cli/js/40_testing.js
index 5b51ac169..5ceff938c 100644
--- a/cli/js/40_testing.js
+++ b/cli/js/40_testing.js
@@ -142,7 +142,10 @@ const OP_DETAILS = {
"op_utime_async": ["change file timestamps", "awaiting the result of a `Deno.utime` call"],
"op_host_recv_message": ["receive a message from a web worker", "terminating a `Worker`"],
"op_host_recv_ctrl": ["receive a message from a web worker", "terminating a `Worker`"],
- "op_ws_close": ["close a WebSocket", "awaiting until the `close` event is emitted on a `WebSocket`, or the `WebSocketStream#closed` promise resolves"],
+ "op_webgpu_buffer_get_map_async": ["map a WebGPU buffer", "awaiting the result of a `GPUBuffer#mapAsync` call"],
+ "op_webgpu_request_adapter": ["request a WebGPU adapter", "awaiting the result of a `navigator.gpu.requestAdapter` call"],
+ "op_webgpu_request_device": ["request a WebGPU device", "awaiting the result of a `GPUAdapter#requestDevice` call"],
+ "op_ws_close": ["close a WebSocket", "awaiting until the `close` event is emitted on a `WebSocket`, or the `WebSocketStream#closed` promise resolves"],
"op_ws_create": ["create a WebSocket", "awaiting until the `open` event is emitted on a `WebSocket`, or the result of a `WebSocketStream#connection` promise"],
"op_ws_next_event": ["receive the next message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
"op_ws_send_text": ["send a message on a WebSocket", "closing a `WebSocket` or `WebSocketStream`"],
diff --git a/cli/main.rs b/cli/main.rs
index c95e5dc37..321f32976 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -323,9 +323,14 @@ pub(crate) static UNSTABLE_GRANULAR_FLAGS: &[(
8,
),
(
+ deno_runtime::deno_webgpu::UNSTABLE_FEATURE_NAME,
+ "Enable unstable `WebGPU` API",
+ 9,
+ ),
+ (
deno_runtime::ops::worker_host::UNSTABLE_FEATURE_NAME,
"Enable unstable Web Worker APIs",
- 9,
+ 10,
),
];
diff --git a/cli/tests/integration/js_unit_tests.rs b/cli/tests/integration/js_unit_tests.rs
index 165ab25bf..10bd137d9 100644
--- a/cli/tests/integration/js_unit_tests.rs
+++ b/cli/tests/integration/js_unit_tests.rs
@@ -101,6 +101,7 @@ util::unit_test_factory!(
version_test,
wasm_test,
webcrypto_test,
+ webgpu_test,
websocket_test,
webstorage_test,
worker_permissions_test,
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index c96d68b93..32df04483 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -1660,6 +1660,17 @@ itest!(unstable_kv_enabled {
output: "run/unstable_kv.enabled.out",
});
+itest!(unstable_webgpu_disabled {
+ args: "run --quiet --reload --allow-read run/unstable_webgpu.js",
+ output: "run/unstable_webgpu.disabled.out",
+});
+
+itest!(unstable_webgpu_enabled {
+ args:
+ "run --quiet --reload --allow-read --unstable-webgpu run/unstable_webgpu.js",
+ output: "run/unstable_webgpu.enabled.out",
+});
+
itest!(import_compression {
args: "run --quiet --reload --allow-net run/import_compression/main.ts",
output: "run/import_compression/main.out",
diff --git a/cli/tests/integration/shared_library_tests.rs b/cli/tests/integration/shared_library_tests.rs
index 506c537f6..3e05f8efc 100644
--- a/cli/tests/integration/shared_library_tests.rs
+++ b/cli/tests/integration/shared_library_tests.rs
@@ -45,14 +45,25 @@ fn macos_shared_libraries() {
// target/release/deno:
// /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1953.1.0)
// /System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices (compatibility version 1.0.0, current version 1228.0.0)
+ // /System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore (compatibility version 1.2.0, current version 1.11.0, weak)
+ // /System/Library/Frameworks/Metal.framework/Versions/A/Metal (compatibility version 1.0.0, current version 341.16.0, weak)
+ // /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics (compatibility version 64.0.0, current version 1774.0.4, weak)
+ // /System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders (compatibility version 1.0.0, current version 127.0.19, weak)
// /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
// /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
- const EXPECTED: [&str; 5] = [
- "/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation",
- "/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices",
- "/usr/lib/libiconv.2.dylib",
- "/usr/lib/libSystem.B.dylib",
- "/usr/lib/libobjc.A.dylib",
+ // /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
+
+ // path and whether its weak or not
+ const EXPECTED: [(&str, bool); 9] = [
+ ("/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation", false),
+ ("/System/Library/Frameworks/CoreServices.framework/Versions/A/CoreServices", false),
+ ("/System/Library/Frameworks/QuartzCore.framework/Versions/A/QuartzCore", true),
+ ("/System/Library/Frameworks/Metal.framework/Versions/A/Metal", true),
+ ("/System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics", true),
+ ("/System/Library/Frameworks/MetalPerformanceShaders.framework/Versions/A/MetalPerformanceShaders", true),
+ ("/usr/lib/libiconv.2.dylib", false),
+ ("/usr/lib/libSystem.B.dylib", false),
+ ("/usr/lib/libobjc.A.dylib", false),
];
let otool = std::process::Command::new("otool")
@@ -64,9 +75,9 @@ fn macos_shared_libraries() {
let output = std::str::from_utf8(&otool.stdout).unwrap();
// Ensure that the output contains only the expected shared libraries.
for line in output.lines().skip(1) {
- let path = line.split_whitespace().next().unwrap();
+ let (path, attributes) = line.trim().split_once(' ').unwrap();
assert!(
- EXPECTED.contains(&path),
+ EXPECTED.contains(&(path, attributes.ends_with("weak)"))),
"Unexpected shared library: {}",
path
);
diff --git a/cli/tests/testdata/run/unstable_webgpu.disabled.out b/cli/tests/testdata/run/unstable_webgpu.disabled.out
new file mode 100644
index 000000000..775866352
--- /dev/null
+++ b/cli/tests/testdata/run/unstable_webgpu.disabled.out
@@ -0,0 +1,2 @@
+main undefined
+worker undefined
diff --git a/cli/tests/testdata/run/unstable_webgpu.enabled.out b/cli/tests/testdata/run/unstable_webgpu.enabled.out
new file mode 100644
index 000000000..e2cc915ba
--- /dev/null
+++ b/cli/tests/testdata/run/unstable_webgpu.enabled.out
@@ -0,0 +1,2 @@
+main [class GPU]
+worker [class GPU]
diff --git a/cli/tests/testdata/run/unstable_webgpu.js b/cli/tests/testdata/run/unstable_webgpu.js
new file mode 100644
index 000000000..a796b1c4d
--- /dev/null
+++ b/cli/tests/testdata/run/unstable_webgpu.js
@@ -0,0 +1,10 @@
+const scope = import.meta.url.slice(-7) === "#worker" ? "worker" : "main";
+
+console.log(scope, globalThis.GPU);
+
+if (scope === "worker") {
+ postMessage("done");
+} else {
+ const worker = new Worker(`${import.meta.url}#worker`, { type: "module" });
+ worker.onmessage = () => Deno.exit(0);
+}
diff --git a/cli/tests/testdata/webgpu/computepass_shader.wgsl b/cli/tests/testdata/webgpu/computepass_shader.wgsl
new file mode 100644
index 000000000..41af4363a
--- /dev/null
+++ b/cli/tests/testdata/webgpu/computepass_shader.wgsl
@@ -0,0 +1,38 @@
+@group(0)
+@binding(0)
+var<storage, read_write> v_indices: array<u32>; // this is used as both input and output for convenience
+
+// The Collatz Conjecture states that for any integer n:
+// If n is even, n = n/2
+// If n is odd, n = 3n+1
+// And repeat this process for each new n, you will always eventually reach 1.
+// Though the conjecture has not been proven, no counterexample has ever been found.
+// This function returns how many times this recurrence needs to be applied to reach 1.
+fn collatz_iterations(n_base: u32) -> u32{
+ var n: u32 = n_base;
+ var i: u32 = 0u;
+ loop {
+ if (n <= 1u) {
+ break;
+ }
+ if (n % 2u == 0u) {
+ n = n / 2u;
+ }
+ else {
+ // Overflow? (i.e. 3*n + 1 > 0xffffffffu?)
+ if (n >= 1431655765u) { // 0x55555555u
+ return 4294967295u; // 0xffffffffu
+ }
+
+ n = 3u * n + 1u;
+ }
+ i = i + 1u;
+ }
+ return i;
+}
+
+@compute
+@workgroup_size(1)
+fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
+ v_indices[global_id.x] = collatz_iterations(v_indices[global_id.x]);
+}
diff --git a/cli/tests/testdata/webgpu/hellotriangle.out b/cli/tests/testdata/webgpu/hellotriangle.out
new file mode 100644
index 000000000..52972ec9e
--- /dev/null
+++ b/cli/tests/testdata/webgpu/hellotriangle.out
Binary files differ
diff --git a/cli/tests/testdata/webgpu/hellotriangle_shader.wgsl b/cli/tests/testdata/webgpu/hellotriangle_shader.wgsl
new file mode 100644
index 000000000..f84ccfe94
--- /dev/null
+++ b/cli/tests/testdata/webgpu/hellotriangle_shader.wgsl
@@ -0,0 +1,11 @@
+@vertex
+fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> {
+ let x = f32(i32(in_vertex_index) - 1);
+ let y = f32(i32(in_vertex_index & 1u) * 2 - 1);
+ return vec4<f32>(x, y, 0.0, 1.0);
+}
+
+@fragment
+fn fs_main() -> @location(0) vec4<f32> {
+ return vec4<f32>(1.0, 0.0, 0.0, 1.0);
+}
diff --git a/cli/tests/unit/webgpu_test.ts b/cli/tests/unit/webgpu_test.ts
new file mode 100644
index 000000000..2d98167cf
--- /dev/null
+++ b/cli/tests/unit/webgpu_test.ts
@@ -0,0 +1,242 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+import { assert, assertEquals } from "./test_util.ts";
+
+let isCI: boolean;
+try {
+ isCI = (Deno.env.get("CI")?.length ?? 0) > 0;
+} catch {
+ isCI = true;
+}
+
+// Skip these tests on linux CI, because the vulkan emulator is not good enough
+// yet, and skip on macOS CI because these do not have virtual GPUs.
+const isLinuxOrMacCI =
+ (Deno.build.os === "linux" || Deno.build.os === "darwin") && isCI;
+// Skip these tests in WSL because it doesn't have good GPU support.
+const isWsl = await checkIsWsl();
+
+Deno.test({
+ permissions: { read: true, env: true },
+ ignore: isWsl || isLinuxOrMacCI,
+}, async function webgpuComputePass() {
+ const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter);
+
+ const numbers = [1, 4, 3, 295];
+
+ const device = await adapter.requestDevice();
+ assert(device);
+
+ const shaderCode = await Deno.readTextFile(
+ "cli/tests/testdata/webgpu/computepass_shader.wgsl",
+ );
+
+ const shaderModule = device.createShaderModule({
+ code: shaderCode,
+ });
+
+ const size = new Uint32Array(numbers).byteLength;
+
+ const stagingBuffer = device.createBuffer({
+ size: size,
+ usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
+ });
+
+ const storageBuffer = device.createBuffer({
+ label: "Storage Buffer",
+ size: size,
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST |
+ GPUBufferUsage.COPY_SRC,
+ mappedAtCreation: true,
+ });
+
+ const buf = new Uint32Array(storageBuffer.getMappedRange());
+
+ buf.set(numbers);
+
+ storageBuffer.unmap();
+
+ const computePipeline = device.createComputePipeline({
+ layout: "auto",
+ compute: {
+ module: shaderModule,
+ entryPoint: "main",
+ },
+ });
+ const bindGroupLayout = computePipeline.getBindGroupLayout(0);
+
+ const bindGroup = device.createBindGroup({
+ layout: bindGroupLayout,
+ entries: [
+ {
+ binding: 0,
+ resource: {
+ buffer: storageBuffer,
+ },
+ },
+ ],
+ });
+
+ const encoder = device.createCommandEncoder();
+
+ const computePass = encoder.beginComputePass();
+ computePass.setPipeline(computePipeline);
+ computePass.setBindGroup(0, bindGroup);
+ computePass.insertDebugMarker("compute collatz iterations");
+ computePass.dispatchWorkgroups(numbers.length);
+ computePass.end();
+
+ encoder.copyBufferToBuffer(storageBuffer, 0, stagingBuffer, 0, size);
+
+ device.queue.submit([encoder.finish()]);
+
+ await stagingBuffer.mapAsync(1);
+
+ const data = stagingBuffer.getMappedRange();
+
+ assertEquals(new Uint32Array(data), new Uint32Array([0, 2, 7, 55]));
+
+ stagingBuffer.unmap();
+
+ device.destroy();
+
+ // TODO(lucacasonato): webgpu spec should add a explicit destroy method for
+ // adapters.
+ const resources = Object.keys(Deno.resources());
+ Deno.close(Number(resources[resources.length - 1]));
+});
+
+Deno.test({
+ permissions: { read: true, env: true },
+ ignore: isWsl || isLinuxOrMacCI,
+}, async function webgpuHelloTriangle() {
+ const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter);
+
+ const device = await adapter.requestDevice();
+ assert(device);
+
+ const shaderCode = await Deno.readTextFile(
+ "cli/tests/testdata/webgpu/hellotriangle_shader.wgsl",
+ );
+
+ const shaderModule = device.createShaderModule({
+ code: shaderCode,
+ });
+
+ const pipelineLayout = device.createPipelineLayout({
+ bindGroupLayouts: [],
+ });
+
+ const renderPipeline = device.createRenderPipeline({
+ layout: pipelineLayout,
+ vertex: {
+ module: shaderModule,
+ entryPoint: "vs_main",
+ },
+ fragment: {
+ module: shaderModule,
+ entryPoint: "fs_main",
+ targets: [
+ {
+ format: "rgba8unorm-srgb",
+ },
+ ],
+ },
+ });
+
+ const dimensions = {
+ width: 200,
+ height: 200,
+ };
+ const unpaddedBytesPerRow = dimensions.width * 4;
+ const align = 256;
+ const paddedBytesPerRowPadding = (align - unpaddedBytesPerRow % align) %
+ align;
+ const paddedBytesPerRow = unpaddedBytesPerRow + paddedBytesPerRowPadding;
+
+ const outputBuffer = device.createBuffer({
+ label: "Capture",
+ size: paddedBytesPerRow * dimensions.height,
+ usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
+ });
+ const texture = device.createTexture({
+ label: "Capture",
+ size: dimensions,
+ format: "rgba8unorm-srgb",
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
+ });
+
+ const encoder = device.createCommandEncoder();
+ const view = texture.createView();
+ const renderPass = encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view,
+ storeOp: "store",
+ loadOp: "clear",
+ clearValue: [0, 1, 0, 1],
+ },
+ ],
+ });
+ renderPass.setPipeline(renderPipeline);
+ renderPass.draw(3, 1);
+ renderPass.end();
+
+ encoder.copyTextureToBuffer(
+ {
+ texture,
+ },
+ {
+ buffer: outputBuffer,
+ bytesPerRow: paddedBytesPerRow,
+ rowsPerImage: 0,
+ },
+ dimensions,
+ );
+
+ const bundle = encoder.finish();
+ device.queue.submit([bundle]);
+
+ await outputBuffer.mapAsync(1);
+ const data = new Uint8Array(outputBuffer.getMappedRange());
+
+ assertEquals(
+ data,
+ await Deno.readFile("cli/tests/testdata/webgpu/hellotriangle.out"),
+ );
+
+ outputBuffer.unmap();
+
+ device.destroy();
+
+ // TODO(lucacasonato): webgpu spec should add a explicit destroy method for
+ // adapters.
+ const resources = Object.keys(Deno.resources());
+ Deno.close(Number(resources[resources.length - 1]));
+});
+
+Deno.test({
+ ignore: isWsl || isLinuxOrMacCI,
+}, async function webgpuAdapterHasFeatures() {
+ const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter);
+ assert(adapter.features);
+ const resources = Object.keys(Deno.resources());
+ Deno.close(Number(resources[resources.length - 1]));
+});
+
+async function checkIsWsl() {
+ return Deno.build.os === "linux" && await hasMicrosoftProcVersion();
+
+ async function hasMicrosoftProcVersion() {
+ // https://github.com/microsoft/WSL/issues/423#issuecomment-221627364
+ try {
+ const procVersion = await Deno.readTextFile("/proc/version");
+ return /microsoft/i.test(procVersion);
+ } catch {
+ return false;
+ }
+ }
+}
diff --git a/cli/tsc/dts/lib.deno.window.d.ts b/cli/tsc/dts/lib.deno.window.d.ts
index 58b57e52c..0e6acf1e7 100644
--- a/cli/tsc/dts/lib.deno.window.d.ts
+++ b/cli/tsc/dts/lib.deno.window.d.ts
@@ -3,6 +3,7 @@
/// <reference no-default-lib="true" />
/// <reference lib="deno.ns" />
/// <reference lib="deno.shared_globals" />
+/// <reference lib="deno.webgpu" />
/// <reference lib="deno.webstorage" />
/// <reference lib="esnext" />
/// <reference lib="deno.cache" />
@@ -102,6 +103,7 @@ declare var caches: CacheStorage;
/** @category Web APIs */
declare interface Navigator {
+ readonly gpu: GPU;
readonly hardwareConcurrency: number;
readonly userAgent: string;
readonly language: string;
diff --git a/cli/tsc/dts/lib.deno.worker.d.ts b/cli/tsc/dts/lib.deno.worker.d.ts
index b165f2086..1d95dd483 100644
--- a/cli/tsc/dts/lib.deno.worker.d.ts
+++ b/cli/tsc/dts/lib.deno.worker.d.ts
@@ -62,6 +62,7 @@ declare var WorkerGlobalScope: {
/** @category Web APIs */
declare interface WorkerNavigator {
+ readonly gpu: GPU;
readonly hardwareConcurrency: number;
readonly userAgent: string;
readonly language: string;
diff --git a/cli/tsc/dts/lib.deno_webgpu.d.ts b/cli/tsc/dts/lib.deno_webgpu.d.ts
new file mode 100644
index 000000000..9545fdc9e
--- /dev/null
+++ b/cli/tsc/dts/lib.deno_webgpu.d.ts
@@ -0,0 +1,1315 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// deno-lint-ignore-file no-explicit-any no-empty-interface
+
+/// <reference no-default-lib="true" />
+/// <reference lib="esnext" />
+
+/** @category WebGPU */
+interface GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUObjectDescriptorBase {
+ label?: string;
+}
+
+/** @category WebGPU */
+declare class GPUSupportedLimits {
+ maxTextureDimension1D?: number;
+ maxTextureDimension2D?: number;
+ maxTextureDimension3D?: number;
+ maxTextureArrayLayers?: number;
+ maxBindGroups?: number;
+ maxBindingsPerBindGroup?: number;
+ maxDynamicUniformBuffersPerPipelineLayout?: number;
+ maxDynamicStorageBuffersPerPipelineLayout?: number;
+ maxSampledTexturesPerShaderStage?: number;
+ maxSamplersPerShaderStage?: number;
+ maxStorageBuffersPerShaderStage?: number;
+ maxStorageTexturesPerShaderStage?: number;
+ maxUniformBuffersPerShaderStage?: number;
+ maxUniformBufferBindingSize?: number;
+ maxStorageBufferBindingSize?: number;
+ minUniformBufferOffsetAlignment?: number;
+ minStorageBufferOffsetAlignment?: number;
+ maxVertexBuffers?: number;
+ maxBufferSize?: number;
+ maxVertexAttributes?: number;
+ maxVertexBufferArrayStride?: number;
+ maxInterStageShaderComponents?: number;
+ maxComputeWorkgroupStorageSize?: number;
+ maxComputeInvocationsPerWorkgroup?: number;
+ maxComputeWorkgroupSizeX?: number;
+ maxComputeWorkgroupSizeY?: number;
+ maxComputeWorkgroupSizeZ?: number;
+ maxComputeWorkgroupsPerDimension?: number;
+}
+
+/** @category WebGPU */
+declare class GPUSupportedFeatures {
+ forEach(
+ callbackfn: (
+ value: GPUFeatureName,
+ value2: GPUFeatureName,
+ set: Set<GPUFeatureName>,
+ ) => void,
+ thisArg?: any,
+ ): void;
+ has(value: GPUFeatureName): boolean;
+ size: number;
+ [Symbol.iterator](): IterableIterator<GPUFeatureName>;
+ entries(): IterableIterator<[GPUFeatureName, GPUFeatureName]>;
+ keys(): IterableIterator<GPUFeatureName>;
+ values(): IterableIterator<GPUFeatureName>;
+}
+
+/** @category WebGPU */
+declare class GPUAdapterInfo {
+ readonly vendor: string;
+ readonly architecture: string;
+ readonly device: string;
+ readonly description: string;
+}
+
+/** @category WebGPU */
+declare class GPU {
+ requestAdapter(
+ options?: GPURequestAdapterOptions,
+ ): Promise<GPUAdapter | null>;
+}
+
+/** @category WebGPU */
+declare interface GPURequestAdapterOptions {
+ powerPreference?: GPUPowerPreference;
+ forceFallbackAdapter?: boolean;
+}
+
+/** @category WebGPU */
+declare type GPUPowerPreference = "low-power" | "high-performance";
+
+/** @category WebGPU */
+declare class GPUAdapter {
+ readonly features: GPUSupportedFeatures;
+ readonly limits: GPUSupportedLimits;
+ readonly isFallbackAdapter: boolean;
+
+ requestDevice(descriptor?: GPUDeviceDescriptor): Promise<GPUDevice>;
+ requestAdapterInfo(unmaskHints?: string[]): Promise<GPUAdapterInfo>;
+}
+
+/** @category WebGPU */
+declare interface GPUDeviceDescriptor extends GPUObjectDescriptorBase {
+ requiredFeatures?: GPUFeatureName[];
+ requiredLimits?: Record<string, number>;
+}
+
+/** @category WebGPU */
+declare type GPUFeatureName =
+ | "depth-clip-control"
+ | "depth32float-stencil8"
+ | "pipeline-statistics-query"
+ | "texture-compression-bc"
+ | "texture-compression-etc2"
+ | "texture-compression-astc"
+ | "timestamp-query"
+ | "indirect-first-instance"
+ | "shader-f16"
+ // extended from spec
+ | "mappable-primary-buffers"
+ | "sampled-texture-binding-array"
+ | "sampled-texture-array-dynamic-indexing"
+ | "sampled-texture-array-non-uniform-indexing"
+ | "unsized-binding-array"
+ | "multi-draw-indirect"
+ | "multi-draw-indirect-count"
+ | "push-constants"
+ | "address-mode-clamp-to-border"
+ | "texture-adapter-specific-format-features"
+ | "shader-float64"
+ | "vertex-attribute-64bit";
+
+/** @category WebGPU */
+declare class GPUDevice extends EventTarget implements GPUObjectBase {
+ label: string;
+
+ readonly lost: Promise<GPUDeviceLostInfo>;
+ pushErrorScope(filter: GPUErrorFilter): undefined;
+ popErrorScope(): Promise<GPUError | null>;
+
+ readonly features: GPUSupportedFeatures;
+ readonly limits: GPUSupportedLimits;
+ readonly queue: GPUQueue;
+
+ destroy(): undefined;
+
+ createBuffer(descriptor: GPUBufferDescriptor): GPUBuffer;
+ createTexture(descriptor: GPUTextureDescriptor): GPUTexture;
+ createSampler(descriptor?: GPUSamplerDescriptor): GPUSampler;
+
+ createBindGroupLayout(
+ descriptor: GPUBindGroupLayoutDescriptor,
+ ): GPUBindGroupLayout;
+ createPipelineLayout(
+ descriptor: GPUPipelineLayoutDescriptor,
+ ): GPUPipelineLayout;
+ createBindGroup(descriptor: GPUBindGroupDescriptor): GPUBindGroup;
+
+ createShaderModule(descriptor: GPUShaderModuleDescriptor): GPUShaderModule;
+ createComputePipeline(
+ descriptor: GPUComputePipelineDescriptor,
+ ): GPUComputePipeline;
+ createRenderPipeline(
+ descriptor: GPURenderPipelineDescriptor,
+ ): GPURenderPipeline;
+ createComputePipelineAsync(
+ descriptor: GPUComputePipelineDescriptor,
+ ): Promise<GPUComputePipeline>;
+ createRenderPipelineAsync(
+ descriptor: GPURenderPipelineDescriptor,
+ ): Promise<GPURenderPipeline>;
+
+ createCommandEncoder(
+ descriptor?: GPUCommandEncoderDescriptor,
+ ): GPUCommandEncoder;
+ createRenderBundleEncoder(
+ descriptor: GPURenderBundleEncoderDescriptor,
+ ): GPURenderBundleEncoder;
+
+ createQuerySet(descriptor: GPUQuerySetDescriptor): GPUQuerySet;
+}
+
+/** @category WebGPU */
+declare class GPUBuffer implements GPUObjectBase {
+ label: string;
+
+ readonly size: number;
+ readonly usage: GPUFlagsConstant;
+ readonly mapState: GPUBufferMapState;
+
+ mapAsync(
+ mode: GPUMapModeFlags,
+ offset?: number,
+ size?: number,
+ ): Promise<undefined>;
+ getMappedRange(offset?: number, size?: number): ArrayBuffer;
+ unmap(): undefined;
+
+ destroy(): undefined;
+}
+
+/** @category WebGPU */
+declare type GPUBufferMapState = "unmapped" | "pending" | "mapped";
+
+/** @category WebGPU */
+declare interface GPUBufferDescriptor extends GPUObjectDescriptorBase {
+ size: number;
+ usage: GPUBufferUsageFlags;
+ mappedAtCreation?: boolean;
+}
+
+/** @category WebGPU */
+declare type GPUBufferUsageFlags = number;
+
+/** @category WebGPU */
+declare type GPUFlagsConstant = number;
+
+/** @category WebGPU */
+declare class GPUBufferUsage {
+ static MAP_READ: 0x0001;
+ static MAP_WRITE: 0x0002;
+ static COPY_SRC: 0x0004;
+ static COPY_DST: 0x0008;
+ static INDEX: 0x0010;
+ static VERTEX: 0x0020;
+ static UNIFORM: 0x0040;
+ static STORAGE: 0x0080;
+ static INDIRECT: 0x0100;
+ static QUERY_RESOLVE: 0x0200;
+}
+
+/** @category WebGPU */
+declare type GPUMapModeFlags = number;
+
+/** @category WebGPU */
+declare class GPUMapMode {
+ static READ: 0x0001;
+ static WRITE: 0x0002;
+}
+
+/** @category WebGPU */
+declare class GPUTexture implements GPUObjectBase {
+ label: string;
+
+ createView(descriptor?: GPUTextureViewDescriptor): GPUTextureView;
+ destroy(): undefined;
+
+ readonly width: number;
+ readonly height: number;
+ readonly depthOrArrayLayers: number;
+ readonly mipLevelCount: number;
+ readonly sampleCount: number;
+ readonly dimension: GPUTextureDimension;
+ readonly format: GPUTextureFormat;
+ readonly usage: GPUFlagsConstant;
+}
+
+/** @category WebGPU */
+declare interface GPUTextureDescriptor extends GPUObjectDescriptorBase {
+ size: GPUExtent3D;
+ mipLevelCount?: number;
+ sampleCount?: number;
+ dimension?: GPUTextureDimension;
+ format: GPUTextureFormat;
+ usage: GPUTextureUsageFlags;
+ viewFormats?: GPUTextureFormat[];
+}
+
+/** @category WebGPU */
+declare type GPUTextureDimension = "1d" | "2d" | "3d";
+
+/** @category WebGPU */
+declare type GPUTextureUsageFlags = number;
+
+/** @category WebGPU */
+declare class GPUTextureUsage {
+ static COPY_SRC: 0x01;
+ static COPY_DST: 0x02;
+ static TEXTURE_BINDING: 0x04;
+ static STORAGE_BINDING: 0x08;
+ static RENDER_ATTACHMENT: 0x10;
+}
+
+/** @category WebGPU */
+declare class GPUTextureView implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUTextureViewDescriptor extends GPUObjectDescriptorBase {
+ format?: GPUTextureFormat;
+ dimension?: GPUTextureViewDimension;
+ aspect?: GPUTextureAspect;
+ baseMipLevel?: number;
+ mipLevelCount?: number;
+ baseArrayLayer?: number;
+ arrayLayerCount?: number;
+}
+
+/** @category WebGPU */
+declare type GPUTextureViewDimension =
+ | "1d"
+ | "2d"
+ | "2d-array"
+ | "cube"
+ | "cube-array"
+ | "3d";
+
+/** @category WebGPU */
+declare type GPUTextureAspect = "all" | "stencil-only" | "depth-only";
+
+/** @category WebGPU */
+declare type GPUTextureFormat =
+ | "r8unorm"
+ | "r8snorm"
+ | "r8uint"
+ | "r8sint"
+ | "r16uint"
+ | "r16sint"
+ | "r16float"
+ | "rg8unorm"
+ | "rg8snorm"
+ | "rg8uint"
+ | "rg8sint"
+ | "r32uint"
+ | "r32sint"
+ | "r32float"
+ | "rg16uint"
+ | "rg16sint"
+ | "rg16float"
+ | "rgba8unorm"
+ | "rgba8unorm-srgb"
+ | "rgba8snorm"
+ | "rgba8uint"
+ | "rgba8sint"
+ | "bgra8unorm"
+ | "bgra8unorm-srgb"
+ | "rgb9e5ufloat"
+ | "rgb10a2unorm"
+ | "rg11b10ufloat"
+ | "rg32uint"
+ | "rg32sint"
+ | "rg32float"
+ | "rgba16uint"
+ | "rgba16sint"
+ | "rgba16float"
+ | "rgba32uint"
+ | "rgba32sint"
+ | "rgba32float"
+ | "stencil8"
+ | "depth16unorm"
+ | "depth24plus"
+ | "depth24plus-stencil8"
+ | "depth32float"
+ | "depth32float-stencil8"
+ | "bc1-rgba-unorm"
+ | "bc1-rgba-unorm-srgb"
+ | "bc2-rgba-unorm"
+ | "bc2-rgba-unorm-srgb"
+ | "bc3-rgba-unorm"
+ | "bc3-rgba-unorm-srgb"
+ | "bc4-r-unorm"
+ | "bc4-r-snorm"
+ | "bc5-rg-unorm"
+ | "bc5-rg-snorm"
+ | "bc6h-rgb-ufloat"
+ | "bc6h-rgb-float"
+ | "bc7-rgba-unorm"
+ | "bc7-rgba-unorm-srgb"
+ | "etc2-rgb8unorm"
+ | "etc2-rgb8unorm-srgb"
+ | "etc2-rgb8a1unorm"
+ | "etc2-rgb8a1unorm-srgb"
+ | "etc2-rgba8unorm"
+ | "etc2-rgba8unorm-srgb"
+ | "eac-r11unorm"
+ | "eac-r11snorm"
+ | "eac-rg11unorm"
+ | "eac-rg11snorm"
+ | "astc-4x4-unorm"
+ | "astc-4x4-unorm-srgb"
+ | "astc-5x4-unorm"
+ | "astc-5x4-unorm-srgb"
+ | "astc-5x5-unorm"
+ | "astc-5x5-unorm-srgb"
+ | "astc-6x5-unorm"
+ | "astc-6x5-unorm-srgb"
+ | "astc-6x6-unorm"
+ | "astc-6x6-unorm-srgb"
+ | "astc-8x5-unorm"
+ | "astc-8x5-unorm-srgb"
+ | "astc-8x6-unorm"
+ | "astc-8x6-unorm-srgb"
+ | "astc-8x8-unorm"
+ | "astc-8x8-unorm-srgb"
+ | "astc-10x5-unorm"
+ | "astc-10x5-unorm-srgb"
+ | "astc-10x6-unorm"
+ | "astc-10x6-unorm-srgb"
+ | "astc-10x8-unorm"
+ | "astc-10x8-unorm-srgb"
+ | "astc-10x10-unorm"
+ | "astc-10x10-unorm-srgb"
+ | "astc-12x10-unorm"
+ | "astc-12x10-unorm-srgb"
+ | "astc-12x12-unorm"
+ | "astc-12x12-unorm-srgb";
+
+/** @category WebGPU */
+declare class GPUSampler implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUSamplerDescriptor extends GPUObjectDescriptorBase {
+ addressModeU?: GPUAddressMode;
+ addressModeV?: GPUAddressMode;
+ addressModeW?: GPUAddressMode;
+ magFilter?: GPUFilterMode;
+ minFilter?: GPUFilterMode;
+ mipmapFilter?: GPUMipmapFilterMode;
+ lodMinClamp?: number;
+ lodMaxClamp?: number;
+ compare?: GPUCompareFunction;
+ maxAnisotropy?: number;
+}
+
+/** @category WebGPU */
+declare type GPUAddressMode = "clamp-to-edge" | "repeat" | "mirror-repeat";
+
+/** @category WebGPU */
+declare type GPUFilterMode = "nearest" | "linear";
+
+/** @category WebGPU */
+declare type GPUMipmapFilterMode = "nearest" | "linear";
+
+/** @category WebGPU */
+declare type GPUCompareFunction =
+ | "never"
+ | "less"
+ | "equal"
+ | "less-equal"
+ | "greater"
+ | "not-equal"
+ | "greater-equal"
+ | "always";
+
+/** @category WebGPU */
+declare class GPUBindGroupLayout implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUBindGroupLayoutDescriptor extends GPUObjectDescriptorBase {
+ entries: GPUBindGroupLayoutEntry[];
+}
+
+/** @category WebGPU */
+declare interface GPUBindGroupLayoutEntry {
+ binding: number;
+ visibility: GPUShaderStageFlags;
+
+ buffer?: GPUBufferBindingLayout;
+ sampler?: GPUSamplerBindingLayout;
+ texture?: GPUTextureBindingLayout;
+ storageTexture?: GPUStorageTextureBindingLayout;
+}
+
+/** @category WebGPU */
+declare type GPUShaderStageFlags = number;
+
+/** @category WebGPU */
+declare class GPUShaderStage {
+ static VERTEX: 0x1;
+ static FRAGMENT: 0x2;
+ static COMPUTE: 0x4;
+}
+
+/** @category WebGPU */
+declare interface GPUBufferBindingLayout {
+ type?: GPUBufferBindingType;
+ hasDynamicOffset?: boolean;
+ minBindingSize?: number;
+}
+
+/** @category WebGPU */
+declare type GPUBufferBindingType = "uniform" | "storage" | "read-only-storage";
+
+/** @category WebGPU */
+declare interface GPUSamplerBindingLayout {
+ type?: GPUSamplerBindingType;
+}
+
+/** @category WebGPU */
+declare type GPUSamplerBindingType =
+ | "filtering"
+ | "non-filtering"
+ | "comparison";
+
+/** @category WebGPU */
+declare interface GPUTextureBindingLayout {
+ sampleType?: GPUTextureSampleType;
+ viewDimension?: GPUTextureViewDimension;
+ multisampled?: boolean;
+}
+
+/** @category WebGPU */
+declare type GPUTextureSampleType =
+ | "float"
+ | "unfilterable-float"
+ | "depth"
+ | "sint"
+ | "uint";
+
+/** @category WebGPU */
+declare type GPUStorageTextureAccess = "write-only";
+
+/** @category WebGPU */
+declare interface GPUStorageTextureBindingLayout {
+ access: GPUStorageTextureAccess;
+ format: GPUTextureFormat;
+ viewDimension?: GPUTextureViewDimension;
+}
+
+/** @category WebGPU */
+declare class GPUBindGroup implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUBindGroupDescriptor extends GPUObjectDescriptorBase {
+ layout: GPUBindGroupLayout;
+ entries: GPUBindGroupEntry[];
+}
+
+/** @category WebGPU */
+declare type GPUBindingResource =
+ | GPUSampler
+ | GPUTextureView
+ | GPUBufferBinding;
+
+/** @category WebGPU */
+declare interface GPUBindGroupEntry {
+ binding: number;
+ resource: GPUBindingResource;
+}
+
+/** @category WebGPU */
+declare interface GPUBufferBinding {
+ buffer: GPUBuffer;
+ offset?: number;
+ size?: number;
+}
+
+/** @category WebGPU */
+declare class GPUPipelineLayout implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUPipelineLayoutDescriptor extends GPUObjectDescriptorBase {
+ bindGroupLayouts: GPUBindGroupLayout[];
+}
+
+/** @category WebGPU */
+declare type GPUCompilationMessageType = "error" | "warning" | "info";
+
+/** @category WebGPU */
+declare interface GPUCompilationMessage {
+ readonly message: string;
+ readonly type: GPUCompilationMessageType;
+ readonly lineNum: number;
+ readonly linePos: number;
+}
+
+/** @category WebGPU */
+declare interface GPUCompilationInfo {
+ readonly messages: ReadonlyArray<GPUCompilationMessage>;
+}
+
+/** @category WebGPU */
+declare class GPUShaderModule implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUShaderModuleDescriptor extends GPUObjectDescriptorBase {
+ code: string;
+ sourceMap?: any;
+}
+
+/** @category WebGPU */
+declare type GPUAutoLayoutMode = "auto";
+
+/** @category WebGPU */
+declare interface GPUPipelineDescriptorBase extends GPUObjectDescriptorBase {
+ layout: GPUPipelineLayout | GPUAutoLayoutMode;
+}
+
+/** @category WebGPU */
+declare interface GPUPipelineBase {
+ getBindGroupLayout(index: number): GPUBindGroupLayout;
+}
+
+/** @category WebGPU */
+declare interface GPUProgrammableStage {
+ module: GPUShaderModule;
+ entryPoint: string;
+}
+
+/** @category WebGPU */
+declare class GPUComputePipeline implements GPUObjectBase, GPUPipelineBase {
+ label: string;
+
+ getBindGroupLayout(index: number): GPUBindGroupLayout;
+}
+
+/** @category WebGPU */
+declare interface GPUComputePipelineDescriptor
+ extends GPUPipelineDescriptorBase {
+ compute: GPUProgrammableStage;
+}
+
+/** @category WebGPU */
+declare class GPURenderPipeline implements GPUObjectBase, GPUPipelineBase {
+ label: string;
+
+ getBindGroupLayout(index: number): GPUBindGroupLayout;
+}
+
+/** @category WebGPU */
+declare interface GPURenderPipelineDescriptor
+ extends GPUPipelineDescriptorBase {
+ vertex: GPUVertexState;
+ primitive?: GPUPrimitiveState;
+ depthStencil?: GPUDepthStencilState;
+ multisample?: GPUMultisampleState;
+ fragment?: GPUFragmentState;
+}
+
+/** @category WebGPU */
+declare interface GPUPrimitiveState {
+ topology?: GPUPrimitiveTopology;
+ stripIndexFormat?: GPUIndexFormat;
+ frontFace?: GPUFrontFace;
+ cullMode?: GPUCullMode;
+ unclippedDepth?: boolean;
+}
+
+/** @category WebGPU */
+declare type GPUPrimitiveTopology =
+ | "point-list"
+ | "line-list"
+ | "line-strip"
+ | "triangle-list"
+ | "triangle-strip";
+
+/** @category WebGPU */
+declare type GPUFrontFace = "ccw" | "cw";
+
+/** @category WebGPU */
+declare type GPUCullMode = "none" | "front" | "back";
+
+/** @category WebGPU */
+declare interface GPUMultisampleState {
+ count?: number;
+ mask?: number;
+ alphaToCoverageEnabled?: boolean;
+}
+
+/** @category WebGPU */
+declare interface GPUFragmentState extends GPUProgrammableStage {
+ targets: (GPUColorTargetState | null)[];
+}
+
+/** @category WebGPU */
+declare interface GPUColorTargetState {
+ format: GPUTextureFormat;
+
+ blend?: GPUBlendState;
+ writeMask?: GPUColorWriteFlags;
+}
+
+/** @category WebGPU */
+declare interface GPUBlendState {
+ color: GPUBlendComponent;
+ alpha: GPUBlendComponent;
+}
+
+/** @category WebGPU */
+declare type GPUColorWriteFlags = number;
+
+/** @category WebGPU */
+declare class GPUColorWrite {
+ static RED: 0x1;
+ static GREEN: 0x2;
+ static BLUE: 0x4;
+ static ALPHA: 0x8;
+ static ALL: 0xF;
+}
+
+/** @category WebGPU */
+declare interface GPUBlendComponent {
+ operation?: GPUBlendOperation;
+ srcFactor?: GPUBlendFactor;
+ dstFactor?: GPUBlendFactor;
+}
+
+/** @category WebGPU */
+declare type GPUBlendFactor =
+ | "zero"
+ | "one"
+ | "src"
+ | "one-minus-src"
+ | "src-alpha"
+ | "one-minus-src-alpha"
+ | "dst"
+ | "one-minus-dst"
+ | "dst-alpha"
+ | "one-minus-dst-alpha"
+ | "src-alpha-saturated"
+ | "constant"
+ | "one-minus-constant";
+
+/** @category WebGPU */
+declare type GPUBlendOperation =
+ | "add"
+ | "subtract"
+ | "reverse-subtract"
+ | "min"
+ | "max";
+
+/** @category WebGPU */
+declare interface GPUDepthStencilState {
+ format: GPUTextureFormat;
+
+ depthWriteEnabled: boolean;
+ depthCompare: GPUCompareFunction;
+
+ stencilFront?: GPUStencilFaceState;
+ stencilBack?: GPUStencilFaceState;
+
+ stencilReadMask?: number;
+ stencilWriteMask?: number;
+
+ depthBias?: number;
+ depthBiasSlopeScale?: number;
+ depthBiasClamp?: number;
+}
+
+/** @category WebGPU */
+declare interface GPUStencilFaceState {
+ compare?: GPUCompareFunction;
+ failOp?: GPUStencilOperation;
+ depthFailOp?: GPUStencilOperation;
+ passOp?: GPUStencilOperation;
+}
+
+/** @category WebGPU */
+declare type GPUStencilOperation =
+ | "keep"
+ | "zero"
+ | "replace"
+ | "invert"
+ | "increment-clamp"
+ | "decrement-clamp"
+ | "increment-wrap"
+ | "decrement-wrap";
+
+/** @category WebGPU */
+declare type GPUIndexFormat = "uint16" | "uint32";
+
+/** @category WebGPU */
+declare type GPUVertexFormat =
+ | "uint8x2"
+ | "uint8x4"
+ | "sint8x2"
+ | "sint8x4"
+ | "unorm8x2"
+ | "unorm8x4"
+ | "snorm8x2"
+ | "snorm8x4"
+ | "uint16x2"
+ | "uint16x4"
+ | "sint16x2"
+ | "sint16x4"
+ | "unorm16x2"
+ | "unorm16x4"
+ | "snorm16x2"
+ | "snorm16x4"
+ | "float16x2"
+ | "float16x4"
+ | "float32"
+ | "float32x2"
+ | "float32x3"
+ | "float32x4"
+ | "uint32"
+ | "uint32x2"
+ | "uint32x3"
+ | "uint32x4"
+ | "sint32"
+ | "sint32x2"
+ | "sint32x3"
+ | "sint32x4";
+
+/** @category WebGPU */
+declare type GPUVertexStepMode = "vertex" | "instance";
+
+/** @category WebGPU */
+declare interface GPUVertexState extends GPUProgrammableStage {
+ buffers?: (GPUVertexBufferLayout | null)[];
+}
+
+/** @category WebGPU */
+declare interface GPUVertexBufferLayout {
+ arrayStride: number;
+ stepMode?: GPUVertexStepMode;
+ attributes: GPUVertexAttribute[];
+}
+
+/** @category WebGPU */
+declare interface GPUVertexAttribute {
+ format: GPUVertexFormat;
+ offset: number;
+
+ shaderLocation: number;
+}
+
+/** @category WebGPU */
+declare interface GPUImageDataLayout {
+ offset?: number;
+ bytesPerRow?: number;
+ rowsPerImage?: number;
+}
+
+/** @category WebGPU */
+declare class GPUCommandBuffer implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPUCommandBufferDescriptor extends GPUObjectDescriptorBase {}
+
+/** @category WebGPU */
+declare class GPUCommandEncoder implements GPUObjectBase {
+ label: string;
+
+ beginRenderPass(descriptor: GPURenderPassDescriptor): GPURenderPassEncoder;
+ beginComputePass(
+ descriptor?: GPUComputePassDescriptor,
+ ): GPUComputePassEncoder;
+
+ copyBufferToBuffer(
+ source: GPUBuffer,
+ sourceOffset: number,
+ destination: GPUBuffer,
+ destinationOffset: number,
+ size: number,
+ ): undefined;
+
+ copyBufferToTexture(
+ source: GPUImageCopyBuffer,
+ destination: GPUImageCopyTexture,
+ copySize: GPUExtent3D,
+ ): undefined;
+
+ copyTextureToBuffer(
+ source: GPUImageCopyTexture,
+ destination: GPUImageCopyBuffer,
+ copySize: GPUExtent3D,
+ ): undefined;
+
+ copyTextureToTexture(
+ source: GPUImageCopyTexture,
+ destination: GPUImageCopyTexture,
+ copySize: GPUExtent3D,
+ ): undefined;
+
+ clearBuffer(
+ destination: GPUBuffer,
+ destinationOffset?: number,
+ size?: number,
+ ): undefined;
+
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+
+ writeTimestamp(querySet: GPUQuerySet, queryIndex: number): undefined;
+
+ resolveQuerySet(
+ querySet: GPUQuerySet,
+ firstQuery: number,
+ queryCount: number,
+ destination: GPUBuffer,
+ destinationOffset: number,
+ ): undefined;
+
+ finish(descriptor?: GPUCommandBufferDescriptor): GPUCommandBuffer;
+}
+
+/** @category WebGPU */
+declare interface GPUCommandEncoderDescriptor extends GPUObjectDescriptorBase {}
+
+/** @category WebGPU */
+declare interface GPUImageCopyBuffer extends GPUImageDataLayout {
+ buffer: GPUBuffer;
+}
+
+/** @category WebGPU */
+declare interface GPUImageCopyTexture {
+ texture: GPUTexture;
+ mipLevel?: number;
+ origin?: GPUOrigin3D;
+ aspect?: GPUTextureAspect;
+}
+
+/** @category WebGPU */
+interface GPUProgrammablePassEncoder {
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+}
+
+/** @category WebGPU */
+declare class GPUComputePassEncoder
+ implements GPUObjectBase, GPUProgrammablePassEncoder {
+ label: string;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+ setPipeline(pipeline: GPUComputePipeline): undefined;
+ dispatchWorkgroups(x: number, y?: number, z?: number): undefined;
+ dispatchWorkgroupsIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+
+ end(): undefined;
+}
+
+/** @category WebGPU */
+declare interface GPUComputePassTimestampWrites {
+ querySet: GPUQuerySet;
+ beginningOfPassWriteIndex?: number;
+ endOfPassWriteIndex?: number;
+}
+
+/** @category WebGPU */
+declare interface GPUComputePassDescriptor extends GPUObjectDescriptorBase {
+ timestampWrites?: GPUComputePassTimestampWrites;
+}
+
+/** @category WebGPU */
+interface GPURenderEncoderBase {
+ setPipeline(pipeline: GPURenderPipeline): undefined;
+
+ setIndexBuffer(
+ buffer: GPUBuffer,
+ indexFormat: GPUIndexFormat,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ setVertexBuffer(
+ slot: number,
+ buffer: GPUBuffer,
+ offset?: number,
+ size?: number,
+ ): undefined;
+
+ draw(
+ vertexCount: number,
+ instanceCount?: number,
+ firstVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexed(
+ indexCount: number,
+ instanceCount?: number,
+ firstIndex?: number,
+ baseVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+
+ drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: number): undefined;
+ drawIndexedIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+}
+
+/** @category WebGPU */
+declare class GPURenderPassEncoder
+ implements GPUObjectBase, GPUProgrammablePassEncoder, GPURenderEncoderBase {
+ label: string;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+ setPipeline(pipeline: GPURenderPipeline): undefined;
+ setIndexBuffer(
+ buffer: GPUBuffer,
+ indexFormat: GPUIndexFormat,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ setVertexBuffer(
+ slot: number,
+ buffer: GPUBuffer,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ draw(
+ vertexCount: number,
+ instanceCount?: number,
+ firstVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexed(
+ indexCount: number,
+ instanceCount?: number,
+ firstIndex?: number,
+ baseVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: number): undefined;
+ drawIndexedIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+
+ setViewport(
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ minDepth: number,
+ maxDepth: number,
+ ): undefined;
+
+ setScissorRect(
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ ): undefined;
+
+ setBlendConstant(color: GPUColor): undefined;
+ setStencilReference(reference: number): undefined;
+
+ beginOcclusionQuery(queryIndex: number): undefined;
+ endOcclusionQuery(): undefined;
+
+ executeBundles(bundles: GPURenderBundle[]): undefined;
+ end(): undefined;
+}
+
+/** @category WebGPU */
+declare interface GPURenderPassTimestampWrites {
+ querySet: GPUQuerySet;
+ beginningOfPassWriteIndex?: number;
+ endOfPassWriteIndex?: number;
+}
+
+/** @category WebGPU */
+declare interface GPURenderPassDescriptor extends GPUObjectDescriptorBase {
+ colorAttachments: (GPURenderPassColorAttachment | null)[];
+ depthStencilAttachment?: GPURenderPassDepthStencilAttachment;
+ occlusionQuerySet?: GPUQuerySet;
+ timestampWrites?: GPURenderPassTimestampWrites;
+}
+
+/** @category WebGPU */
+declare interface GPURenderPassColorAttachment {
+ view: GPUTextureView;
+ resolveTarget?: GPUTextureView;
+
+ clearValue?: GPUColor;
+ loadOp: GPULoadOp;
+ storeOp: GPUStoreOp;
+}
+
+/** @category WebGPU */
+declare interface GPURenderPassDepthStencilAttachment {
+ view: GPUTextureView;
+
+ depthClearValue?: number;
+ depthLoadOp?: GPULoadOp;
+ depthStoreOp?: GPUStoreOp;
+ depthReadOnly?: boolean;
+
+ stencilClearValue?: number;
+ stencilLoadOp?: GPULoadOp;
+ stencilStoreOp?: GPUStoreOp;
+ stencilReadOnly?: boolean;
+}
+
+/** @category WebGPU */
+declare type GPULoadOp = "load" | "clear";
+
+/** @category WebGPU */
+declare type GPUStoreOp = "store" | "discard";
+
+/** @category WebGPU */
+declare class GPURenderBundle implements GPUObjectBase {
+ label: string;
+}
+
+/** @category WebGPU */
+declare interface GPURenderBundleDescriptor extends GPUObjectDescriptorBase {}
+
+/** @category WebGPU */
+declare class GPURenderBundleEncoder
+ implements GPUObjectBase, GPUProgrammablePassEncoder, GPURenderEncoderBase {
+ label: string;
+ draw(
+ vertexCount: number,
+ instanceCount?: number,
+ firstVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexed(
+ indexCount: number,
+ instanceCount?: number,
+ firstIndex?: number,
+ baseVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexedIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+ drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: number): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+ popDebugGroup(): undefined;
+ pushDebugGroup(groupLabel: string): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+ setIndexBuffer(
+ buffer: GPUBuffer,
+ indexFormat: GPUIndexFormat,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ setPipeline(pipeline: GPURenderPipeline): undefined;
+ setVertexBuffer(
+ slot: number,
+ buffer: GPUBuffer,
+ offset?: number,
+ size?: number,
+ ): undefined;
+
+ finish(descriptor?: GPURenderBundleDescriptor): GPURenderBundle;
+}
+
+/** @category WebGPU */
+declare interface GPURenderPassLayout extends GPUObjectDescriptorBase {
+ colorFormats: (GPUTextureFormat | null)[];
+ depthStencilFormat?: GPUTextureFormat;
+ sampleCount?: number;
+}
+
+/** @category WebGPU */
+declare interface GPURenderBundleEncoderDescriptor extends GPURenderPassLayout {
+ depthReadOnly?: boolean;
+ stencilReadOnly?: boolean;
+}
+
+/** @category WebGPU */
+declare class GPUQueue implements GPUObjectBase {
+ label: string;
+
+ submit(commandBuffers: GPUCommandBuffer[]): undefined;
+
+ onSubmittedWorkDone(): Promise<undefined>;
+
+ writeBuffer(
+ buffer: GPUBuffer,
+ bufferOffset: number,
+ data: BufferSource,
+ dataOffset?: number,
+ size?: number,
+ ): undefined;
+
+ writeTexture(
+ destination: GPUImageCopyTexture,
+ data: BufferSource,
+ dataLayout: GPUImageDataLayout,
+ size: GPUExtent3D,
+ ): undefined;
+}
+
+/** @category WebGPU */
+declare class GPUQuerySet implements GPUObjectBase {
+ label: string;
+
+ destroy(): undefined;
+
+ readonly type: GPUQueryType;
+ readonly count: number;
+}
+
+/** @category WebGPU */
+declare interface GPUQuerySetDescriptor extends GPUObjectDescriptorBase {
+ type: GPUQueryType;
+ count: number;
+}
+
+/** @category WebGPU */
+declare type GPUQueryType = "occlusion" | "timestamp";
+
+/** @category WebGPU */
+declare type GPUDeviceLostReason = "destroyed";
+
+/** @category WebGPU */
+declare interface GPUDeviceLostInfo {
+ readonly reason: GPUDeviceLostReason;
+ readonly message: string;
+}
+
+/** @category WebGPU */
+declare class GPUError {
+ readonly message: string;
+}
+
+/** @category WebGPU */
+declare class GPUOutOfMemoryError extends GPUError {
+ constructor(message: string);
+}
+
+/** @category WebGPU */
+declare class GPUValidationError extends GPUError {
+ constructor(message: string);
+}
+
+/** @category WebGPU */
+declare type GPUErrorFilter = "out-of-memory" | "validation";
+
+/** @category WebGPU */
+declare interface GPUColorDict {
+ r: number;
+ g: number;
+ b: number;
+ a: number;
+}
+
+/** @category WebGPU */
+declare type GPUColor = number[] | GPUColorDict;
+
+/** @category WebGPU */
+declare interface GPUOrigin3DDict {
+ x?: number;
+ y?: number;
+ z?: number;
+}
+
+/** @category WebGPU */
+declare type GPUOrigin3D = number[] | GPUOrigin3DDict;
+
+/** @category WebGPU */
+declare interface GPUExtent3DDict {
+ width: number;
+ height?: number;
+ depthOrArrayLayers?: number;
+}
+
+/** @category WebGPU */
+declare type GPUExtent3D = number[] | GPUExtent3DDict;
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index 0c3a5602e..2a820a6ee 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -92,6 +92,7 @@ pub fn get_types_declaration_file_text(unstable: bool) -> String {
"deno.url",
"deno.web",
"deno.fetch",
+ "deno.webgpu",
"deno.websocket",
"deno.webstorage",
"deno.crypto",
diff --git a/ext/webgpu/01_webgpu.js b/ext/webgpu/01_webgpu.js
new file mode 100644
index 000000000..264eaa443
--- /dev/null
+++ b/ext/webgpu/01_webgpu.js
@@ -0,0 +1,7087 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+/// <reference path="../../core/lib.deno_core.d.ts" />
+/// <reference path="../web/internal.d.ts" />
+/// <reference path="../web/lib.deno_web.d.ts" />
+/// <reference path="./lib.deno_webgpu.d.ts" />
+
+import { core, primordials } from "ext:core/mod.js";
+const ops = core.ops;
+import * as webidl from "ext:deno_webidl/00_webidl.js";
+import { EventTarget } from "ext:deno_web/02_event.js";
+import DOMException from "ext:deno_web/01_dom_exception.js";
+import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
+const {
+ ArrayBuffer,
+ ArrayBufferIsView,
+ ArrayIsArray,
+ ArrayPrototypeFilter,
+ ArrayPrototypeMap,
+ ArrayPrototypePop,
+ ArrayPrototypePush,
+ ObjectHasOwn,
+ ArrayPrototypeIncludes,
+ ArrayBufferPrototypeGetByteLength,
+ Error,
+ MathMax,
+ ObjectDefineProperty,
+ ObjectPrototypeIsPrototypeOf,
+ Promise,
+ PromisePrototypeCatch,
+ PromisePrototypeThen,
+ PromiseReject,
+ PromiseResolve,
+ SafeArrayIterator,
+ SafePromiseAll,
+ SafeSet,
+ TypedArrayPrototypeGetSymbolToStringTag,
+ TypedArrayPrototypeGetBuffer,
+ DataViewPrototypeGetBuffer,
+ SafeWeakRef,
+ SetPrototypeHas,
+ Symbol,
+ SymbolFor,
+ SymbolIterator,
+ TypeError,
+ Uint32Array,
+ Uint32ArrayPrototype,
+ Uint8Array,
+} = primordials;
+
+const _rid = Symbol("[[rid]]");
+const _size = Symbol("[[size]]");
+const _usage = Symbol("[[usage]]");
+const _state = Symbol("[[state]]");
+const _mappingRange = Symbol("[[mapping_range]]");
+const _mappedRanges = Symbol("[[mapped_ranges]]");
+const _mapMode = Symbol("[[map_mode]]");
+const _adapter = Symbol("[[adapter]]");
+const _cleanup = Symbol("[[cleanup]]");
+const _vendor = Symbol("[[vendor]]");
+const _architecture = Symbol("[[architecture]]");
+const _description = Symbol("[[description]]");
+const _limits = Symbol("[[limits]]");
+const _reason = Symbol("[[reason]]");
+const _message = Symbol("[[message]]");
+const _label = Symbol("[[label]]");
+const _device = Symbol("[[device]]");
+const _queue = Symbol("[[queue]]");
+const _views = Symbol("[[views]]");
+const _texture = Symbol("[[texture]]");
+const _encoders = Symbol("[[encoders]]");
+const _encoder = Symbol("[[encoder]]");
+const _descriptor = Symbol("[[descriptor]]");
+const _width = Symbol("[[width]]");
+const _height = Symbol("[[height]]");
+const _depthOrArrayLayers = Symbol("[[depthOrArrayLayers]]");
+const _mipLevelCount = Symbol("[[mipLevelCount]]");
+const _sampleCount = Symbol("[[sampleCount]]");
+const _dimension = Symbol("[[dimension]]");
+const _format = Symbol("[[format]]");
+const _type = Symbol("[[type]]");
+const _count = Symbol("[[count]]");
+
+/**
+ * @param {any} self
+ * @param {string} prefix
+ * @param {string} context
+ * @returns {InnerGPUDevice & {rid: number}}
+ */
+function assertDevice(self, prefix, context) {
+ const device = self[_device];
+ const deviceRid = device?.rid;
+ if (deviceRid === undefined) {
+ throw new DOMException(
+ `${prefix}: ${context} references an invalid or destroyed device.`,
+ "OperationError",
+ );
+ }
+ return device;
+}
+
+/**
+ * @param {InnerGPUDevice} self
+ * @param {any} resource
+ * @param {{prefix: string, resourceContext: string, selfContext: string}} opts
+ * @returns {InnerGPUDevice & {rid: number}}
+ */
+function assertDeviceMatch(
+ self,
+ resource,
+ { prefix, resourceContext, selfContext },
+) {
+ const resourceDevice = assertDevice(resource, prefix, resourceContext);
+ if (resourceDevice.rid !== self.rid) {
+ throw new DOMException(
+ `${prefix}: ${resourceContext} belongs to a diffent device than ${selfContext}.`,
+ "OperationError",
+ );
+ }
+ return { ...resourceDevice, rid: resourceDevice.rid };
+}
+
+/**
+ * @param {any} self
+ * @param {string} prefix
+ * @param {string} context
+ * @returns {number}
+ */
+function assertResource(self, prefix, context) {
+ const rid = self[_rid];
+ if (rid === undefined) {
+ throw new DOMException(
+ `${prefix}: ${context} an invalid or destroyed resource.`,
+ "OperationError",
+ );
+ }
+ return rid;
+}
+
+/**
+ * @param {number[] | GPUExtent3DDict} data
+ * @returns {GPUExtent3DDict}
+ */
+function normalizeGPUExtent3D(data) {
+ if (ArrayIsArray(data)) {
+ return {
+ width: data[0],
+ height: data[1] ?? 1,
+ depthOrArrayLayers: data[2] ?? 1,
+ };
+ } else {
+ return {
+ width: data.width,
+ height: data.height ?? 1,
+ depthOrArrayLayers: data.depthOrArrayLayers ?? 1,
+ };
+ }
+}
+
+/**
+ * @param {number[] | GPUOrigin3DDict} data
+ * @returns {GPUOrigin3DDict}
+ */
+function normalizeGPUOrigin3D(data) {
+ if (ArrayIsArray(data)) {
+ return {
+ x: data[0],
+ y: data[1],
+ z: data[2],
+ };
+ } else {
+ return data;
+ }
+}
+
+/**
+ * @param {number[] | GPUColor} data
+ * @returns {GPUColor}
+ */
+function normalizeGPUColor(data) {
+ if (ArrayIsArray(data)) {
+ return {
+ r: data[0],
+ g: data[1],
+ b: data[2],
+ a: data[3],
+ };
+ } else {
+ return data;
+ }
+}
+
+const illegalConstructorKey = Symbol("illegalConstructorKey");
+class GPUError extends Error {
+ constructor(key = null) {
+ super();
+ if (key !== illegalConstructorKey) {
+ webidl.illegalConstructor();
+ }
+ }
+
+ [_message];
+ get message() {
+ webidl.assertBranded(this, GPUErrorPrototype);
+ return this[_message];
+ }
+}
+const GPUErrorPrototype = GPUError.prototype;
+
+class GPUValidationError extends GPUError {
+ name = "GPUValidationError";
+ /** @param {string} message */
+ constructor(message) {
+ const prefix = "Failed to construct 'GPUValidationError'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ message = webidl.converters.DOMString(message, prefix, "Argument 1");
+ super(illegalConstructorKey);
+ this[webidl.brand] = webidl.brand;
+ this[_message] = message;
+ }
+}
+const GPUValidationErrorPrototype = GPUValidationError.prototype;
+
+class GPUOutOfMemoryError extends GPUError {
+ name = "GPUOutOfMemoryError";
+ constructor(message) {
+ const prefix = "Failed to construct 'GPUOutOfMemoryError'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ message = webidl.converters.DOMString(message, prefix, "Argument 1");
+ super(illegalConstructorKey);
+ this[webidl.brand] = webidl.brand;
+ this[_message] = message;
+ }
+}
+const GPUOutOfMemoryErrorPrototype = GPUOutOfMemoryError.prototype;
+
+class GPU {
+ [webidl.brand] = webidl.brand;
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPURequestAdapterOptions} options
+ */
+ async requestAdapter(options = {}) {
+ webidl.assertBranded(this, GPUPrototype);
+ options = webidl.converters.GPURequestAdapterOptions(
+ options,
+ "Failed to execute 'requestAdapter' on 'GPU'",
+ "Argument 1",
+ );
+
+ const { err, ...data } = await core.opAsync(
+ "op_webgpu_request_adapter",
+ options.powerPreference,
+ options.forceFallbackAdapter,
+ );
+
+ if (err) {
+ return null;
+ } else {
+ return createGPUAdapter(data);
+ }
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return `${this.constructor.name} ${inspect({}, inspectOptions)}`;
+ }
+}
+const GPUPrototype = GPU.prototype;
+
+/**
+ * @typedef InnerGPUAdapter
+ * @property {number} rid
+ * @property {GPUSupportedFeatures} features
+ * @property {GPUSupportedLimits} limits
+ * @property {boolean} isFallbackAdapter
+ */
+
+/**
+ * @param {InnerGPUAdapter} inner
+ * @returns {GPUAdapter}
+ */
+function createGPUAdapter(inner) {
+ /** @type {GPUAdapter} */
+ const adapter = webidl.createBranded(GPUAdapter);
+ adapter[_adapter] = {
+ ...inner,
+ features: createGPUSupportedFeatures(inner.features),
+ limits: createGPUSupportedLimits(inner.limits),
+ };
+ return adapter;
+}
+
+class GPUAdapter {
+ /** @type {InnerGPUAdapter} */
+ [_adapter];
+
+ /** @returns {GPUSupportedFeatures} */
+ get features() {
+ webidl.assertBranded(this, GPUAdapterPrototype);
+ return this[_adapter].features;
+ }
+ /** @returns {GPUSupportedLimits} */
+ get limits() {
+ webidl.assertBranded(this, GPUAdapterPrototype);
+ return this[_adapter].limits;
+ }
+ /** @returns {boolean} */
+ get isFallbackAdapter() {
+ webidl.assertBranded(this, GPUAdapterPrototype);
+ return this[_adapter].isFallbackAdapter;
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPUDeviceDescriptor} descriptor
+ * @returns {Promise<GPUDevice>}
+ */
+ async requestDevice(descriptor = {}) {
+ webidl.assertBranded(this, GPUAdapterPrototype);
+ const prefix = "Failed to execute 'requestDevice' on 'GPUAdapter'";
+ descriptor = webidl.converters.GPUDeviceDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const requiredFeatures = descriptor.requiredFeatures ?? [];
+ for (let i = 0; i < requiredFeatures.length; ++i) {
+ const feature = requiredFeatures[i];
+ if (
+ !SetPrototypeHas(this[_adapter].features[webidl.setlikeInner], feature)
+ ) {
+ throw new TypeError(
+ `${prefix}: requiredFeatures must be a subset of the adapter features.`,
+ );
+ }
+ }
+
+ const { rid, features, limits } = await core.opAsync(
+ "op_webgpu_request_device",
+ this[_adapter].rid,
+ descriptor.label,
+ requiredFeatures,
+ descriptor.requiredLimits,
+ );
+
+ const inner = new InnerGPUDevice({
+ rid,
+ adapter: this,
+ features: createGPUSupportedFeatures(features),
+ limits: createGPUSupportedLimits(limits),
+ });
+ return createGPUDevice(
+ descriptor.label,
+ inner,
+ createGPUQueue(descriptor.label, inner),
+ );
+ }
+
+ /**
+ * @param {string[]} unmaskHints
+ * @returns {Promise<GPUAdapterInfo>}
+ */
+ async requestAdapterInfo(unmaskHints = []) {
+ webidl.assertBranded(this, GPUAdapterPrototype);
+ const prefix = "Failed to execute 'requestAdapterInfo' on 'GPUAdapter'";
+ unmaskHints = webidl.converters["sequence<DOMString>"](
+ unmaskHints,
+ prefix,
+ "Argument 1",
+ );
+
+ const {
+ vendor,
+ architecture,
+ device,
+ description,
+ } = await core.opAsync(
+ "op_webgpu_request_adapter_info",
+ this[_adapter].rid,
+ );
+
+ const adapterInfo = webidl.createBranded(GPUAdapterInfo);
+ adapterInfo[_vendor] = ArrayPrototypeIncludes(unmaskHints, "vendor")
+ ? vendor
+ : "";
+ adapterInfo[_architecture] =
+ ArrayPrototypeIncludes(unmaskHints, "architecture") ? architecture : "";
+ adapterInfo[_device] = ArrayPrototypeIncludes(unmaskHints, "device")
+ ? device
+ : "";
+ adapterInfo[_description] =
+ ArrayPrototypeIncludes(unmaskHints, "description") ? description : "";
+ return adapterInfo;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUAdapterPrototype, this),
+ keys: [
+ "features",
+ "limits",
+ "isFallbackAdapter",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+const GPUAdapterPrototype = GPUAdapter.prototype;
+
+class GPUAdapterInfo {
+ /** @type {string} */
+ [_vendor];
+ /** @returns {string} */
+ get vendor() {
+ webidl.assertBranded(this, GPUAdapterInfoPrototype);
+ return this[_vendor];
+ }
+
+ /** @type {string} */
+ [_architecture];
+ /** @returns {string} */
+ get architecture() {
+ webidl.assertBranded(this, GPUAdapterInfoPrototype);
+ return this[_architecture];
+ }
+
+ /** @type {string} */
+ [_device];
+ /** @returns {string} */
+ get device() {
+ webidl.assertBranded(this, GPUAdapterInfoPrototype);
+ return this[_device];
+ }
+
+ /** @type {string} */
+ [_description];
+ /** @returns {string} */
+ get description() {
+ webidl.assertBranded(this, GPUAdapterInfoPrototype);
+ return this[_description];
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUAdapterInfoPrototype, this),
+ keys: [
+ "vendor",
+ "architecture",
+ "device",
+ "description",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+const GPUAdapterInfoPrototype = GPUAdapterInfo.prototype;
+
+function createGPUSupportedLimits(limits) {
+ /** @type {GPUSupportedLimits} */
+ const adapterFeatures = webidl.createBranded(GPUSupportedLimits);
+ adapterFeatures[_limits] = limits;
+ return adapterFeatures;
+}
+
+/**
+ * @typedef InnerAdapterLimits
+ * @property {number} maxTextureDimension1D
+ * @property {number} maxTextureDimension2D
+ * @property {number} maxTextureDimension3D
+ * @property {number} maxTextureArrayLayers
+ * @property {number} maxBindGroups
+ * @property {number} maxDynamicUniformBuffersPerPipelineLayout
+ * @property {number} maxDynamicStorageBuffersPerPipelineLayout
+ * @property {number} maxSampledTexturesPerShaderStage
+ * @property {number} maxSamplersPerShaderStage
+ * @property {number} maxStorageBuffersPerShaderStage
+ * @property {number} maxStorageTexturesPerShaderStage
+ * @property {number} maxUniformBuffersPerShaderStage
+ * @property {number} maxUniformBufferBindingSize
+ * @property {number} maxStorageBufferBindingSize
+ * @property {number} minUniformBufferOffsetAlignment
+ * @property {number} minStorageBufferOffsetAlignment
+ * @property {number} maxVertexBuffers
+ * @property {number} maxVertexAttributes
+ * @property {number} maxVertexBufferArrayStride
+ * @property {number} maxInterStageShaderComponents
+ * @property {number} maxComputeWorkgroupStorageSize
+ * @property {number} maxComputeInvocationsPerWorkgroup
+ * @property {number} maxComputeWorkgroupSizeX
+ * @property {number} maxComputeWorkgroupSizeY
+ * @property {number} maxComputeWorkgroupSizeZ
+ * @property {number} maxComputeWorkgroupsPerDimension
+ */
+
+class GPUSupportedLimits {
+ /** @type {InnerAdapterLimits} */
+ [_limits];
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ get maxTextureDimension1D() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxTextureDimension1D;
+ }
+ get maxTextureDimension2D() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxTextureDimension2D;
+ }
+ get maxTextureDimension3D() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxTextureDimension3D;
+ }
+ get maxTextureArrayLayers() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxTextureArrayLayers;
+ }
+ get maxBindGroups() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxBindGroups;
+ }
+ get maxBindingsPerBindGroup() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxBindingsPerBindGroup;
+ }
+ get maxBufferSize() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxBufferSize;
+ }
+ get maxDynamicUniformBuffersPerPipelineLayout() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxDynamicUniformBuffersPerPipelineLayout;
+ }
+ get maxDynamicStorageBuffersPerPipelineLayout() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxDynamicStorageBuffersPerPipelineLayout;
+ }
+ get maxSampledTexturesPerShaderStage() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxSampledTexturesPerShaderStage;
+ }
+ get maxSamplersPerShaderStage() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxSamplersPerShaderStage;
+ }
+ get maxStorageBuffersPerShaderStage() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxStorageBuffersPerShaderStage;
+ }
+ get maxStorageTexturesPerShaderStage() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxStorageTexturesPerShaderStage;
+ }
+ get maxUniformBuffersPerShaderStage() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxUniformBuffersPerShaderStage;
+ }
+ get maxUniformBufferBindingSize() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxUniformBufferBindingSize;
+ }
+ get maxStorageBufferBindingSize() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxStorageBufferBindingSize;
+ }
+ get minUniformBufferOffsetAlignment() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].minUniformBufferOffsetAlignment;
+ }
+ get minStorageBufferOffsetAlignment() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].minStorageBufferOffsetAlignment;
+ }
+ get maxVertexBuffers() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxVertexBuffers;
+ }
+ get maxVertexAttributes() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxVertexAttributes;
+ }
+ get maxVertexBufferArrayStride() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxVertexBufferArrayStride;
+ }
+ get maxInterStageShaderComponents() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxInterStageShaderComponents;
+ }
+ get maxComputeWorkgroupStorageSize() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxComputeWorkgroupStorageSize;
+ }
+ get maxComputeInvocationsPerWorkgroup() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxComputeInvocationsPerWorkgroup;
+ }
+ get maxComputeWorkgroupSizeX() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxComputeWorkgroupSizeX;
+ }
+ get maxComputeWorkgroupSizeY() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxComputeWorkgroupSizeY;
+ }
+ get maxComputeWorkgroupSizeZ() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxComputeWorkgroupSizeZ;
+ }
+ get maxComputeWorkgroupsPerDimension() {
+ webidl.assertBranded(this, GPUSupportedLimitsPrototype);
+ return this[_limits].maxComputeWorkgroupsPerDimension;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPUSupportedLimitsPrototype,
+ this,
+ ),
+ keys: [
+ "maxTextureDimension1D",
+ "maxTextureDimension2D",
+ "maxTextureDimension3D",
+ "maxTextureArrayLayers",
+ "maxBindGroups",
+ "maxBindingsPerBindGroup",
+ "maxBufferSize",
+ "maxDynamicUniformBuffersPerPipelineLayout",
+ "maxDynamicStorageBuffersPerPipelineLayout",
+ "maxSampledTexturesPerShaderStage",
+ "maxSamplersPerShaderStage",
+ "maxStorageBuffersPerShaderStage",
+ "maxStorageTexturesPerShaderStage",
+ "maxUniformBuffersPerShaderStage",
+ "maxUniformBufferBindingSize",
+ "maxStorageBufferBindingSize",
+ "minUniformBufferOffsetAlignment",
+ "minStorageBufferOffsetAlignment",
+ "maxVertexBuffers",
+ "maxVertexAttributes",
+ "maxVertexBufferArrayStride",
+ "maxInterStageShaderComponents",
+ "maxComputeWorkgroupStorageSize",
+ "maxComputeInvocationsPerWorkgroup",
+ "maxComputeWorkgroupSizeX",
+ "maxComputeWorkgroupSizeY",
+ "maxComputeWorkgroupSizeZ",
+ "maxComputeWorkgroupsPerDimension",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+const GPUSupportedLimitsPrototype = GPUSupportedLimits.prototype;
+
+function createGPUSupportedFeatures(features) {
+ /** @type {GPUSupportedFeatures} */
+ const supportedFeatures = webidl.createBranded(GPUSupportedFeatures);
+ supportedFeatures[webidl.setlikeInner] = new SafeSet(features);
+ webidl.setlike(
+ supportedFeatures,
+ GPUSupportedFeaturesPrototype,
+ true,
+ );
+ return supportedFeatures;
+}
+
+class GPUSupportedFeatures {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) {
+ return `${this.constructor.name} ${
+ // deno-lint-ignore prefer-primordials
+ inspect([...this], inspectOptions)}`;
+ } else {
+ return `${this.constructor.name} ${inspect({}, inspectOptions)}`;
+ }
+ }
+}
+
+const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype;
+
+/**
+ * @param {string | undefined} reason
+ * @param {string} message
+ * @returns {GPUDeviceLostInfo}
+ */
+function createGPUDeviceLostInfo(reason, message) {
+ /** @type {GPUDeviceLostInfo} */
+ const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo);
+ deviceLostInfo[_reason] = reason ?? "unknown";
+ deviceLostInfo[_message] = message;
+ return deviceLostInfo;
+}
+
+class GPUDeviceLostInfo {
+ /** @type {string} */
+ [_reason];
+ /** @type {string} */
+ [_message];
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ get reason() {
+ webidl.assertBranded(this, GPUDeviceLostInfoPrototype);
+ return this[_reason];
+ }
+ get message() {
+ webidl.assertBranded(this, GPUDeviceLostInfoPrototype);
+ return this[_message];
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPUDeviceLostInfoPrototype,
+ this,
+ ),
+ keys: [
+ "reason",
+ "message",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+
+const GPUDeviceLostInfoPrototype = GPUDeviceLostInfo.prototype;
+
+/**
+ * @param {string} name
+ * @param {any} type
+ */
+function GPUObjectBaseMixin(name, type) {
+ type.prototype[_label] = null;
+ ObjectDefineProperty(type.prototype, "label", {
+ /**
+ * @return {string | null}
+ */
+ get() {
+ webidl.assertBranded(this, type.prototype);
+ return this[_label];
+ },
+ /**
+ * @param {string | null} label
+ */
+ set(label) {
+ webidl.assertBranded(this, type.prototype);
+ label = webidl.converters["UVString?"](
+ label,
+ `Failed to set 'label' on '${name}'`,
+ "Argument 1",
+ );
+ this[_label] = label;
+ },
+ });
+}
+
+/**
+ * @typedef ErrorScope
+ * @property {string} filter
+ * @property {Promise<void>[]} operations
+ */
+
+/**
+ * @typedef InnerGPUDeviceOptions
+ * @property {GPUAdapter} adapter
+ * @property {number | undefined} rid
+ * @property {GPUSupportedFeatures} features
+ * @property {GPUSupportedLimits} limits
+ */
+
+class InnerGPUDevice {
+ /** @type {GPUAdapter} */
+ adapter;
+ /** @type {number | undefined} */
+ rid;
+ /** @type {GPUSupportedFeatures} */
+ features;
+ /** @type {GPUSupportedLimits} */
+ limits;
+ /** @type {SafeWeakRef<any>[]} */
+ resources;
+ /** @type {boolean} */
+ isLost;
+ /** @type {Promise<GPUDeviceLostInfo>} */
+ lost;
+ /** @type {(info: GPUDeviceLostInfo) => void} */
+ resolveLost;
+ /** @type {ErrorScope[]} */
+ errorScopeStack;
+
+ /**
+ * @param {InnerGPUDeviceOptions} options
+ */
+ constructor(options) {
+ this.adapter = options.adapter;
+ this.rid = options.rid;
+ this.features = options.features;
+ this.limits = options.limits;
+ this.resources = [];
+ this.isLost = false;
+ this.resolveLost = () => {};
+ this.lost = new Promise((resolve) => {
+ this.resolveLost = resolve;
+ });
+ this.errorScopeStack = [];
+ }
+
+ /** @param {any} resource */
+ trackResource(resource) {
+ ArrayPrototypePush(this.resources, new SafeWeakRef(resource));
+ }
+
+ /** @param {{ type: string, value: string | null } | undefined} err */
+ pushError(err) {
+ this.pushErrorPromise(PromiseResolve(err));
+ }
+
+ /** @param {Promise<{ type: string, value: string | null } | undefined>} promise */
+ pushErrorPromise(promise) {
+ const operation = PromisePrototypeThen(promise, (err) => {
+ if (err) {
+ switch (err.type) {
+ case "lost":
+ this.isLost = true;
+ this.resolveLost(
+ createGPUDeviceLostInfo(undefined, "device was lost"),
+ );
+ break;
+ case "validation":
+ return PromiseReject(
+ new GPUValidationError(err.value ?? "validation error"),
+ );
+ case "out-of-memory":
+ return PromiseReject(new GPUOutOfMemoryError());
+ }
+ }
+ });
+
+ const validationStack = ArrayPrototypeFilter(
+ this.errorScopeStack,
+ ({ filter }) => filter == "validation",
+ );
+ const validationScope = validationStack[validationStack.length - 1];
+ const validationFilteredPromise = PromisePrototypeCatch(
+ operation,
+ (err) => {
+ if (ObjectPrototypeIsPrototypeOf(GPUValidationErrorPrototype, err)) {
+ return PromiseReject(err);
+ }
+ return PromiseResolve();
+ },
+ );
+ if (validationScope) {
+ ArrayPrototypePush(
+ validationScope.operations,
+ validationFilteredPromise,
+ );
+ } else {
+ PromisePrototypeCatch(validationFilteredPromise, () => {
+ // TODO(lucacasonato): emit an UncapturedErrorEvent
+ });
+ }
+ // prevent uncaptured promise rejections
+ PromisePrototypeCatch(validationFilteredPromise, (_err) => {});
+
+ const oomStack = ArrayPrototypeFilter(
+ this.errorScopeStack,
+ ({ filter }) => filter == "out-of-memory",
+ );
+ const oomScope = oomStack[oomStack.length - 1];
+ const oomFilteredPromise = PromisePrototypeCatch(operation, (err) => {
+ if (ObjectPrototypeIsPrototypeOf(GPUOutOfMemoryErrorPrototype, err)) {
+ return PromiseReject(err);
+ }
+ return PromiseResolve();
+ });
+ if (oomScope) {
+ ArrayPrototypePush(oomScope.operations, oomFilteredPromise);
+ } else {
+ PromisePrototypeCatch(oomFilteredPromise, () => {
+ // TODO(lucacasonato): emit an UncapturedErrorEvent
+ });
+ }
+ // prevent uncaptured promise rejections
+ PromisePrototypeCatch(oomFilteredPromise, (_err) => {});
+ }
+}
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} inner
+ * @param {GPUQueue} queue
+ * @returns {GPUDevice}
+ */
+function createGPUDevice(label, inner, queue) {
+ /** @type {GPUDevice} */
+ const device = webidl.createBranded(GPUDevice);
+ device[_label] = label;
+ device[_device] = inner;
+ device[_queue] = queue;
+ return device;
+}
+
+class GPUDevice extends EventTarget {
+ /** @type {InnerGPUDevice} */
+ [_device];
+
+ /** @type {GPUQueue} */
+ [_queue];
+
+ [_cleanup]() {
+ const device = this[_device];
+ const resources = device.resources;
+ while (resources.length > 0) {
+ const resource = ArrayPrototypePop(resources)?.deref();
+ if (resource) {
+ resource[_cleanup]();
+ }
+ }
+ const rid = device.rid;
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ device.rid = undefined;
+ }
+ }
+
+ get features() {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ return this[_device].features;
+ }
+ get limits() {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ return this[_device].limits;
+ }
+ get queue() {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ return this[_queue];
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ super();
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ this[_cleanup]();
+ }
+
+ /**
+ * @param {GPUBufferDescriptor} descriptor
+ * @returns {GPUBuffer}
+ */
+ createBuffer(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createBuffer' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUBufferDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_buffer(
+ device.rid,
+ descriptor.label,
+ descriptor.size,
+ descriptor.usage,
+ descriptor.mappedAtCreation,
+ );
+ device.pushError(err);
+ /** @type {CreateGPUBufferOptions} */
+ let options;
+ if (descriptor.mappedAtCreation) {
+ options = {
+ mapping: new ArrayBuffer(descriptor.size),
+ mappingRange: [0, descriptor.size],
+ mappedRanges: [],
+ state: "mapped at creation",
+ };
+ } else {
+ options = {
+ mapping: null,
+ mappedRanges: null,
+ mappingRange: null,
+ state: "unmapped",
+ };
+ }
+ const buffer = createGPUBuffer(
+ descriptor.label,
+ device,
+ rid,
+ descriptor.size,
+ descriptor.usage,
+ options,
+ );
+ device.trackResource(buffer);
+ return buffer;
+ }
+
+ /**
+ * @param {GPUTextureDescriptor} descriptor
+ * @returns {GPUTexture}
+ */
+ createTexture(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createTexture' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUTextureDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_texture({
+ deviceRid: device.rid,
+ ...descriptor,
+ size: normalizeGPUExtent3D(descriptor.size),
+ });
+ device.pushError(err);
+
+ const texture = createGPUTexture(
+ descriptor,
+ device,
+ rid,
+ );
+ device.trackResource(texture);
+ return texture;
+ }
+
+ /**
+ * @param {GPUSamplerDescriptor} descriptor
+ * @returns {GPUSampler}
+ */
+ createSampler(descriptor = {}) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createSampler' on 'GPUDevice'";
+ descriptor = webidl.converters.GPUSamplerDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_sampler({
+ deviceRid: device.rid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const sampler = createGPUSampler(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(sampler);
+ return sampler;
+ }
+
+ /**
+ * @param {GPUBindGroupLayoutDescriptor} descriptor
+ * @returns {GPUBindGroupLayout}
+ */
+ createBindGroupLayout(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createBindGroupLayout' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUBindGroupLayoutDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ for (let i = 0; i < descriptor.entries.length; ++i) {
+ const entry = descriptor.entries[i];
+
+ let j = 0;
+ // deno-lint-ignore prefer-primordials
+ if (entry.buffer) j++;
+ if (entry.sampler) j++;
+ if (entry.texture) j++;
+ if (entry.storageTexture) j++;
+
+ if (j !== 1) {
+ throw new Error(); // TODO(@crowlKats): correct error
+ }
+ }
+
+ const { rid, err } = ops.op_webgpu_create_bind_group_layout(
+ device.rid,
+ descriptor.label,
+ descriptor.entries,
+ );
+ device.pushError(err);
+
+ const bindGroupLayout = createGPUBindGroupLayout(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(bindGroupLayout);
+ return bindGroupLayout;
+ }
+
+ /**
+ * @param {GPUPipelineLayoutDescriptor} descriptor
+ * @returns {GPUPipelineLayout}
+ */
+ createPipelineLayout(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createPipelineLayout' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUPipelineLayoutDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const bindGroupLayouts = ArrayPrototypeMap(
+ descriptor.bindGroupLayouts,
+ (layout, i) => {
+ const context = `bind group layout ${i + 1}`;
+ const rid = assertResource(layout, prefix, context);
+ assertDeviceMatch(device, layout, {
+ prefix,
+ selfContext: "this",
+ resourceContext: context,
+ });
+ return rid;
+ },
+ );
+ const { rid, err } = ops.op_webgpu_create_pipeline_layout(
+ device.rid,
+ descriptor.label,
+ bindGroupLayouts,
+ );
+ device.pushError(err);
+
+ const pipelineLayout = createGPUPipelineLayout(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(pipelineLayout);
+ return pipelineLayout;
+ }
+
+ /**
+ * @param {GPUBindGroupDescriptor} descriptor
+ * @returns {GPUBindGroup}
+ */
+ createBindGroup(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createBindGroup' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUBindGroupDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const layout = assertResource(descriptor.layout, prefix, "layout");
+ assertDeviceMatch(device, descriptor.layout, {
+ prefix,
+ resourceContext: "layout",
+ selfContext: "this",
+ });
+ const entries = ArrayPrototypeMap(descriptor.entries, (entry, i) => {
+ const context = `entry ${i + 1}`;
+ const resource = entry.resource;
+ if (ObjectPrototypeIsPrototypeOf(GPUSamplerPrototype, resource)) {
+ const rid = assertResource(resource, prefix, context);
+ assertDeviceMatch(device, resource, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return {
+ binding: entry.binding,
+ kind: "GPUSampler",
+ resource: rid,
+ };
+ } else if (
+ ObjectPrototypeIsPrototypeOf(GPUTextureViewPrototype, resource)
+ ) {
+ const rid = assertResource(resource, prefix, context);
+ assertResource(resource[_texture], prefix, context);
+ assertDeviceMatch(device, resource[_texture], {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return {
+ binding: entry.binding,
+ kind: "GPUTextureView",
+ resource: rid,
+ };
+ } else {
+ // deno-lint-ignore prefer-primordials
+ const rid = assertResource(resource.buffer, prefix, context);
+ // deno-lint-ignore prefer-primordials
+ assertDeviceMatch(device, resource.buffer, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return {
+ binding: entry.binding,
+ kind: "GPUBufferBinding",
+ resource: rid,
+ offset: entry.resource.offset,
+ size: entry.resource.size,
+ };
+ }
+ });
+
+ const { rid, err } = ops.op_webgpu_create_bind_group(
+ device.rid,
+ descriptor.label,
+ layout,
+ entries,
+ );
+ device.pushError(err);
+
+ const bindGroup = createGPUBindGroup(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(bindGroup);
+ return bindGroup;
+ }
+
+ /**
+ * @param {GPUShaderModuleDescriptor} descriptor
+ */
+ createShaderModule(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createShaderModule' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUShaderModuleDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_shader_module(
+ device.rid,
+ descriptor.label,
+ descriptor.code,
+ );
+ device.pushError(err);
+
+ const shaderModule = createGPUShaderModule(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(shaderModule);
+ return shaderModule;
+ }
+
+ /**
+ * @param {GPUComputePipelineDescriptor} descriptor
+ * @returns {GPUComputePipeline}
+ */
+ createComputePipeline(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createComputePipeline' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUComputePipelineDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ let layout = descriptor.layout;
+ if (typeof descriptor.layout !== "string") {
+ const context = "layout";
+ layout = assertResource(descriptor.layout, prefix, context);
+ assertDeviceMatch(device, descriptor.layout, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ }
+ const module = assertResource(
+ descriptor.compute.module,
+ prefix,
+ "compute shader module",
+ );
+ assertDeviceMatch(device, descriptor.compute.module, {
+ prefix,
+ resourceContext: "compute shader module",
+ selfContext: "this",
+ });
+
+ const { rid, err } = ops.op_webgpu_create_compute_pipeline(
+ device.rid,
+ descriptor.label,
+ layout,
+ {
+ module,
+ entryPoint: descriptor.compute.entryPoint,
+ constants: descriptor.compute.constants,
+ },
+ );
+ device.pushError(err);
+
+ const computePipeline = createGPUComputePipeline(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(computePipeline);
+ return computePipeline;
+ }
+
+ /**
+ * @param {GPURenderPipelineDescriptor} descriptor
+ * @returns {GPURenderPipeline}
+ */
+ createRenderPipeline(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createRenderPipeline' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPURenderPipelineDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ let layout = descriptor.layout;
+ if (typeof descriptor.layout !== "string") {
+ const context = "layout";
+ layout = assertResource(descriptor.layout, prefix, context);
+ assertDeviceMatch(device, descriptor.layout, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ }
+ const module = assertResource(
+ descriptor.vertex.module,
+ prefix,
+ "vertex shader module",
+ );
+ assertDeviceMatch(device, descriptor.vertex.module, {
+ prefix,
+ resourceContext: "vertex shader module",
+ selfContext: "this",
+ });
+ let fragment = undefined;
+ if (descriptor.fragment) {
+ const module = assertResource(
+ descriptor.fragment.module,
+ prefix,
+ "fragment shader module",
+ );
+ assertDeviceMatch(device, descriptor.fragment.module, {
+ prefix,
+ resourceContext: "fragment shader module",
+ selfContext: "this",
+ });
+ fragment = {
+ module,
+ entryPoint: descriptor.fragment.entryPoint,
+ targets: descriptor.fragment.targets,
+ };
+ }
+
+ const { rid, err } = ops.op_webgpu_create_render_pipeline({
+ deviceRid: device.rid,
+ label: descriptor.label,
+ layout,
+ vertex: {
+ module,
+ entryPoint: descriptor.vertex.entryPoint,
+ buffers: descriptor.vertex.buffers,
+ },
+ primitive: descriptor.primitive,
+ depthStencil: descriptor.depthStencil,
+ multisample: descriptor.multisample,
+ fragment,
+ });
+ device.pushError(err);
+
+ const renderPipeline = createGPURenderPipeline(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(renderPipeline);
+ return renderPipeline;
+ }
+
+ createComputePipelineAsync(descriptor) {
+ // TODO(lucacasonato): this should be real async
+ return PromiseResolve(this.createComputePipeline(descriptor));
+ }
+
+ createRenderPipelineAsync(descriptor) {
+ // TODO(lucacasonato): this should be real async
+ return PromiseResolve(this.createRenderPipeline(descriptor));
+ }
+
+ /**
+ * @param {GPUCommandEncoderDescriptor} descriptor
+ * @returns {GPUCommandEncoder}
+ */
+ createCommandEncoder(descriptor = {}) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createCommandEncoder' on 'GPUDevice'";
+ descriptor = webidl.converters.GPUCommandEncoderDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_command_encoder(
+ device.rid,
+ descriptor.label,
+ );
+ device.pushError(err);
+
+ const commandEncoder = createGPUCommandEncoder(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(commandEncoder);
+ return commandEncoder;
+ }
+
+ /**
+ * @param {GPURenderBundleEncoderDescriptor} descriptor
+ * @returns {GPURenderBundleEncoder}
+ */
+ createRenderBundleEncoder(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix =
+ "Failed to execute 'createRenderBundleEncoder' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPURenderBundleEncoderDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_render_bundle_encoder({
+ deviceRid: device.rid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const renderBundleEncoder = createGPURenderBundleEncoder(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(renderBundleEncoder);
+ return renderBundleEncoder;
+ }
+
+ /**
+ * @param {GPUQuerySetDescriptor} descriptor
+ * @returns {GPUQuerySet}
+ */
+ createQuerySet(descriptor) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'createQuerySet' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPUQuerySetDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_query_set({
+ deviceRid: device.rid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const querySet = createGPUQuerySet(
+ descriptor.label,
+ device,
+ rid,
+ descriptor,
+ );
+ device.trackResource(querySet);
+ return querySet;
+ }
+
+ get lost() {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const device = this[_device];
+ if (!device) {
+ return PromiseResolve(true);
+ }
+ if (device.rid === undefined) {
+ return PromiseResolve(true);
+ }
+ return device.lost;
+ }
+
+ /**
+ * @param {GPUErrorFilter} filter
+ */
+ pushErrorScope(filter) {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'pushErrorScope' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ filter = webidl.converters.GPUErrorFilter(filter, prefix, "Argument 1");
+ const device = assertDevice(this, prefix, "this");
+ ArrayPrototypePush(device.errorScopeStack, { filter, operations: [] });
+ }
+
+ /**
+ * @returns {Promise<GPUError | null>}
+ */
+ // deno-lint-ignore require-await
+ async popErrorScope() {
+ webidl.assertBranded(this, GPUDevicePrototype);
+ const prefix = "Failed to execute 'popErrorScope' on 'GPUDevice'";
+ const device = assertDevice(this, prefix, "this");
+ if (device.isLost) {
+ throw new DOMException("Device has been lost.", "OperationError");
+ }
+ const scope = ArrayPrototypePop(device.errorScopeStack);
+ if (!scope) {
+ throw new DOMException(
+ "There are no error scopes on the error scope stack.",
+ "OperationError",
+ );
+ }
+ const operations = SafePromiseAll(scope.operations);
+ return PromisePrototypeThen(
+ operations,
+ () => PromiseResolve(null),
+ (err) => PromiseResolve(err),
+ );
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUDevicePrototype, this),
+ keys: [
+ "features",
+ "label",
+ "limits",
+ "lost",
+ "queue",
+ // TODO(lucacasonato): emit an UncapturedErrorEvent
+ // "onuncapturederror"
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUDevice", GPUDevice);
+const GPUDevicePrototype = GPUDevice.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @returns {GPUQueue}
+ */
+function createGPUQueue(label, device) {
+ /** @type {GPUQueue} */
+ const queue = webidl.createBranded(GPUQueue);
+ queue[_label] = label;
+ queue[_device] = device;
+ return queue;
+}
+
+class GPUQueue {
+ /** @type {InnerGPUDevice} */
+ [_device];
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPUCommandBuffer[]} commandBuffers
+ */
+ submit(commandBuffers) {
+ webidl.assertBranded(this, GPUQueuePrototype);
+ const prefix = "Failed to execute 'submit' on 'GPUQueue'";
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix,
+ });
+ commandBuffers = webidl.converters["sequence<GPUCommandBuffer>"](
+ commandBuffers,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const commandBufferRids = ArrayPrototypeMap(
+ commandBuffers,
+ (buffer, i) => {
+ const context = `command buffer ${i + 1}`;
+ const rid = assertResource(buffer, prefix, context);
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ selfContext: "this",
+ resourceContext: context,
+ });
+ return rid;
+ },
+ );
+ const { err } = ops.op_webgpu_queue_submit(device.rid, commandBufferRids);
+ for (let i = 0; i < commandBuffers.length; ++i) {
+ commandBuffers[i][_rid] = undefined;
+ }
+ device.pushError(err);
+ }
+
+ onSubmittedWorkDone() {
+ webidl.assertBranded(this, GPUQueuePrototype);
+ return PromiseResolve();
+ }
+
+ /**
+ * @param {GPUBuffer} buffer
+ * @param {number} bufferOffset
+ * @param {BufferSource} data
+ * @param {number} [dataOffset]
+ * @param {number} [size]
+ */
+ writeBuffer(buffer, bufferOffset, data, dataOffset = 0, size) {
+ webidl.assertBranded(this, GPUQueuePrototype);
+ const prefix = "Failed to execute 'writeBuffer' on 'GPUQueue'";
+ webidl.requiredArguments(arguments.length, 3, prefix);
+ buffer = webidl.converters["GPUBuffer"](buffer, prefix, "Argument 1");
+ bufferOffset = webidl.converters["GPUSize64"](
+ bufferOffset,
+ prefix,
+ "Argument 2",
+ );
+ data = webidl.converters.BufferSource(data, prefix, "Argument 3");
+ dataOffset = webidl.converters["GPUSize64"](
+ dataOffset,
+ prefix,
+ "Argument 4",
+ );
+ size = size === undefined
+ ? undefined
+ : webidl.converters.GPUSize64(size, prefix, "Argument 5");
+ const device = assertDevice(this, prefix, "this");
+ const bufferRid = assertResource(buffer, prefix, "Argument 1");
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ selfContext: "this",
+ resourceContext: "Argument 1",
+ });
+ /** @type {ArrayBufferLike} */
+ let abLike = data;
+ if (ArrayBufferIsView(data)) {
+ if (TypedArrayPrototypeGetSymbolToStringTag(data) !== undefined) {
+ // TypedArray
+ abLike = TypedArrayPrototypeGetBuffer(
+ /** @type {Uint8Array} */ (data),
+ );
+ } else {
+ // DataView
+ abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data));
+ }
+ }
+ const { err } = ops.op_webgpu_write_buffer(
+ device.rid,
+ bufferRid,
+ bufferOffset,
+ dataOffset,
+ size,
+ new Uint8Array(abLike),
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyTexture} destination
+ * @param {BufferSource} data
+ * @param {GPUImageDataLayout} dataLayout
+ * @param {GPUExtent3D} size
+ */
+ writeTexture(destination, data, dataLayout, size) {
+ webidl.assertBranded(this, GPUQueuePrototype);
+ const prefix = "Failed to execute 'writeTexture' on 'GPUQueue'";
+ webidl.requiredArguments(arguments.length, 4, prefix);
+ destination = webidl.converters.GPUImageCopyTexture(
+ destination,
+ prefix,
+ "Argument 1",
+ );
+ data = webidl.converters.BufferSource(data, prefix, "Argument 2");
+ dataLayout = webidl.converters.GPUImageDataLayout(
+ dataLayout,
+ prefix,
+ "Argument 3",
+ );
+ size = webidl.converters.GPUExtent3D(size, prefix, "Argument 4");
+ const device = assertDevice(this, prefix, "this");
+ const textureRid = assertResource(destination.texture, prefix, "texture");
+ assertDeviceMatch(device, destination.texture, {
+ prefix,
+ selfContext: "this",
+ resourceContext: "texture",
+ });
+
+ /** @type {ArrayBufferLike} */
+ let abLike = data;
+ if (ArrayBufferIsView(data)) {
+ if (TypedArrayPrototypeGetSymbolToStringTag(data) !== undefined) {
+ // TypedArray
+ abLike = TypedArrayPrototypeGetBuffer(
+ /** @type {Uint8Array} */ (data),
+ );
+ } else {
+ // DataView
+ abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data));
+ }
+ }
+
+ const { err } = ops.op_webgpu_write_texture(
+ device.rid,
+ {
+ texture: textureRid,
+ mipLevel: destination.mipLevel,
+ origin: destination.origin
+ ? normalizeGPUOrigin3D(destination.origin)
+ : undefined,
+ aspect: destination.aspect,
+ },
+ dataLayout,
+ normalizeGPUExtent3D(size),
+ new Uint8Array(abLike),
+ );
+ device.pushError(err);
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUQueuePrototype, this),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUQueue", GPUQueue);
+const GPUQueuePrototype = GPUQueue.prototype;
+
+/**
+ * @typedef CreateGPUBufferOptions
+ * @property {ArrayBuffer | null} mapping
+ * @property {number[] | null} mappingRange
+ * @property {[ArrayBuffer, number, number][] | null} mappedRanges
+ * @property {"mapped" | "mapped at creation" | "mapped pending" | "unmapped" | "destroy" } state
+ */
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @param {number} size
+ * @param {number} usage
+ * @param {CreateGPUBufferOptions} options
+ * @returns {GPUBuffer}
+ */
+function createGPUBuffer(label, device, rid, size, usage, options) {
+ /** @type {GPUBuffer} */
+ const buffer = webidl.createBranded(GPUBuffer);
+ buffer[_label] = label;
+ buffer[_device] = device;
+ buffer[_rid] = rid;
+ buffer[_size] = size;
+ buffer[_usage] = usage;
+ buffer[_mappingRange] = options.mappingRange;
+ buffer[_mappedRanges] = options.mappedRanges;
+ buffer[_state] = options.state;
+ return buffer;
+}
+
+class GPUBuffer {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number} */
+ [_rid];
+ /** @type {number} */
+ [_size];
+ /** @type {number} */
+ [_usage];
+ /** @type {"mapped" | "mapped at creation" | "pending" | "unmapped" | "destroy"} */
+ [_state];
+ /** @type {[number, number] | null} */
+ [_mappingRange];
+ /** @type {[ArrayBuffer, number, number][] | null} */
+ [_mappedRanges];
+ /** @type {number} */
+ [_mapMode];
+
+ [_cleanup]() {
+ const mappedRanges = this[_mappedRanges];
+ if (mappedRanges) {
+ while (mappedRanges.length > 0) {
+ const mappedRange = ArrayPrototypePop(mappedRanges);
+ if (mappedRange !== undefined) {
+ core.close(mappedRange[1]);
+ }
+ }
+ }
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ this[_state] = "destroy";
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ get size() {
+ webidl.assertBranded(this, GPUBufferPrototype);
+ return this[_size];
+ }
+
+ get usage() {
+ webidl.assertBranded(this, GPUBufferPrototype);
+ return this[_usage];
+ }
+
+ get mapState() {
+ webidl.assertBranded(this, GPUBufferPrototype);
+ const state = this[_state];
+ if (state === "mapped at creation") {
+ return "mapped";
+ } else {
+ return state;
+ }
+ }
+
+ /**
+ * @param {number} mode
+ * @param {number} offset
+ * @param {number} [size]
+ */
+ async mapAsync(mode, offset = 0, size) {
+ webidl.assertBranded(this, GPUBufferPrototype);
+ const prefix = "Failed to execute 'mapAsync' on 'GPUBuffer'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ mode = webidl.converters.GPUMapModeFlags(mode, prefix, "Argument 1");
+ offset = webidl.converters.GPUSize64(offset, prefix, "Argument 2");
+ size = size === undefined
+ ? undefined
+ : webidl.converters.GPUSize64(size, prefix, "Argument 3");
+ const device = assertDevice(this, prefix, "this");
+ const bufferRid = assertResource(this, prefix, "this");
+ /** @type {number} */
+ let rangeSize;
+ if (size === undefined) {
+ rangeSize = MathMax(0, this[_size] - offset);
+ } else {
+ rangeSize = this[_size];
+ }
+ if ((offset % 8) !== 0) {
+ throw new DOMException(
+ `${prefix}: offset must be a multiple of 8.`,
+ "OperationError",
+ );
+ }
+ if ((rangeSize % 4) !== 0) {
+ throw new DOMException(
+ `${prefix}: rangeSize must be a multiple of 4.`,
+ "OperationError",
+ );
+ }
+ if ((offset + rangeSize) > this[_size]) {
+ throw new DOMException(
+ `${prefix}: offset + rangeSize must be less than or equal to buffer size.`,
+ "OperationError",
+ );
+ }
+ if (this[_state] !== "unmapped") {
+ throw new DOMException(
+ `${prefix}: GPUBuffer is not currently unmapped.`,
+ "OperationError",
+ );
+ }
+ const readMode = (mode & 0x0001) === 0x0001;
+ const writeMode = (mode & 0x0002) === 0x0002;
+ if ((readMode && writeMode) || (!readMode && !writeMode)) {
+ throw new DOMException(
+ `${prefix}: exactly one of READ or WRITE map mode must be set.`,
+ "OperationError",
+ );
+ }
+ if (readMode && !((this[_usage] && 0x0001) === 0x0001)) {
+ throw new DOMException(
+ `${prefix}: READ map mode not valid because buffer does not have MAP_READ usage.`,
+ "OperationError",
+ );
+ }
+ if (writeMode && !((this[_usage] && 0x0002) === 0x0002)) {
+ throw new DOMException(
+ `${prefix}: WRITE map mode not valid because buffer does not have MAP_WRITE usage.`,
+ "OperationError",
+ );
+ }
+
+ this[_mapMode] = mode;
+ this[_state] = "pending";
+ const promise = PromisePrototypeThen(
+ core.opAsync(
+ "op_webgpu_buffer_get_map_async",
+ bufferRid,
+ device.rid,
+ mode,
+ offset,
+ rangeSize,
+ ),
+ ({ err }) => err,
+ );
+ device.pushErrorPromise(promise);
+ const err = await promise;
+ if (err) {
+ throw new DOMException("validation error occured", "OperationError");
+ }
+ this[_state] = "mapped";
+ this[_mappingRange] = [offset, offset + rangeSize];
+ /** @type {[ArrayBuffer, number, number][] | null} */
+ this[_mappedRanges] = [];
+ }
+
+ /**
+ * @param {number} offset
+ * @param {number} size
+ */
+ getMappedRange(offset = 0, size) {
+ webidl.assertBranded(this, GPUBufferPrototype);
+ const prefix = "Failed to execute 'getMappedRange' on 'GPUBuffer'";
+ offset = webidl.converters.GPUSize64(offset, prefix, "Argument 1");
+ if (size !== undefined) {
+ size = webidl.converters.GPUSize64(size, prefix, "Argument 2");
+ }
+ assertDevice(this, prefix, "this");
+ const bufferRid = assertResource(this, prefix, "this");
+ /** @type {number} */
+ let rangeSize;
+ if (size === undefined) {
+ rangeSize = MathMax(0, this[_size] - offset);
+ } else {
+ rangeSize = size;
+ }
+
+ const mappedRanges = this[_mappedRanges];
+ if (!mappedRanges) {
+ throw new DOMException(`${prefix}: invalid state.`, "OperationError");
+ }
+ for (let i = 0; i < mappedRanges.length; ++i) {
+ const { 0: buffer, 1: _rid, 2: start } = mappedRanges[i];
+ // TODO(lucacasonato): is this logic correct?
+ const end = start + ArrayBufferPrototypeGetByteLength(buffer);
+ if (
+ (start >= offset && start < (offset + rangeSize)) ||
+ (end >= offset && end < (offset + rangeSize))
+ ) {
+ throw new DOMException(
+ `${prefix}: requested buffer overlaps with another mapped range.`,
+ "OperationError",
+ );
+ }
+ }
+
+ const buffer = new ArrayBuffer(rangeSize);
+ const { rid } = ops.op_webgpu_buffer_get_mapped_range(
+ bufferRid,
+ offset,
+ size,
+ new Uint8Array(buffer),
+ );
+
+ ArrayPrototypePush(mappedRanges, [buffer, rid, offset]);
+
+ return buffer;
+ }
+
+ unmap() {
+ webidl.assertBranded(this, GPUBufferPrototype);
+ const prefix = "Failed to execute 'unmap' on 'GPUBuffer'";
+ const device = assertDevice(this, prefix, "this");
+ const bufferRid = assertResource(this, prefix, "this");
+ if (this[_state] === "unmapped" || this[_state] === "destroyed") {
+ throw new DOMException(
+ `${prefix}: buffer is not ready to be unmapped.`,
+ "OperationError",
+ );
+ }
+ if (this[_state] === "pending") {
+ // TODO(lucacasonato): this is not spec compliant.
+ throw new DOMException(
+ `${prefix}: can not unmap while mapping. This is a Deno limitation.`,
+ "OperationError",
+ );
+ } else if (
+ this[_state] === "mapped" || this[_state] === "mapped at creation"
+ ) {
+ /** @type {boolean} */
+ let write = false;
+ if (this[_state] === "mapped at creation") {
+ write = true;
+ } else if (this[_state] === "mapped") {
+ const mapMode = this[_mapMode];
+ if (mapMode === undefined) {
+ throw new DOMException(
+ `${prefix}: invalid state.`,
+ "OperationError",
+ );
+ }
+ if ((mapMode & 0x0002) === 0x0002) {
+ write = true;
+ }
+ }
+
+ const mappedRanges = this[_mappedRanges];
+ if (!mappedRanges) {
+ throw new DOMException(`${prefix}: invalid state.`, "OperationError");
+ }
+ for (let i = 0; i < mappedRanges.length; ++i) {
+ const { 0: buffer, 1: mappedRid } = mappedRanges[i];
+ const { err } = ops.op_webgpu_buffer_unmap(
+ bufferRid,
+ mappedRid,
+ ...new SafeArrayIterator(write ? [new Uint8Array(buffer)] : []),
+ );
+ device.pushError(err);
+ if (err) return;
+ }
+ this[_mappingRange] = null;
+ this[_mappedRanges] = null;
+ }
+
+ this[_state] = "unmapped";
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUBufferPrototype);
+ this[_cleanup]();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUBufferPrototype, this),
+ keys: [
+ "label",
+ "mapState",
+ "size",
+ "usage",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUBuffer", GPUBuffer);
+const GPUBufferPrototype = GPUBuffer.prototype;
+
+class GPUBufferUsage {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get MAP_READ() {
+ return 0x0001;
+ }
+ static get MAP_WRITE() {
+ return 0x0002;
+ }
+ static get COPY_SRC() {
+ return 0x0004;
+ }
+ static get COPY_DST() {
+ return 0x0008;
+ }
+ static get INDEX() {
+ return 0x0010;
+ }
+ static get VERTEX() {
+ return 0x0020;
+ }
+ static get UNIFORM() {
+ return 0x0040;
+ }
+ static get STORAGE() {
+ return 0x0080;
+ }
+ static get INDIRECT() {
+ return 0x0100;
+ }
+ static get QUERY_RESOLVE() {
+ return 0x0200;
+ }
+}
+
+class GPUMapMode {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get READ() {
+ return 0x0001;
+ }
+ static get WRITE() {
+ return 0x0002;
+ }
+}
+
+/**
+ * @param {GPUTextureDescriptor} descriptor
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUTexture}
+ */
+function createGPUTexture(descriptor, device, rid) {
+ /** @type {GPUTexture} */
+ const texture = webidl.createBranded(GPUTexture);
+ texture[_label] = descriptor.label;
+ texture[_device] = device;
+ texture[_rid] = rid;
+ texture[_views] = [];
+ texture[_width] = descriptor.size.width;
+ texture[_height] = descriptor.size.height;
+ texture[_depthOrArrayLayers] = descriptor.size.depthOrArrayLayers;
+ texture[_mipLevelCount] = descriptor.mipLevelCount;
+ texture[_sampleCount] = descriptor.sampleCount;
+ texture[_dimension] = descriptor.dimension;
+ texture[_format] = descriptor.format;
+ texture[_usage] = descriptor.usage;
+ return texture;
+}
+
+class GPUTexture {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+ /** @type {SafeWeakRef<GPUTextureView>[]} */
+ [_views];
+
+ /** @type {number} */
+ [_width];
+ /** @type {number} */
+ [_height];
+ /** @type {number} */
+ [_depthOrArrayLayers];
+ /** @type {number} */
+ [_mipLevelCount];
+ /** @type {number} */
+ [_sampleCount];
+ /** @type {GPUTextureDimension} */
+ [_dimension];
+ /** @type {GPUTextureFormat} */
+ [_format];
+ /** @type {number} */
+ [_usage];
+
+ [_cleanup]() {
+ const views = this[_views];
+ while (views.length > 0) {
+ const view = ArrayPrototypePop(views)?.deref();
+ if (view) {
+ view[_cleanup]();
+ }
+ }
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPUTextureViewDescriptor} descriptor
+ */
+ createView(descriptor = {}) {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ const prefix = "Failed to execute 'createView' on 'GPUTexture'";
+ webidl.requiredArguments(arguments.length, 0, prefix);
+ descriptor = webidl.converters.GPUTextureViewDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const textureRid = assertResource(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_create_texture_view({
+ textureRid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const textureView = createGPUTextureView(
+ descriptor.label,
+ this,
+ rid,
+ );
+ ArrayPrototypePush(this[_views], new SafeWeakRef(textureView));
+ return textureView;
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ this[_cleanup]();
+ }
+
+ get width() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_width];
+ }
+
+ get height() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_height];
+ }
+
+ get depthOrArrayLayers() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_depthOrArrayLayers];
+ }
+
+ get mipLevelCount() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_mipLevelCount];
+ }
+
+ get sampleCount() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_sampleCount];
+ }
+
+ get dimension() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_dimension];
+ }
+
+ get format() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_format];
+ }
+
+ get usage() {
+ webidl.assertBranded(this, GPUTexturePrototype);
+ return this[_usage];
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUTexturePrototype, this),
+ keys: [
+ "label",
+ "width",
+ "height",
+ "depthOrArrayLayers",
+ "mipLevelCount",
+ "sampleCount",
+ "dimension",
+ "format",
+ "usage",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUTexture", GPUTexture);
+const GPUTexturePrototype = GPUTexture.prototype;
+
+class GPUTextureUsage {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get COPY_SRC() {
+ return 0x01;
+ }
+ static get COPY_DST() {
+ return 0x02;
+ }
+ static get TEXTURE_BINDING() {
+ return 0x04;
+ }
+ static get STORAGE_BINDING() {
+ return 0x08;
+ }
+ static get RENDER_ATTACHMENT() {
+ return 0x10;
+ }
+}
+
+/**
+ * @param {string | null} label
+ * @param {GPUTexture} texture
+ * @param {number} rid
+ * @returns {GPUTextureView}
+ */
+function createGPUTextureView(label, texture, rid) {
+ /** @type {GPUTextureView} */
+ const textureView = webidl.createBranded(GPUTextureView);
+ textureView[_label] = label;
+ textureView[_texture] = texture;
+ textureView[_rid] = rid;
+ return textureView;
+}
+class GPUTextureView {
+ /** @type {GPUTexture} */
+ [_texture];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUTextureViewPrototype, this),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUTextureView", GPUTextureView);
+const GPUTextureViewPrototype = GPUTextureView.prototype;
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUSampler}
+ */
+function createGPUSampler(label, device, rid) {
+ /** @type {GPUSampler} */
+ const sampler = webidl.createBranded(GPUSampler);
+ sampler[_label] = label;
+ sampler[_device] = device;
+ sampler[_rid] = rid;
+ return sampler;
+}
+class GPUSampler {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+}
+GPUObjectBaseMixin("GPUSampler", GPUSampler);
+const GPUSamplerPrototype = GPUSampler.prototype;
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUBindGroupLayout}
+ */
+function createGPUBindGroupLayout(label, device, rid) {
+ /** @type {GPUBindGroupLayout} */
+ const bindGroupLayout = webidl.createBranded(GPUBindGroupLayout);
+ bindGroupLayout[_label] = label;
+ bindGroupLayout[_device] = device;
+ bindGroupLayout[_rid] = rid;
+ return bindGroupLayout;
+}
+class GPUBindGroupLayout {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPUBindGroupLayoutPrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUBindGroupLayout", GPUBindGroupLayout);
+const GPUBindGroupLayoutPrototype = GPUBindGroupLayout.prototype;
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUPipelineLayout}
+ */
+function createGPUPipelineLayout(label, device, rid) {
+ /** @type {GPUPipelineLayout} */
+ const pipelineLayout = webidl.createBranded(GPUPipelineLayout);
+ pipelineLayout[_label] = label;
+ pipelineLayout[_device] = device;
+ pipelineLayout[_rid] = rid;
+ return pipelineLayout;
+}
+class GPUPipelineLayout {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPUPipelineLayoutPrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUPipelineLayout", GPUPipelineLayout);
+const GPUPipelineLayoutPrototype = GPUPipelineLayout.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUBindGroup}
+ */
+function createGPUBindGroup(label, device, rid) {
+ /** @type {GPUBindGroup} */
+ const bindGroup = webidl.createBranded(GPUBindGroup);
+ bindGroup[_label] = label;
+ bindGroup[_device] = device;
+ bindGroup[_rid] = rid;
+ return bindGroup;
+}
+class GPUBindGroup {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUBindGroupPrototype, this),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUBindGroup", GPUBindGroup);
+const GPUBindGroupPrototype = GPUBindGroup.prototype;
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUShaderModule}
+ */
+function createGPUShaderModule(label, device, rid) {
+ /** @type {GPUShaderModule} */
+ const bindGroup = webidl.createBranded(GPUShaderModule);
+ bindGroup[_label] = label;
+ bindGroup[_device] = device;
+ bindGroup[_rid] = rid;
+ return bindGroup;
+}
+class GPUShaderModule {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUShaderModulePrototype, this),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUShaderModule", GPUShaderModule);
+const GPUShaderModulePrototype = GPUShaderModule.prototype;
+class GPUShaderStage {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get VERTEX() {
+ return 0x1;
+ }
+
+ static get FRAGMENT() {
+ return 0x2;
+ }
+
+ static get COMPUTE() {
+ return 0x4;
+ }
+}
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUComputePipeline}
+ */
+function createGPUComputePipeline(label, device, rid) {
+ /** @type {GPUComputePipeline} */
+ const pipeline = webidl.createBranded(GPUComputePipeline);
+ pipeline[_label] = label;
+ pipeline[_device] = device;
+ pipeline[_rid] = rid;
+ return pipeline;
+}
+class GPUComputePipeline {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {number} index
+ * @returns {GPUBindGroupLayout}
+ */
+ getBindGroupLayout(index) {
+ webidl.assertBranded(this, GPUComputePipelinePrototype);
+ const prefix =
+ "Failed to execute 'getBindGroupLayout' on 'GPUComputePipeline'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ index = webidl.converters["unsigned long"](index, prefix, "Argument 1");
+ const device = assertDevice(this, prefix, "this");
+ const computePipelineRid = assertResource(this, prefix, "this");
+ const { rid, label, err } = ops
+ .op_webgpu_compute_pipeline_get_bind_group_layout(
+ computePipelineRid,
+ index,
+ );
+ device.pushError(err);
+
+ const bindGroupLayout = createGPUBindGroupLayout(
+ label,
+ device,
+ rid,
+ );
+ device.trackResource(bindGroupLayout);
+ return bindGroupLayout;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPUComputePipelinePrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUComputePipeline", GPUComputePipeline);
+const GPUComputePipelinePrototype = GPUComputePipeline.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPURenderPipeline}
+ */
+function createGPURenderPipeline(label, device, rid) {
+ /** @type {GPURenderPipeline} */
+ const pipeline = webidl.createBranded(GPURenderPipeline);
+ pipeline[_label] = label;
+ pipeline[_device] = device;
+ pipeline[_rid] = rid;
+ return pipeline;
+}
+class GPURenderPipeline {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {number} index
+ */
+ getBindGroupLayout(index) {
+ webidl.assertBranded(this, GPURenderPipelinePrototype);
+ const prefix =
+ "Failed to execute 'getBindGroupLayout' on 'GPURenderPipeline'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ index = webidl.converters["unsigned long"](index, prefix, "Argument 1");
+ const device = assertDevice(this, prefix, "this");
+ const renderPipelineRid = assertResource(this, prefix, "this");
+ const { rid, label, err } = ops
+ .op_webgpu_render_pipeline_get_bind_group_layout(
+ renderPipelineRid,
+ index,
+ );
+ device.pushError(err);
+
+ const bindGroupLayout = createGPUBindGroupLayout(
+ label,
+ device,
+ rid,
+ );
+ device.trackResource(bindGroupLayout);
+ return bindGroupLayout;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPURenderPipelinePrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPURenderPipeline", GPURenderPipeline);
+const GPURenderPipelinePrototype = GPURenderPipeline.prototype;
+
+class GPUColorWrite {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get RED() {
+ return 0x1;
+ }
+ static get GREEN() {
+ return 0x2;
+ }
+ static get BLUE() {
+ return 0x4;
+ }
+ static get ALPHA() {
+ return 0x8;
+ }
+ static get ALL() {
+ return 0xF;
+ }
+}
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUCommandEncoder}
+ */
+function createGPUCommandEncoder(label, device, rid) {
+ /** @type {GPUCommandEncoder} */
+ const encoder = webidl.createBranded(GPUCommandEncoder);
+ encoder[_label] = label;
+ encoder[_device] = device;
+ encoder[_rid] = rid;
+ encoder[_encoders] = [];
+ return encoder;
+}
+class GPUCommandEncoder {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+ /** @type {SafeWeakRef<GPURenderPassEncoder | GPUComputePassEncoder>[]} */
+ [_encoders];
+
+ [_cleanup]() {
+ const encoders = this[_encoders];
+ while (encoders.length > 0) {
+ const encoder = ArrayPrototypePop(encoders)?.deref();
+ if (encoder) {
+ encoder[_cleanup]();
+ }
+ }
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPURenderPassDescriptor} descriptor
+ * @return {GPURenderPassEncoder}
+ */
+ beginRenderPass(descriptor) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix = "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ descriptor = webidl.converters.GPURenderPassDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+
+ if (this[_rid] === undefined) {
+ throw new DOMException(
+ "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder': already consumed",
+ "OperationError",
+ );
+ }
+
+ let depthStencilAttachment;
+ if (descriptor.depthStencilAttachment) {
+ if (
+ descriptor.depthStencilAttachment.depthLoadOp === "clear" &&
+ !(ObjectHasOwn(descriptor.depthStencilAttachment, "depthClearValue"))
+ ) {
+ throw webidl.makeException(
+ TypeError,
+ '`depthClearValue` must be specified when `depthLoadOp` is "clear"',
+ prefix,
+ "Argument 1",
+ );
+ }
+
+ const view = assertResource(
+ descriptor.depthStencilAttachment.view,
+ prefix,
+ "texture view for depth stencil attachment",
+ );
+ assertDeviceMatch(
+ device,
+ descriptor.depthStencilAttachment.view[_texture],
+ {
+ prefix,
+ resourceContext: "texture view for depth stencil attachment",
+ selfContext: "this",
+ },
+ );
+
+ depthStencilAttachment = {
+ ...descriptor.depthStencilAttachment,
+ view,
+ };
+ }
+ const colorAttachments = ArrayPrototypeMap(
+ descriptor.colorAttachments,
+ (colorAttachment, i) => {
+ const context = `color attachment ${i + 1}`;
+ const view = assertResource(
+ colorAttachment.view,
+ prefix,
+ `texture view for ${context}`,
+ );
+ assertResource(
+ colorAttachment.view[_texture],
+ prefix,
+ `texture backing texture view for ${context}`,
+ );
+ assertDeviceMatch(
+ device,
+ colorAttachment.view[_texture],
+ {
+ prefix,
+ resourceContext: `texture view for ${context}`,
+ selfContext: "this",
+ },
+ );
+ let resolveTarget;
+ if (colorAttachment.resolveTarget) {
+ resolveTarget = assertResource(
+ colorAttachment.resolveTarget,
+ prefix,
+ `resolve target texture view for ${context}`,
+ );
+ assertResource(
+ colorAttachment.resolveTarget[_texture],
+ prefix,
+ `texture backing resolve target texture view for ${context}`,
+ );
+ assertDeviceMatch(
+ device,
+ colorAttachment.resolveTarget[_texture],
+ {
+ prefix,
+ resourceContext: `resolve target texture view for ${context}`,
+ selfContext: "this",
+ },
+ );
+ }
+ return {
+ view: view,
+ resolveTarget,
+ storeOp: colorAttachment.storeOp,
+ loadOp: colorAttachment.loadOp,
+ clearValue: normalizeGPUColor(colorAttachment.clearValue),
+ };
+ },
+ );
+
+ let occlusionQuerySet;
+
+ if (descriptor.occlusionQuerySet) {
+ occlusionQuerySet = assertResource(
+ descriptor.occlusionQuerySet,
+ prefix,
+ "occlusionQuerySet",
+ );
+ }
+
+ let timestampWrites = null;
+ if (descriptor.timestampWrites) {
+ const querySet = assertResource(
+ descriptor.timestampWrites.querySet,
+ prefix,
+ "querySet",
+ );
+
+ timestampWrites = {
+ querySet,
+ beginningOfPassWriteIndex:
+ descriptor.timestampWrites.beginningOfPassWriteIndex,
+ endOfPassWriteIndex: descriptor.timestampWrites.endOfPassWriteIndex,
+ };
+ }
+
+ const { rid } = ops.op_webgpu_command_encoder_begin_render_pass(
+ commandEncoderRid,
+ descriptor.label,
+ colorAttachments,
+ depthStencilAttachment,
+ occlusionQuerySet,
+ timestampWrites,
+ );
+
+ const renderPassEncoder = createGPURenderPassEncoder(
+ descriptor.label,
+ this,
+ rid,
+ );
+ ArrayPrototypePush(this[_encoders], new SafeWeakRef(renderPassEncoder));
+ return renderPassEncoder;
+ }
+
+ /**
+ * @param {GPUComputePassDescriptor} descriptor
+ */
+ beginComputePass(descriptor = {}) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix =
+ "Failed to execute 'beginComputePass' on 'GPUCommandEncoder'";
+ descriptor = webidl.converters.GPUComputePassDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+
+ assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+
+ let timestampWrites = null;
+ if (descriptor.timestampWrites) {
+ const querySet = assertResource(
+ descriptor.timestampWrites.querySet,
+ prefix,
+ "querySet",
+ );
+
+ timestampWrites = {
+ querySet,
+ beginningOfPassWriteIndex:
+ descriptor.timestampWrites.beginningOfPassWriteIndex,
+ endOfPassWriteIndex: descriptor.timestampWrites.endOfPassWriteIndex,
+ };
+ }
+
+ const { rid } = ops.op_webgpu_command_encoder_begin_compute_pass(
+ commandEncoderRid,
+ descriptor.label,
+ timestampWrites,
+ );
+
+ const computePassEncoder = createGPUComputePassEncoder(
+ descriptor.label,
+ this,
+ rid,
+ );
+ ArrayPrototypePush(this[_encoders], new SafeWeakRef(computePassEncoder));
+ return computePassEncoder;
+ }
+
+ /**
+ * @param {GPUBuffer} source
+ * @param {number} sourceOffset
+ * @param {GPUBuffer} destination
+ * @param {number} destinationOffset
+ * @param {number} size
+ */
+ copyBufferToBuffer(
+ source,
+ sourceOffset,
+ destination,
+ destinationOffset,
+ size,
+ ) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix =
+ "Failed to execute 'copyBufferToBuffer' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 5, prefix);
+ source = webidl.converters.GPUBuffer(source, prefix, "Argument 1");
+ sourceOffset = webidl.converters.GPUSize64(
+ sourceOffset,
+ prefix,
+ "Argument 2",
+ );
+ destination = webidl.converters.GPUBuffer(
+ destination,
+ prefix,
+ "Argument 3",
+ );
+ destinationOffset = webidl.converters.GPUSize64(
+ destinationOffset,
+ prefix,
+ "Argument 4",
+ );
+ size = webidl.converters.GPUSize64(size, prefix, "Argument 5");
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const sourceRid = assertResource(source, prefix, "Argument 1");
+ assertDeviceMatch(device, source, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ const destinationRid = assertResource(destination, prefix, "Argument 3");
+ assertDeviceMatch(device, destination, {
+ prefix,
+ resourceContext: "Argument 3",
+ selfContext: "this",
+ });
+
+ const { err } = ops.op_webgpu_command_encoder_copy_buffer_to_buffer(
+ commandEncoderRid,
+ sourceRid,
+ sourceOffset,
+ destinationRid,
+ destinationOffset,
+ size,
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyBuffer} source
+ * @param {GPUImageCopyTexture} destination
+ * @param {GPUExtent3D} copySize
+ */
+ copyBufferToTexture(source, destination, copySize) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix =
+ "Failed to execute 'copyBufferToTexture' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 3, prefix);
+ source = webidl.converters.GPUImageCopyBuffer(source, prefix, "Argument 1");
+ destination = webidl.converters.GPUImageCopyTexture(
+ destination,
+ prefix,
+ "Argument 2",
+ );
+ copySize = webidl.converters.GPUExtent3D(copySize, prefix, "Argument 3");
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const sourceBufferRid = assertResource(
+ // deno-lint-ignore prefer-primordials
+ source.buffer,
+ prefix,
+ "source in Argument 1",
+ );
+ // deno-lint-ignore prefer-primordials
+ assertDeviceMatch(device, source.buffer, {
+ prefix,
+ resourceContext: "source in Argument 1",
+ selfContext: "this",
+ });
+ const destinationTextureRid = assertResource(
+ destination.texture,
+ prefix,
+ "texture in Argument 2",
+ );
+ assertDeviceMatch(device, destination.texture, {
+ prefix,
+ resourceContext: "texture in Argument 2",
+ selfContext: "this",
+ });
+
+ const { err } = ops.op_webgpu_command_encoder_copy_buffer_to_texture(
+ commandEncoderRid,
+ {
+ ...source,
+ buffer: sourceBufferRid,
+ },
+ {
+ texture: destinationTextureRid,
+ mipLevel: destination.mipLevel,
+ origin: destination.origin
+ ? normalizeGPUOrigin3D(destination.origin)
+ : undefined,
+ aspect: destination.aspect,
+ },
+ normalizeGPUExtent3D(copySize),
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyTexture} source
+ * @param {GPUImageCopyBuffer} destination
+ * @param {GPUExtent3D} copySize
+ */
+ copyTextureToBuffer(source, destination, copySize) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix =
+ "Failed to execute 'copyTextureToBuffer' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 3, prefix);
+ source = webidl.converters.GPUImageCopyTexture(
+ source,
+ prefix,
+ "Argument 1",
+ );
+ destination = webidl.converters.GPUImageCopyBuffer(
+ destination,
+ prefix,
+ "Argument 2",
+ );
+ copySize = webidl.converters.GPUExtent3D(copySize, prefix, "Argument 3");
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const sourceTextureRid = assertResource(
+ source.texture,
+ prefix,
+ "texture in Argument 1",
+ );
+ assertDeviceMatch(device, source.texture, {
+ prefix,
+ resourceContext: "texture in Argument 1",
+ selfContext: "this",
+ });
+ const destinationBufferRid = assertResource(
+ // deno-lint-ignore prefer-primordials
+ destination.buffer,
+ prefix,
+ "buffer in Argument 2",
+ );
+ // deno-lint-ignore prefer-primordials
+ assertDeviceMatch(device, destination.buffer, {
+ prefix,
+ resourceContext: "buffer in Argument 2",
+ selfContext: "this",
+ });
+ const { err } = ops.op_webgpu_command_encoder_copy_texture_to_buffer(
+ commandEncoderRid,
+ {
+ texture: sourceTextureRid,
+ mipLevel: source.mipLevel,
+ origin: source.origin ? normalizeGPUOrigin3D(source.origin) : undefined,
+ aspect: source.aspect,
+ },
+ {
+ ...destination,
+ buffer: destinationBufferRid,
+ },
+ normalizeGPUExtent3D(copySize),
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyTexture} source
+ * @param {GPUImageCopyTexture} destination
+ * @param {GPUExtent3D} copySize
+ */
+ copyTextureToTexture(source, destination, copySize) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix =
+ "Failed to execute 'copyTextureToTexture' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 3, prefix);
+ source = webidl.converters.GPUImageCopyTexture(
+ source,
+ prefix,
+ "Argument 1",
+ );
+ destination = webidl.converters.GPUImageCopyTexture(
+ destination,
+ prefix,
+ "Argument 2",
+ );
+ copySize = webidl.converters.GPUExtent3D(copySize, prefix, "Argument 3");
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const sourceTextureRid = assertResource(
+ source.texture,
+ prefix,
+ "texture in Argument 1",
+ );
+ assertDeviceMatch(device, source.texture, {
+ prefix,
+ resourceContext: "texture in Argument 1",
+ selfContext: "this",
+ });
+ const destinationTextureRid = assertResource(
+ destination.texture,
+ prefix,
+ "texture in Argument 2",
+ );
+ assertDeviceMatch(device, destination.texture, {
+ prefix,
+ resourceContext: "texture in Argument 2",
+ selfContext: "this",
+ });
+ const { err } = ops.op_webgpu_command_encoder_copy_texture_to_texture(
+ commandEncoderRid,
+ {
+ texture: sourceTextureRid,
+ mipLevel: source.mipLevel,
+ origin: source.origin ? normalizeGPUOrigin3D(source.origin) : undefined,
+ aspect: source.aspect,
+ },
+ {
+ texture: destinationTextureRid,
+ mipLevel: destination.mipLevel,
+ origin: destination.origin
+ ? normalizeGPUOrigin3D(destination.origin)
+ : undefined,
+ aspect: source.aspect,
+ },
+ normalizeGPUExtent3D(copySize),
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUBuffer} buffer
+ * @param {GPUSize64} offset
+ * @param {GPUSize64} size
+ */
+ clearBuffer(buffer, offset = 0, size = undefined) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix = "Failed to execute 'clearBuffer' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 3, prefix);
+ buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 1");
+ offset = webidl.converters.GPUSize64(offset, prefix, "Argument 2");
+ size = webidl.converters.GPUSize64(size, prefix, "Argument 3");
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const bufferRid = assertResource(buffer, prefix, "Argument 1");
+ const { err } = ops.op_webgpu_command_encoder_clear_buffer(
+ commandEncoderRid,
+ bufferRid,
+ offset,
+ size,
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix = "Failed to execute 'pushDebugGroup' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const { err } = ops.op_webgpu_command_encoder_push_debug_group(
+ commandEncoderRid,
+ groupLabel,
+ );
+ device.pushError(err);
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix = "Failed to execute 'popDebugGroup' on 'GPUCommandEncoder'";
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const { err } = ops.op_webgpu_command_encoder_pop_debug_group(
+ commandEncoderRid,
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ markerLabel = webidl.converters.USVString(
+ markerLabel,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const { err } = ops.op_webgpu_command_encoder_insert_debug_marker(
+ commandEncoderRid,
+ markerLabel,
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUQuerySet} querySet
+ * @param {number} queryIndex
+ */
+ writeTimestamp(querySet, queryIndex) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix = "Failed to execute 'writeTimestamp' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ querySet = webidl.converters.GPUQuerySet(querySet, prefix, "Argument 1");
+ queryIndex = webidl.converters.GPUSize32(queryIndex, prefix, "Argument 2");
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const querySetRid = assertResource(querySet, prefix, "Argument 1");
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ const { err } = ops.op_webgpu_command_encoder_write_timestamp(
+ commandEncoderRid,
+ querySetRid,
+ queryIndex,
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUQuerySet} querySet
+ * @param {number} firstQuery
+ * @param {number} queryCount
+ * @param {GPUBuffer} destination
+ * @param {number} destinationOffset
+ */
+ resolveQuerySet(
+ querySet,
+ firstQuery,
+ queryCount,
+ destination,
+ destinationOffset,
+ ) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix = "Failed to execute 'resolveQuerySet' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 5, { prefix });
+ querySet = webidl.converters.GPUQuerySet(querySet, prefix, "Argument 1");
+ firstQuery = webidl.converters.GPUSize32(firstQuery, prefix, "Argument 2");
+ queryCount = webidl.converters.GPUSize32(queryCount, prefix, "Argument 3");
+ destination = webidl.converters.GPUBuffer(
+ destination,
+ prefix,
+ "Argument 4",
+ );
+ destinationOffset = webidl.converters.GPUSize64(
+ destinationOffset,
+ prefix,
+ "Argument 5",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const querySetRid = assertResource(querySet, prefix, "Argument 1");
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ const destinationRid = assertResource(destination, prefix, "Argument 3");
+ assertDeviceMatch(device, destination, {
+ prefix,
+ resourceContext: "Argument 3",
+ selfContext: "this",
+ });
+ const { err } = ops.op_webgpu_command_encoder_resolve_query_set(
+ commandEncoderRid,
+ querySetRid,
+ firstQuery,
+ queryCount,
+ destinationRid,
+ destinationOffset,
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUCommandBufferDescriptor} descriptor
+ * @returns {GPUCommandBuffer}
+ */
+ finish(descriptor = {}) {
+ webidl.assertBranded(this, GPUCommandEncoderPrototype);
+ const prefix = "Failed to execute 'finish' on 'GPUCommandEncoder'";
+ descriptor = webidl.converters.GPUCommandBufferDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const commandEncoderRid = assertResource(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_command_encoder_finish(
+ commandEncoderRid,
+ descriptor.label,
+ );
+ device.pushError(err);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+
+ const commandBuffer = createGPUCommandBuffer(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(commandBuffer);
+ return commandBuffer;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPUCommandEncoderPrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUCommandEncoder", GPUCommandEncoder);
+const GPUCommandEncoderPrototype = GPUCommandEncoder.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {GPUCommandEncoder} encoder
+ * @param {number} rid
+ * @returns {GPURenderPassEncoder}
+ */
+function createGPURenderPassEncoder(label, encoder, rid) {
+ /** @type {GPURenderPassEncoder} */
+ const passEncoder = webidl.createBranded(GPURenderPassEncoder);
+ passEncoder[_label] = label;
+ passEncoder[_encoder] = encoder;
+ passEncoder[_rid] = rid;
+ return passEncoder;
+}
+
+class GPURenderPassEncoder {
+ /** @type {GPUCommandEncoder} */
+ [_encoder];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {number} x
+ * @param {number} y
+ * @param {number} width
+ * @param {number} height
+ * @param {number} minDepth
+ * @param {number} maxDepth
+ */
+ setViewport(x, y, width, height, minDepth, maxDepth) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix = "Failed to execute 'setViewport' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 6, { prefix });
+ x = webidl.converters.float(x, prefix, "Argument 1");
+ y = webidl.converters.float(y, prefix, "Argument 2");
+ width = webidl.converters.float(width, prefix, "Argument 3");
+ height = webidl.converters.float(height, prefix, "Argument 4");
+ minDepth = webidl.converters.float(minDepth, prefix, "Argument 5");
+ maxDepth = webidl.converters.float(maxDepth, prefix, "Argument 6");
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_set_viewport({
+ renderPassRid,
+ x,
+ y,
+ width,
+ height,
+ minDepth,
+ maxDepth,
+ });
+ }
+
+ /**
+ * @param {number} x
+ * @param {number} y
+ * @param {number} width
+ * @param {number} height
+ */
+ setScissorRect(x, y, width, height) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setScissorRect' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 4, prefix);
+ x = webidl.converters.GPUIntegerCoordinate(x, prefix, "Argument 1");
+ y = webidl.converters.GPUIntegerCoordinate(y, prefix, "Argument 2");
+ width = webidl.converters.GPUIntegerCoordinate(width, prefix, "Argument 3");
+ height = webidl.converters.GPUIntegerCoordinate(
+ height,
+ prefix,
+ "Argument 4",
+ );
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_set_scissor_rect(
+ renderPassRid,
+ x,
+ y,
+ width,
+ height,
+ );
+ }
+
+ /**
+ * @param {GPUColor} color
+ */
+ setBlendConstant(color) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setBlendConstant' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ color = webidl.converters.GPUColor(color, prefix, "Argument 1");
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_set_blend_constant(
+ renderPassRid,
+ normalizeGPUColor(color),
+ );
+ }
+
+ /**
+ * @param {number} reference
+ */
+ setStencilReference(reference) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setStencilReference' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ reference = webidl.converters.GPUStencilValue(
+ reference,
+ prefix,
+ "Argument 1",
+ );
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_set_stencil_reference(
+ renderPassRid,
+ reference,
+ );
+ }
+
+ /**
+ * @param {number} queryIndex
+ */
+ beginOcclusionQuery(queryIndex) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'beginOcclusionQuery' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ queryIndex = webidl.converters.GPUSize32(queryIndex, prefix, "Argument 1");
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_begin_occlusion_query(
+ renderPassRid,
+ queryIndex,
+ );
+ }
+
+ endOcclusionQuery() {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'endOcclusionQuery' on 'GPUComputePassEncoder'";
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_end_occlusion_query(renderPassRid);
+ }
+
+ /**
+ * @param {GPURenderBundle[]} bundles
+ */
+ executeBundles(bundles) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'executeBundles' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ bundles = webidl.converters["sequence<GPURenderBundle>"](
+ bundles,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ const bundleRids = ArrayPrototypeMap(bundles, (bundle, i) => {
+ const context = `bundle ${i + 1}`;
+ const rid = assertResource(bundle, prefix, context);
+ assertDeviceMatch(device, bundle, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return rid;
+ });
+ ops.op_webgpu_render_pass_execute_bundles(renderPassRid, bundleRids);
+ }
+
+ end() {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix = "Failed to execute 'end' on 'GPURenderPassEncoder'";
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ const commandEncoderRid = assertResource(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ const renderPassRid = assertResource(this, prefix, "this");
+ const { err } = ops.op_webgpu_render_pass_end(
+ commandEncoderRid,
+ renderPassRid,
+ );
+ device.pushError(err);
+ this[_rid] = undefined;
+ }
+
+ // TODO(lucacasonato): has an overload
+ setBindGroup(
+ index,
+ bindGroup,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ ) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix = "Failed to execute 'setBindGroup' on 'GPURenderPassEncoder'";
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ const bindGroupRid = assertResource(bindGroup, prefix, "Argument 2");
+ assertDeviceMatch(device, bindGroup, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ if (
+ !(ObjectPrototypeIsPrototypeOf(
+ Uint32ArrayPrototype,
+ dynamicOffsetsData,
+ ))
+ ) {
+ dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []);
+ dynamicOffsetsDataStart = 0;
+ dynamicOffsetsDataLength = dynamicOffsetsData.length;
+ }
+ ops.op_webgpu_render_pass_set_bind_group(
+ renderPassRid,
+ index,
+ bindGroupRid,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ );
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'pushDebugGroup' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_push_debug_group(renderPassRid, groupLabel);
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'popDebugGroup' on 'GPURenderPassEncoder'";
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_pop_debug_group(renderPassRid);
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ markerLabel = webidl.converters.USVString(
+ markerLabel,
+ prefix,
+ "Argument 1",
+ );
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_insert_debug_marker(renderPassRid, markerLabel);
+ }
+
+ /**
+ * @param {GPURenderPipeline} pipeline
+ */
+ setPipeline(pipeline) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix = "Failed to execute 'setPipeline' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ pipeline = webidl.converters.GPURenderPipeline(
+ pipeline,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ const pipelineRid = assertResource(pipeline, prefix, "Argument 1");
+ assertDeviceMatch(device, pipeline, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_pass_set_pipeline(renderPassRid, pipelineRid);
+ }
+
+ /**
+ * @param {GPUBuffer} buffer
+ * @param {GPUIndexFormat} indexFormat
+ * @param {number} offset
+ * @param {number} size
+ */
+ setIndexBuffer(buffer, indexFormat, offset = 0, size) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setIndexBuffer' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 1");
+ indexFormat = webidl.converters.GPUIndexFormat(
+ indexFormat,
+ prefix,
+ "Argument 2",
+ );
+ offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
+ if (size !== undefined) {
+ size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
+ }
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ const bufferRid = assertResource(buffer, prefix, "Argument 1");
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_pass_set_index_buffer(
+ renderPassRid,
+ bufferRid,
+ indexFormat,
+ offset,
+ size,
+ );
+ }
+
+ /**
+ * @param {number} slot
+ * @param {GPUBuffer} buffer
+ * @param {number} offset
+ * @param {number} size
+ */
+ setVertexBuffer(slot, buffer, offset = 0, size) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setVertexBuffer' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ slot = webidl.converters.GPUSize32(slot, prefix, "Argument 1");
+ buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 2");
+ offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
+ if (size !== undefined) {
+ size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
+ }
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ const bufferRid = assertResource(buffer, prefix, "Argument 2");
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_pass_set_vertex_buffer(
+ renderPassRid,
+ slot,
+ bufferRid,
+ offset,
+ size,
+ );
+ }
+
+ /**
+ * @param {number} vertexCount
+ * @param {number} instanceCount
+ * @param {number} firstVertex
+ * @param {number} firstInstance
+ */
+ draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix = "Failed to execute 'draw' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ vertexCount = webidl.converters.GPUSize32(
+ vertexCount,
+ prefix,
+ "Argument 1",
+ );
+ instanceCount = webidl.converters.GPUSize32(
+ instanceCount,
+ prefix,
+ "Argument 2",
+ );
+ firstVertex = webidl.converters.GPUSize32(
+ firstVertex,
+ prefix,
+ "Argument 3",
+ );
+ firstInstance = webidl.converters.GPUSize32(
+ firstInstance,
+ prefix,
+ "Argument 4",
+ );
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_draw(
+ renderPassRid,
+ vertexCount,
+ instanceCount,
+ firstVertex,
+ firstInstance,
+ );
+ }
+
+ /**
+ * @param {number} indexCount
+ * @param {number} instanceCount
+ * @param {number} firstIndex
+ * @param {number} baseVertex
+ * @param {number} firstInstance
+ */
+ drawIndexed(
+ indexCount,
+ instanceCount = 1,
+ firstIndex = 0,
+ baseVertex = 0,
+ firstInstance = 0,
+ ) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix = "Failed to execute 'drawIndexed' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ indexCount = webidl.converters.GPUSize32(indexCount, prefix, "Argument 1");
+ instanceCount = webidl.converters.GPUSize32(
+ instanceCount,
+ prefix,
+ "Argument 2",
+ );
+ firstIndex = webidl.converters.GPUSize32(firstIndex, prefix, "Argument 3");
+ baseVertex = webidl.converters.GPUSignedOffset32(
+ baseVertex,
+ prefix,
+ "Argument 4",
+ );
+ firstInstance = webidl.converters.GPUSize32(
+ firstInstance,
+ prefix,
+ "Argument 5",
+ );
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_pass_draw_indexed(
+ renderPassRid,
+ indexCount,
+ instanceCount,
+ firstIndex,
+ baseVertex,
+ firstInstance,
+ );
+ }
+
+ /**
+ * @param {GPUBuffer} indirectBuffer
+ * @param {number} indirectOffset
+ */
+ drawIndirect(indirectBuffer, indirectOffset) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix = "Failed to execute 'drawIndirect' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ indirectBuffer = webidl.converters.GPUBuffer(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ indirectOffset = webidl.converters.GPUSize64(
+ indirectOffset,
+ prefix,
+ "Argument 2",
+ );
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ const indirectBufferRid = assertResource(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_pass_draw_indirect(
+ renderPassRid,
+ indirectBufferRid,
+ indirectOffset,
+ );
+ }
+
+ /**
+ * @param {GPUBuffer} indirectBuffer
+ * @param {number} indirectOffset
+ */
+ drawIndexedIndirect(indirectBuffer, indirectOffset) {
+ webidl.assertBranded(this, GPURenderPassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'drawIndexedIndirect' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ indirectBuffer = webidl.converters.GPUBuffer(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ indirectOffset = webidl.converters.GPUSize64(
+ indirectOffset,
+ prefix,
+ "Argument 2",
+ );
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const renderPassRid = assertResource(this, prefix, "this");
+ const indirectBufferRid = assertResource(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_pass_draw_indexed_indirect(
+ renderPassRid,
+ indirectBufferRid,
+ indirectOffset,
+ );
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPURenderPassEncoderPrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPURenderPassEncoder", GPURenderPassEncoder);
+const GPURenderPassEncoderPrototype = GPURenderPassEncoder.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {GPUCommandEncoder} encoder
+ * @param {number} rid
+ * @returns {GPUComputePassEncoder}
+ */
+function createGPUComputePassEncoder(label, encoder, rid) {
+ /** @type {GPUComputePassEncoder} */
+ const computePassEncoder = webidl.createBranded(GPUComputePassEncoder);
+ computePassEncoder[_label] = label;
+ computePassEncoder[_encoder] = encoder;
+ computePassEncoder[_rid] = rid;
+ return computePassEncoder;
+}
+
+class GPUComputePassEncoder {
+ /** @type {GPUCommandEncoder} */
+ [_encoder];
+
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPUComputePipeline} pipeline
+ */
+ setPipeline(pipeline) {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix = "Failed to execute 'setPipeline' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ pipeline = webidl.converters.GPUComputePipeline(
+ pipeline,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const computePassRid = assertResource(this, prefix, "this");
+ const pipelineRid = assertResource(pipeline, prefix, "Argument 1");
+ assertDeviceMatch(device, pipeline, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_compute_pass_set_pipeline(computePassRid, pipelineRid);
+ }
+
+ /**
+ * @param {number} workgroupCountX
+ * @param {number} workgroupCountY
+ * @param {number} workgroupCountZ
+ */
+ dispatchWorkgroups(
+ workgroupCountX,
+ workgroupCountY = 1,
+ workgroupCountZ = 1,
+ ) {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'dispatchWorkgroups' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ workgroupCountX = webidl.converters.GPUSize32(
+ workgroupCountX,
+ prefix,
+ "Argument 1",
+ );
+ workgroupCountY = webidl.converters.GPUSize32(
+ workgroupCountY,
+ prefix,
+ "Argument 2",
+ );
+ workgroupCountZ = webidl.converters.GPUSize32(
+ workgroupCountZ,
+ prefix,
+ "Argument 3",
+ );
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const computePassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_compute_pass_dispatch_workgroups(
+ computePassRid,
+ workgroupCountX,
+ workgroupCountY,
+ workgroupCountZ,
+ );
+ }
+
+ /**
+ * @param {GPUBuffer} indirectBuffer
+ * @param {number} indirectOffset
+ */
+ dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset) {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'dispatchWorkgroupsIndirect' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ indirectBuffer = webidl.converters.GPUBuffer(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ indirectOffset = webidl.converters.GPUSize64(
+ indirectOffset,
+ prefix,
+ "Argument 2",
+ );
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const computePassRid = assertResource(this, prefix, "this");
+ const indirectBufferRid = assertResource(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_compute_pass_dispatch_workgroups_indirect(
+ computePassRid,
+ indirectBufferRid,
+ indirectOffset,
+ );
+ }
+
+ end() {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix = "Failed to execute 'end' on 'GPUComputePassEncoder'";
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ const commandEncoderRid = assertResource(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ const computePassRid = assertResource(this, prefix, "this");
+ const { err } = ops.op_webgpu_compute_pass_end(
+ commandEncoderRid,
+ computePassRid,
+ );
+ device.pushError(err);
+ this[_rid] = undefined;
+ }
+
+ // TODO(lucacasonato): has an overload
+ setBindGroup(
+ index,
+ bindGroup,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ ) {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'";
+ const device = assertDevice(
+ this[_encoder],
+ prefix,
+ "encoder referenced by this",
+ );
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const computePassRid = assertResource(this, prefix, "this");
+ const bindGroupRid = assertResource(bindGroup, prefix, "Argument 2");
+ assertDeviceMatch(device, bindGroup, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ if (
+ !(ObjectPrototypeIsPrototypeOf(
+ Uint32ArrayPrototype,
+ dynamicOffsetsData,
+ ))
+ ) {
+ dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []);
+ dynamicOffsetsDataStart = 0;
+ dynamicOffsetsDataLength = dynamicOffsetsData.length;
+ }
+ ops.op_webgpu_compute_pass_set_bind_group(
+ computePassRid,
+ index,
+ bindGroupRid,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ );
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'pushDebugGroup' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const computePassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_compute_pass_push_debug_group(computePassRid, groupLabel);
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'popDebugGroup' on 'GPUComputePassEncoder'";
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const computePassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_compute_pass_pop_debug_group(computePassRid);
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPUComputePassEncoderPrototype);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ markerLabel = webidl.converters.USVString(
+ markerLabel,
+ prefix,
+ "Argument 1",
+ );
+ assertDevice(this[_encoder], prefix, "encoder referenced by this");
+ assertResource(this[_encoder], prefix, "encoder referenced by this");
+ const computePassRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_compute_pass_insert_debug_marker(
+ computePassRid,
+ markerLabel,
+ );
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPUComputePassEncoderPrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUComputePassEncoder", GPUComputePassEncoder);
+const GPUComputePassEncoderPrototype = GPUComputePassEncoder.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUCommandBuffer}
+ */
+function createGPUCommandBuffer(label, device, rid) {
+ /** @type {GPUCommandBuffer} */
+ const commandBuffer = webidl.createBranded(GPUCommandBuffer);
+ commandBuffer[_label] = label;
+ commandBuffer[_device] = device;
+ commandBuffer[_rid] = rid;
+ return commandBuffer;
+}
+
+class GPUCommandBuffer {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUCommandBufferPrototype, this),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUCommandBuffer", GPUCommandBuffer);
+const GPUCommandBufferPrototype = GPUCommandBuffer.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPURenderBundleEncoder}
+ */
+function createGPURenderBundleEncoder(label, device, rid) {
+ /** @type {GPURenderBundleEncoder} */
+ const bundleEncoder = webidl.createBranded(GPURenderBundleEncoder);
+ bundleEncoder[_label] = label;
+ bundleEncoder[_device] = device;
+ bundleEncoder[_rid] = rid;
+ return bundleEncoder;
+}
+
+class GPURenderBundleEncoder {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPURenderBundleDescriptor} descriptor
+ */
+ finish(descriptor = {}) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix = "Failed to execute 'finish' on 'GPURenderBundleEncoder'";
+ descriptor = webidl.converters.GPURenderBundleDescriptor(
+ descriptor,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ const { rid, err } = ops.op_webgpu_render_bundle_encoder_finish(
+ renderBundleEncoderRid,
+ descriptor.label,
+ );
+ device.pushError(err);
+ this[_rid] = undefined;
+
+ const renderBundle = createGPURenderBundle(
+ descriptor.label,
+ device,
+ rid,
+ );
+ device.trackResource(renderBundle);
+ return renderBundle;
+ }
+
+ // TODO(lucacasonato): has an overload
+ setBindGroup(
+ index,
+ bindGroup,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ ) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setBindGroup' on 'GPURenderBundleEncoder'";
+ const device = assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ const bindGroupRid = assertResource(bindGroup, prefix, "Argument 2");
+ assertDeviceMatch(device, bindGroup, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ if (
+ !(ObjectPrototypeIsPrototypeOf(
+ Uint32ArrayPrototype,
+ dynamicOffsetsData,
+ ))
+ ) {
+ dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []);
+ dynamicOffsetsDataStart = 0;
+ dynamicOffsetsDataLength = dynamicOffsetsData.length;
+ }
+ ops.op_webgpu_render_bundle_encoder_set_bind_group(
+ renderBundleEncoderRid,
+ index,
+ bindGroupRid,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ );
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'pushDebugGroup' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
+ assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_bundle_encoder_push_debug_group(
+ renderBundleEncoderRid,
+ groupLabel,
+ );
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'popDebugGroup' on 'GPURenderBundleEncoder'";
+ assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_bundle_encoder_pop_debug_group(
+ renderBundleEncoderRid,
+ );
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ markerLabel = webidl.converters.USVString(
+ markerLabel,
+ prefix,
+ "Argument 1",
+ );
+ assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_bundle_encoder_insert_debug_marker(
+ renderBundleEncoderRid,
+ markerLabel,
+ );
+ }
+
+ /**
+ * @param {GPURenderPipeline} pipeline
+ */
+ setPipeline(pipeline) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setPipeline' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ pipeline = webidl.converters.GPURenderPipeline(
+ pipeline,
+ prefix,
+ "Argument 1",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ const pipelineRid = assertResource(pipeline, prefix, "Argument 1");
+ assertDeviceMatch(device, pipeline, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_bundle_encoder_set_pipeline(
+ renderBundleEncoderRid,
+ pipelineRid,
+ );
+ }
+
+ /**
+ * @param {GPUBuffer} buffer
+ * @param {GPUIndexFormat} indexFormat
+ * @param {number} offset
+ * @param {number} size
+ */
+ setIndexBuffer(buffer, indexFormat, offset = 0, size = 0) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setIndexBuffer' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 1");
+ indexFormat = webidl.converters.GPUIndexFormat(
+ indexFormat,
+ prefix,
+ "Argument 2",
+ );
+ offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
+ size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
+ const device = assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ const bufferRid = assertResource(buffer, prefix, "Argument 1");
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_bundle_encoder_set_index_buffer(
+ renderBundleEncoderRid,
+ bufferRid,
+ indexFormat,
+ offset,
+ size,
+ );
+ }
+
+ /**
+ * @param {number} slot
+ * @param {GPUBuffer} buffer
+ * @param {number} offset
+ * @param {number} size
+ */
+ setVertexBuffer(slot, buffer, offset = 0, size) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'setVertexBuffer' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ slot = webidl.converters.GPUSize32(slot, prefix, "Argument 1");
+ buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 2");
+ offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
+ if (size !== undefined) {
+ size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
+ }
+ const device = assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ const bufferRid = assertResource(buffer, prefix, "Argument 2");
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_bundle_encoder_set_vertex_buffer(
+ renderBundleEncoderRid,
+ slot,
+ bufferRid,
+ offset,
+ size,
+ );
+ }
+
+ /**
+ * @param {number} vertexCount
+ * @param {number} instanceCount
+ * @param {number} firstVertex
+ * @param {number} firstInstance
+ */
+ draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix = "Failed to execute 'draw' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ vertexCount = webidl.converters.GPUSize32(
+ vertexCount,
+ prefix,
+ "Argument 1",
+ );
+ instanceCount = webidl.converters.GPUSize32(
+ instanceCount,
+ prefix,
+ "Argument 2",
+ );
+ firstVertex = webidl.converters.GPUSize32(
+ firstVertex,
+ prefix,
+ "Argument 3",
+ );
+ firstInstance = webidl.converters.GPUSize32(
+ firstInstance,
+ prefix,
+ "Argument 4",
+ );
+ assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_bundle_encoder_draw(
+ renderBundleEncoderRid,
+ vertexCount,
+ instanceCount,
+ firstVertex,
+ firstInstance,
+ );
+ }
+
+ /**
+ * @param {number} indexCount
+ * @param {number} instanceCount
+ * @param {number} firstIndex
+ * @param {number} baseVertex
+ * @param {number} firstInstance
+ */
+ drawIndexed(
+ indexCount,
+ instanceCount = 1,
+ firstIndex = 0,
+ baseVertex = 0,
+ firstInstance = 0,
+ ) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'drawIndexed' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, prefix);
+ indexCount = webidl.converters.GPUSize32(indexCount, prefix, "Argument 1");
+ instanceCount = webidl.converters.GPUSize32(
+ instanceCount,
+ prefix,
+ "Argument 2",
+ );
+ firstIndex = webidl.converters.GPUSize32(firstIndex, prefix, "Argument 3");
+ baseVertex = webidl.converters.GPUSignedOffset32(
+ baseVertex,
+ prefix,
+ "Argument 4",
+ );
+ firstInstance = webidl.converters.GPUSize32(
+ firstInstance,
+ prefix,
+ "Argument 5",
+ );
+ assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ ops.op_webgpu_render_bundle_encoder_draw_indexed(
+ renderBundleEncoderRid,
+ indexCount,
+ instanceCount,
+ firstIndex,
+ baseVertex,
+ firstInstance,
+ );
+ }
+
+ /**
+ * @param {GPUBuffer} indirectBuffer
+ * @param {number} indirectOffset
+ */
+ drawIndirect(indirectBuffer, indirectOffset) {
+ webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
+ const prefix =
+ "Failed to execute 'drawIndirect' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 2, prefix);
+ indirectBuffer = webidl.converters.GPUBuffer(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ indirectOffset = webidl.converters.GPUSize64(
+ indirectOffset,
+ prefix,
+ "Argument 2",
+ );
+ const device = assertDevice(this, prefix, "this");
+ const renderBundleEncoderRid = assertResource(this, prefix, "this");
+ const indirectBufferRid = assertResource(
+ indirectBuffer,
+ prefix,
+ "Argument 1",
+ );
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ ops.op_webgpu_render_bundle_encoder_draw_indirect(
+ renderBundleEncoderRid,
+ indirectBufferRid,
+ indirectOffset,
+ );
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ GPURenderBundleEncoderPrototype,
+ this,
+ ),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPURenderBundleEncoder", GPURenderBundleEncoder);
+const GPURenderBundleEncoderPrototype = GPURenderBundleEncoder.prototype;
+
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPURenderBundle}
+ */
+function createGPURenderBundle(label, device, rid) {
+ /** @type {GPURenderBundle} */
+ const bundle = webidl.createBranded(GPURenderBundle);
+ bundle[_label] = label;
+ bundle[_device] = device;
+ bundle[_rid] = rid;
+ return bundle;
+}
+
+class GPURenderBundle {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPURenderBundlePrototype, this),
+ keys: [
+ "label",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPURenderBundle", GPURenderBundle);
+const GPURenderBundlePrototype = GPURenderBundle.prototype;
+/**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUQuerySet}
+ */
+function createGPUQuerySet(label, device, rid, descriptor) {
+ /** @type {GPUQuerySet} */
+ const queue = webidl.createBranded(GPUQuerySet);
+ queue[_label] = label;
+ queue[_device] = device;
+ queue[_rid] = rid;
+ queue[_descriptor] = descriptor;
+ return queue;
+}
+
+class GPUQuerySet {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+ /** @type {GPUQuerySetDescriptor} */
+ [_descriptor];
+ /** @type {GPUQueryType} */
+ [_type];
+ /** @type {number} */
+ [_count];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUQuerySetPrototype);
+ this[_cleanup]();
+ }
+
+ get type() {
+ webidl.assertBranded(this, GPUQuerySetPrototype);
+ return this[_type]();
+ }
+
+ get count() {
+ webidl.assertBranded(this, GPUQuerySetPrototype);
+ return this[_count]();
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUQuerySetPrototype, this),
+ keys: [
+ "label",
+ "type",
+ "count",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+GPUObjectBaseMixin("GPUQuerySet", GPUQuerySet);
+const GPUQuerySetPrototype = GPUQuerySet.prototype;
+
+// Converters
+
+// This needs to be initialized after all of the base classes are implemented,
+// otherwise their converters might not be available yet.
+// DICTIONARY: GPUObjectDescriptorBase
+const dictMembersGPUObjectDescriptorBase = [
+ { key: "label", converter: webidl.converters["USVString"], defaultValue: "" },
+];
+webidl.converters["GPUObjectDescriptorBase"] = webidl
+ .createDictionaryConverter(
+ "GPUObjectDescriptorBase",
+ dictMembersGPUObjectDescriptorBase,
+ );
+
+// INTERFACE: GPUSupportedLimits
+webidl.converters.GPUSupportedLimits = webidl.createInterfaceConverter(
+ "GPUSupportedLimits",
+ GPUSupportedLimits.prototype,
+);
+
+// INTERFACE: GPUSupportedFeatures
+webidl.converters.GPUSupportedFeatures = webidl.createInterfaceConverter(
+ "GPUSupportedFeatures",
+ GPUSupportedFeatures.prototype,
+);
+
+// INTERFACE: GPU
+webidl.converters.GPU = webidl.createInterfaceConverter("GPU", GPU.prototype);
+
+// ENUM: GPUPowerPreference
+webidl.converters["GPUPowerPreference"] = webidl.createEnumConverter(
+ "GPUPowerPreference",
+ [
+ "low-power",
+ "high-performance",
+ ],
+);
+
+// DICTIONARY: GPURequestAdapterOptions
+const dictMembersGPURequestAdapterOptions = [
+ {
+ key: "powerPreference",
+ converter: webidl.converters["GPUPowerPreference"],
+ },
+ {
+ key: "forceFallbackAdapter",
+ converter: webidl.converters.boolean,
+ defaultValue: false,
+ },
+];
+webidl.converters["GPURequestAdapterOptions"] = webidl
+ .createDictionaryConverter(
+ "GPURequestAdapterOptions",
+ dictMembersGPURequestAdapterOptions,
+ );
+
+// INTERFACE: GPUAdapter
+webidl.converters.GPUAdapter = webidl.createInterfaceConverter(
+ "GPUAdapter",
+ GPUAdapter.prototype,
+);
+
+// ENUM: GPUFeatureName
+webidl.converters["GPUFeatureName"] = webidl.createEnumConverter(
+ "GPUFeatureName",
+ [
+ // api
+ "depth-clip-control",
+ "timestamp-query",
+ "indirect-first-instance",
+ // shader
+ "shader-f16",
+ // texture formats
+ "depth32float-stencil8",
+ "texture-compression-bc",
+ "texture-compression-etc2",
+ "texture-compression-astc",
+ "rg11b10ufloat-renderable",
+ "bgra8unorm-storage",
+
+ // extended from spec
+
+ // texture formats
+ "texture-format-16-bit-norm",
+ "texture-compression-astc-hdr",
+ "texture-adapter-specific-format-features",
+ // api
+ //"pipeline-statistics-query",
+ "timestamp-query-inside-passes",
+ "mappable-primary-buffers",
+ "texture-binding-array",
+ "buffer-binding-array",
+ "storage-resource-binding-array",
+ "sampled-texture-and-storage-buffer-array-non-uniform-indexing",
+ "uniform-buffer-and-storage-texture-array-non-uniform-indexing",
+ "partially-bound-binding-array",
+ "multi-draw-indirect",
+ "multi-draw-indirect-count",
+ "push-constants",
+ "address-mode-clamp-to-zero",
+ "address-mode-clamp-to-border",
+ "polygon-mode-line",
+ "polygon-mode-point",
+ "conservative-rasterization",
+ "vertex-writable-storage",
+ "clear-texture",
+ "spirv-shader-passthrough",
+ "multiview",
+ "vertex-attribute-64-bit",
+ // shader
+ "shader-f64",
+ "shader-i16",
+ "shader-primitive-index",
+ "shader-early-depth-test",
+ ],
+);
+
+// TYPEDEF: GPUSize32
+webidl.converters["GPUSize32"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// TYPEDEF: GPUSize64
+webidl.converters["GPUSize64"] = (V, opts) =>
+ webidl.converters["unsigned long long"](V, { ...opts, enforceRange: true });
+
+// DICTIONARY: GPUDeviceDescriptor
+const dictMembersGPUDeviceDescriptor = [
+ {
+ key: "requiredFeatures",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUFeatureName"],
+ ),
+ get defaultValue() {
+ return [];
+ },
+ },
+ {
+ key: "requiredLimits",
+ converter: webidl.createRecordConverter(
+ webidl.converters["DOMString"],
+ webidl.converters["GPUSize64"],
+ ),
+ },
+];
+webidl.converters["GPUDeviceDescriptor"] = webidl.createDictionaryConverter(
+ "GPUDeviceDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUDeviceDescriptor,
+);
+
+// INTERFACE: GPUDevice
+webidl.converters.GPUDevice = webidl.createInterfaceConverter(
+ "GPUDevice",
+ GPUDevice.prototype,
+);
+
+// INTERFACE: GPUBuffer
+webidl.converters.GPUBuffer = webidl.createInterfaceConverter(
+ "GPUBuffer",
+ GPUBuffer.prototype,
+);
+
+// TYPEDEF: GPUBufferUsageFlags
+webidl.converters["GPUBufferUsageFlags"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// DICTIONARY: GPUBufferDescriptor
+const dictMembersGPUBufferDescriptor = [
+ { key: "size", converter: webidl.converters["GPUSize64"], required: true },
+ {
+ key: "usage",
+ converter: webidl.converters["GPUBufferUsageFlags"],
+ required: true,
+ },
+ {
+ key: "mappedAtCreation",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+];
+webidl.converters["GPUBufferDescriptor"] = webidl.createDictionaryConverter(
+ "GPUBufferDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUBufferDescriptor,
+);
+
+// INTERFACE: GPUBufferUsage
+webidl.converters.GPUBufferUsage = webidl.createInterfaceConverter(
+ "GPUBufferUsage",
+ GPUBufferUsage.prototype,
+);
+
+// TYPEDEF: GPUMapModeFlags
+webidl.converters["GPUMapModeFlags"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// INTERFACE: GPUMapMode
+webidl.converters.GPUMapMode = webidl.createInterfaceConverter(
+ "GPUMapMode",
+ GPUMapMode.prototype,
+);
+
+// INTERFACE: GPUTexture
+webidl.converters.GPUTexture = webidl.createInterfaceConverter(
+ "GPUTexture",
+ GPUTexture.prototype,
+);
+
+// TYPEDEF: GPUIntegerCoordinate
+webidl.converters["GPUIntegerCoordinate"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+webidl.converters["sequence<GPUIntegerCoordinate>"] = webidl
+ .createSequenceConverter(webidl.converters["GPUIntegerCoordinate"]);
+
+// DICTIONARY: GPUExtent3DDict
+const dictMembersGPUExtent3DDict = [
+ {
+ key: "width",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ required: true,
+ },
+ {
+ key: "height",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 1,
+ },
+ {
+ key: "depthOrArrayLayers",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 1,
+ },
+];
+webidl.converters["GPUExtent3DDict"] = webidl.createDictionaryConverter(
+ "GPUExtent3DDict",
+ dictMembersGPUExtent3DDict,
+);
+
+// TYPEDEF: GPUExtent3D
+webidl.converters["GPUExtent3D"] = (V, opts) => {
+ // Union for (sequence<GPUIntegerCoordinate> or GPUExtent3DDict)
+ if (V === null || V === undefined) {
+ return webidl.converters["GPUExtent3DDict"](V, opts);
+ }
+ if (typeof V === "object") {
+ const method = V[SymbolIterator];
+ if (method !== undefined) {
+ return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
+ }
+ return webidl.converters["GPUExtent3DDict"](V, opts);
+ }
+ throw webidl.makeException(
+ TypeError,
+ "can not be converted to sequence<GPUIntegerCoordinate> or GPUExtent3DDict.",
+ opts,
+ );
+};
+
+// ENUM: GPUTextureDimension
+webidl.converters["GPUTextureDimension"] = webidl.createEnumConverter(
+ "GPUTextureDimension",
+ [
+ "1d",
+ "2d",
+ "3d",
+ ],
+);
+
+// ENUM: GPUTextureFormat
+webidl.converters["GPUTextureFormat"] = webidl.createEnumConverter(
+ "GPUTextureFormat",
+ [
+ "r8unorm",
+ "r8snorm",
+ "r8uint",
+ "r8sint",
+ "r16uint",
+ "r16sint",
+ "r16float",
+ "rg8unorm",
+ "rg8snorm",
+ "rg8uint",
+ "rg8sint",
+ "r32uint",
+ "r32sint",
+ "r32float",
+ "rg16uint",
+ "rg16sint",
+ "rg16float",
+ "rgba8unorm",
+ "rgba8unorm-srgb",
+ "rgba8snorm",
+ "rgba8uint",
+ "rgba8sint",
+ "bgra8unorm",
+ "bgra8unorm-srgb",
+ "rgb9e5ufloat",
+ "rgb10a2unorm",
+ "rg11b10ufloat",
+ "rg32uint",
+ "rg32sint",
+ "rg32float",
+ "rgba16uint",
+ "rgba16sint",
+ "rgba16float",
+ "rgba32uint",
+ "rgba32sint",
+ "rgba32float",
+ "stencil8",
+ "depth16unorm",
+ "depth24plus",
+ "depth24plus-stencil8",
+ "depth32float",
+ "depth32float-stencil8",
+ "bc1-rgba-unorm",
+ "bc1-rgba-unorm-srgb",
+ "bc2-rgba-unorm",
+ "bc2-rgba-unorm-srgb",
+ "bc3-rgba-unorm",
+ "bc3-rgba-unorm-srgb",
+ "bc4-r-unorm",
+ "bc4-r-snorm",
+ "bc5-rg-unorm",
+ "bc5-rg-snorm",
+ "bc6h-rgb-ufloat",
+ "bc6h-rgb-float",
+ "bc7-rgba-unorm",
+ "bc7-rgba-unorm-srgb",
+ "etc2-rgb8unorm",
+ "etc2-rgb8unorm-srgb",
+ "etc2-rgb8a1unorm",
+ "etc2-rgb8a1unorm-srgb",
+ "etc2-rgba8unorm",
+ "etc2-rgba8unorm-srgb",
+ "eac-r11unorm",
+ "eac-r11snorm",
+ "eac-rg11unorm",
+ "eac-rg11snorm",
+ "astc-4x4-unorm",
+ "astc-4x4-unorm-srgb",
+ "astc-5x4-unorm",
+ "astc-5x4-unorm-srgb",
+ "astc-5x5-unorm",
+ "astc-5x5-unorm-srgb",
+ "astc-6x5-unorm",
+ "astc-6x5-unorm-srgb",
+ "astc-6x6-unorm",
+ "astc-6x6-unorm-srgb",
+ "astc-8x5-unorm",
+ "astc-8x5-unorm-srgb",
+ "astc-8x6-unorm",
+ "astc-8x6-unorm-srgb",
+ "astc-8x8-unorm",
+ "astc-8x8-unorm-srgb",
+ "astc-10x5-unorm",
+ "astc-10x5-unorm-srgb",
+ "astc-10x6-unorm",
+ "astc-10x6-unorm-srgb",
+ "astc-10x8-unorm",
+ "astc-10x8-unorm-srgb",
+ "astc-10x10-unorm",
+ "astc-10x10-unorm-srgb",
+ "astc-12x10-unorm",
+ "astc-12x10-unorm-srgb",
+ "astc-12x12-unorm",
+ "astc-12x12-unorm-srgb",
+ ],
+);
+
+// TYPEDEF: GPUTextureUsageFlags
+webidl.converters["GPUTextureUsageFlags"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// DICTIONARY: GPUTextureDescriptor
+const dictMembersGPUTextureDescriptor = [
+ {
+ key: "size",
+ converter: webidl.converters["GPUExtent3D"],
+ required: true,
+ },
+ {
+ key: "mipLevelCount",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 1,
+ },
+ {
+ key: "sampleCount",
+ converter: webidl.converters["GPUSize32"],
+ defaultValue: 1,
+ },
+ {
+ key: "dimension",
+ converter: webidl.converters["GPUTextureDimension"],
+ defaultValue: "2d",
+ },
+ {
+ key: "format",
+ converter: webidl.converters["GPUTextureFormat"],
+ required: true,
+ },
+ {
+ key: "usage",
+ converter: webidl.converters["GPUTextureUsageFlags"],
+ required: true,
+ },
+ {
+ key: "viewFormats",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUTextureFormat"],
+ ),
+ get defaultValue() {
+ return [];
+ },
+ },
+];
+webidl.converters["GPUTextureDescriptor"] = webidl.createDictionaryConverter(
+ "GPUTextureDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUTextureDescriptor,
+);
+
+// INTERFACE: GPUTextureUsage
+webidl.converters.GPUTextureUsage = webidl.createInterfaceConverter(
+ "GPUTextureUsage",
+ GPUTextureUsage.prototype,
+);
+
+// INTERFACE: GPUTextureView
+webidl.converters.GPUTextureView = webidl.createInterfaceConverter(
+ "GPUTextureView",
+ GPUTextureView.prototype,
+);
+
+// ENUM: GPUTextureViewDimension
+webidl.converters["GPUTextureViewDimension"] = webidl.createEnumConverter(
+ "GPUTextureViewDimension",
+ [
+ "1d",
+ "2d",
+ "2d-array",
+ "cube",
+ "cube-array",
+ "3d",
+ ],
+);
+
+// ENUM: GPUTextureAspect
+webidl.converters["GPUTextureAspect"] = webidl.createEnumConverter(
+ "GPUTextureAspect",
+ [
+ "all",
+ "stencil-only",
+ "depth-only",
+ ],
+);
+
+// DICTIONARY: GPUTextureViewDescriptor
+const dictMembersGPUTextureViewDescriptor = [
+ { key: "format", converter: webidl.converters["GPUTextureFormat"] },
+ {
+ key: "dimension",
+ converter: webidl.converters["GPUTextureViewDimension"],
+ },
+ {
+ key: "aspect",
+ converter: webidl.converters["GPUTextureAspect"],
+ defaultValue: "all",
+ },
+ {
+ key: "baseMipLevel",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+ {
+ key: "mipLevelCount",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ },
+ {
+ key: "baseArrayLayer",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+ {
+ key: "arrayLayerCount",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ },
+];
+webidl.converters["GPUTextureViewDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUTextureViewDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUTextureViewDescriptor,
+ );
+
+// INTERFACE: GPUSampler
+webidl.converters.GPUSampler = webidl.createInterfaceConverter(
+ "GPUSampler",
+ GPUSampler.prototype,
+);
+
+// ENUM: GPUAddressMode
+webidl.converters["GPUAddressMode"] = webidl.createEnumConverter(
+ "GPUAddressMode",
+ [
+ "clamp-to-edge",
+ "repeat",
+ "mirror-repeat",
+ ],
+);
+
+// ENUM: GPUFilterMode
+webidl.converters["GPUFilterMode"] = webidl.createEnumConverter(
+ "GPUFilterMode",
+ [
+ "nearest",
+ "linear",
+ ],
+);
+
+// ENUM: GPUMipmapFilterMode
+webidl.converters["GPUMipmapFilterMode"] = webidl.createEnumConverter(
+ "GPUMipmapFilterMode",
+ [
+ "nearest",
+ "linear",
+ ],
+);
+
+// ENUM: GPUCompareFunction
+webidl.converters["GPUCompareFunction"] = webidl.createEnumConverter(
+ "GPUCompareFunction",
+ [
+ "never",
+ "less",
+ "equal",
+ "less-equal",
+ "greater",
+ "not-equal",
+ "greater-equal",
+ "always",
+ ],
+);
+
+// DICTIONARY: GPUSamplerDescriptor
+const dictMembersGPUSamplerDescriptor = [
+ {
+ key: "addressModeU",
+ converter: webidl.converters["GPUAddressMode"],
+ defaultValue: "clamp-to-edge",
+ },
+ {
+ key: "addressModeV",
+ converter: webidl.converters["GPUAddressMode"],
+ defaultValue: "clamp-to-edge",
+ },
+ {
+ key: "addressModeW",
+ converter: webidl.converters["GPUAddressMode"],
+ defaultValue: "clamp-to-edge",
+ },
+ {
+ key: "magFilter",
+ converter: webidl.converters["GPUFilterMode"],
+ defaultValue: "nearest",
+ },
+ {
+ key: "minFilter",
+ converter: webidl.converters["GPUFilterMode"],
+ defaultValue: "nearest",
+ },
+ {
+ key: "mipmapFilter",
+ converter: webidl.converters["GPUMipmapFilterMode"],
+ defaultValue: "nearest",
+ },
+ {
+ key: "lodMinClamp",
+ converter: webidl.converters["float"],
+ defaultValue: 0,
+ },
+ {
+ key: "lodMaxClamp",
+ converter: webidl.converters["float"],
+ defaultValue: 0xffffffff,
+ },
+ { key: "compare", converter: webidl.converters["GPUCompareFunction"] },
+ {
+ key: "maxAnisotropy",
+ converter: (V, opts) =>
+ webidl.converters["unsigned short"](V, { ...opts, clamp: true }),
+ defaultValue: 1,
+ },
+];
+webidl.converters["GPUSamplerDescriptor"] = webidl.createDictionaryConverter(
+ "GPUSamplerDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUSamplerDescriptor,
+);
+
+// INTERFACE: GPUBindGroupLayout
+webidl.converters.GPUBindGroupLayout = webidl.createInterfaceConverter(
+ "GPUBindGroupLayout",
+ GPUBindGroupLayout.prototype,
+);
+
+// TYPEDEF: GPUIndex32
+webidl.converters["GPUIndex32"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// TYPEDEF: GPUShaderStageFlags
+webidl.converters["GPUShaderStageFlags"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// ENUM: GPUBufferBindingType
+webidl.converters["GPUBufferBindingType"] = webidl.createEnumConverter(
+ "GPUBufferBindingType",
+ [
+ "uniform",
+ "storage",
+ "read-only-storage",
+ ],
+);
+
+// DICTIONARY: GPUBufferBindingLayout
+const dictMembersGPUBufferBindingLayout = [
+ {
+ key: "type",
+ converter: webidl.converters["GPUBufferBindingType"],
+ defaultValue: "uniform",
+ },
+ {
+ key: "hasDynamicOffset",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+ {
+ key: "minBindingSize",
+ converter: webidl.converters["GPUSize64"],
+ defaultValue: 0,
+ },
+];
+webidl.converters["GPUBufferBindingLayout"] = webidl
+ .createDictionaryConverter(
+ "GPUBufferBindingLayout",
+ dictMembersGPUBufferBindingLayout,
+ );
+
+// ENUM: GPUSamplerBindingType
+webidl.converters["GPUSamplerBindingType"] = webidl.createEnumConverter(
+ "GPUSamplerBindingType",
+ [
+ "filtering",
+ "non-filtering",
+ "comparison",
+ ],
+);
+
+// DICTIONARY: GPUSamplerBindingLayout
+const dictMembersGPUSamplerBindingLayout = [
+ {
+ key: "type",
+ converter: webidl.converters["GPUSamplerBindingType"],
+ defaultValue: "filtering",
+ },
+];
+webidl.converters["GPUSamplerBindingLayout"] = webidl
+ .createDictionaryConverter(
+ "GPUSamplerBindingLayout",
+ dictMembersGPUSamplerBindingLayout,
+ );
+
+// ENUM: GPUTextureSampleType
+webidl.converters["GPUTextureSampleType"] = webidl.createEnumConverter(
+ "GPUTextureSampleType",
+ [
+ "float",
+ "unfilterable-float",
+ "depth",
+ "sint",
+ "uint",
+ ],
+);
+
+// DICTIONARY: GPUTextureBindingLayout
+const dictMembersGPUTextureBindingLayout = [
+ {
+ key: "sampleType",
+ converter: webidl.converters["GPUTextureSampleType"],
+ defaultValue: "float",
+ },
+ {
+ key: "viewDimension",
+ converter: webidl.converters["GPUTextureViewDimension"],
+ defaultValue: "2d",
+ },
+ {
+ key: "multisampled",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+];
+webidl.converters["GPUTextureBindingLayout"] = webidl
+ .createDictionaryConverter(
+ "GPUTextureBindingLayout",
+ dictMembersGPUTextureBindingLayout,
+ );
+
+// ENUM: GPUStorageTextureAccess
+webidl.converters["GPUStorageTextureAccess"] = webidl.createEnumConverter(
+ "GPUStorageTextureAccess",
+ [
+ "write-only",
+ ],
+);
+
+// DICTIONARY: GPUStorageTextureBindingLayout
+const dictMembersGPUStorageTextureBindingLayout = [
+ {
+ key: "access",
+ converter: webidl.converters["GPUStorageTextureAccess"],
+ defaultValue: "write-only",
+ },
+ {
+ key: "format",
+ converter: webidl.converters["GPUTextureFormat"],
+ required: true,
+ },
+ {
+ key: "viewDimension",
+ converter: webidl.converters["GPUTextureViewDimension"],
+ defaultValue: "2d",
+ },
+];
+webidl.converters["GPUStorageTextureBindingLayout"] = webidl
+ .createDictionaryConverter(
+ "GPUStorageTextureBindingLayout",
+ dictMembersGPUStorageTextureBindingLayout,
+ );
+
+// DICTIONARY: GPUBindGroupLayoutEntry
+const dictMembersGPUBindGroupLayoutEntry = [
+ {
+ key: "binding",
+ converter: webidl.converters["GPUIndex32"],
+ required: true,
+ },
+ {
+ key: "visibility",
+ converter: webidl.converters["GPUShaderStageFlags"],
+ required: true,
+ },
+ { key: "buffer", converter: webidl.converters["GPUBufferBindingLayout"] },
+ { key: "sampler", converter: webidl.converters["GPUSamplerBindingLayout"] },
+ { key: "texture", converter: webidl.converters["GPUTextureBindingLayout"] },
+ {
+ key: "storageTexture",
+ converter: webidl.converters["GPUStorageTextureBindingLayout"],
+ },
+];
+webidl.converters["GPUBindGroupLayoutEntry"] = webidl
+ .createDictionaryConverter(
+ "GPUBindGroupLayoutEntry",
+ dictMembersGPUBindGroupLayoutEntry,
+ );
+
+// DICTIONARY: GPUBindGroupLayoutDescriptor
+const dictMembersGPUBindGroupLayoutDescriptor = [
+ {
+ key: "entries",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUBindGroupLayoutEntry"],
+ ),
+ required: true,
+ },
+];
+webidl.converters["GPUBindGroupLayoutDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUBindGroupLayoutDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUBindGroupLayoutDescriptor,
+ );
+
+// INTERFACE: GPUShaderStage
+webidl.converters.GPUShaderStage = webidl.createInterfaceConverter(
+ "GPUShaderStage",
+ GPUShaderStage.prototype,
+);
+
+// INTERFACE: GPUBindGroup
+webidl.converters.GPUBindGroup = webidl.createInterfaceConverter(
+ "GPUBindGroup",
+ GPUBindGroup.prototype,
+);
+
+// DICTIONARY: GPUBufferBinding
+const dictMembersGPUBufferBinding = [
+ {
+ key: "buffer",
+ converter: webidl.converters["GPUBuffer"],
+ required: true,
+ },
+ {
+ key: "offset",
+ converter: webidl.converters["GPUSize64"],
+ defaultValue: 0,
+ },
+ { key: "size", converter: webidl.converters["GPUSize64"] },
+];
+webidl.converters["GPUBufferBinding"] = webidl.createDictionaryConverter(
+ "GPUBufferBinding",
+ dictMembersGPUBufferBinding,
+);
+
+// TYPEDEF: GPUBindingResource
+webidl.converters["GPUBindingResource"] =
+ webidl.converters.any /** put union here! **/;
+
+// DICTIONARY: GPUBindGroupEntry
+const dictMembersGPUBindGroupEntry = [
+ {
+ key: "binding",
+ converter: webidl.converters["GPUIndex32"],
+ required: true,
+ },
+ {
+ key: "resource",
+ converter: webidl.converters["GPUBindingResource"],
+ required: true,
+ },
+];
+webidl.converters["GPUBindGroupEntry"] = webidl.createDictionaryConverter(
+ "GPUBindGroupEntry",
+ dictMembersGPUBindGroupEntry,
+);
+
+// DICTIONARY: GPUBindGroupDescriptor
+const dictMembersGPUBindGroupDescriptor = [
+ {
+ key: "layout",
+ converter: webidl.converters["GPUBindGroupLayout"],
+ required: true,
+ },
+ {
+ key: "entries",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUBindGroupEntry"],
+ ),
+ required: true,
+ },
+];
+webidl.converters["GPUBindGroupDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUBindGroupDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUBindGroupDescriptor,
+ );
+
+// INTERFACE: GPUPipelineLayout
+webidl.converters.GPUPipelineLayout = webidl.createInterfaceConverter(
+ "GPUPipelineLayout",
+ GPUPipelineLayout.prototype,
+);
+
+// DICTIONARY: GPUPipelineLayoutDescriptor
+const dictMembersGPUPipelineLayoutDescriptor = [
+ {
+ key: "bindGroupLayouts",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUBindGroupLayout"],
+ ),
+ required: true,
+ },
+];
+webidl.converters["GPUPipelineLayoutDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUPipelineLayoutDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUPipelineLayoutDescriptor,
+ );
+
+// INTERFACE: GPUShaderModule
+webidl.converters.GPUShaderModule = webidl.createInterfaceConverter(
+ "GPUShaderModule",
+ GPUShaderModule.prototype,
+);
+
+// DICTIONARY: GPUShaderModuleDescriptor
+const dictMembersGPUShaderModuleDescriptor = [
+ {
+ key: "code",
+ converter: webidl.converters["DOMString"],
+ required: true,
+ },
+];
+webidl.converters["GPUShaderModuleDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUShaderModuleDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUShaderModuleDescriptor,
+ );
+
+// // ENUM: GPUCompilationMessageType
+// webidl.converters["GPUCompilationMessageType"] = webidl.createEnumConverter(
+// "GPUCompilationMessageType",
+// [
+// "error",
+// "warning",
+// "info",
+// ],
+// );
+
+// // INTERFACE: GPUCompilationMessage
+// webidl.converters.GPUCompilationMessage = webidl.createInterfaceConverter(
+// "GPUCompilationMessage",
+// GPUCompilationMessage.prototype,
+// );
+
+// // INTERFACE: GPUCompilationInfo
+// webidl.converters.GPUCompilationInfo = webidl.createInterfaceConverter(
+// "GPUCompilationInfo",
+// GPUCompilationInfo.prototype,
+// );
+
+webidl.converters["GPUAutoLayoutMode"] = webidl.createEnumConverter(
+ "GPUAutoLayoutMode",
+ [
+ "auto",
+ ],
+);
+
+webidl.converters["GPUPipelineLayout or GPUAutoLayoutMode"] = (V, opts) => {
+ if (typeof V === "object") {
+ return webidl.converters["GPUPipelineLayout"](V, opts);
+ }
+ return webidl.converters["GPUAutoLayoutMode"](V, opts);
+};
+
+// DICTIONARY: GPUPipelineDescriptorBase
+const dictMembersGPUPipelineDescriptorBase = [
+ {
+ key: "layout",
+ converter: webidl.converters["GPUPipelineLayout or GPUAutoLayoutMode"],
+ required: true,
+ },
+];
+webidl.converters["GPUPipelineDescriptorBase"] = webidl
+ .createDictionaryConverter(
+ "GPUPipelineDescriptorBase",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUPipelineDescriptorBase,
+ );
+
+// TYPEDEF: GPUPipelineConstantValue
+webidl.converters.GPUPipelineConstantValue = webidl.converters.double;
+
+webidl.converters["record<USVString, GPUPipelineConstantValue>"] = webidl
+ .createRecordConverter(
+ webidl.converters.USVString,
+ webidl.converters.GPUPipelineConstantValue,
+ );
+
+// DICTIONARY: GPUProgrammableStage
+const dictMembersGPUProgrammableStage = [
+ {
+ key: "module",
+ converter: webidl.converters["GPUShaderModule"],
+ required: true,
+ },
+ {
+ key: "entryPoint",
+ converter: webidl.converters["USVString"],
+ required: true,
+ },
+ {
+ key: "constants",
+ converter: webidl.converters["record<USVString, GPUPipelineConstantValue>"],
+ },
+];
+webidl.converters["GPUProgrammableStage"] = webidl.createDictionaryConverter(
+ "GPUProgrammableStage",
+ dictMembersGPUProgrammableStage,
+);
+
+// INTERFACE: GPUComputePipeline
+webidl.converters.GPUComputePipeline = webidl.createInterfaceConverter(
+ "GPUComputePipeline",
+ GPUComputePipeline.prototype,
+);
+
+// DICTIONARY: GPUComputePipelineDescriptor
+const dictMembersGPUComputePipelineDescriptor = [
+ {
+ key: "compute",
+ converter: webidl.converters["GPUProgrammableStage"],
+ required: true,
+ },
+];
+webidl.converters["GPUComputePipelineDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUComputePipelineDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUPipelineDescriptorBase,
+ dictMembersGPUComputePipelineDescriptor,
+ );
+
+// INTERFACE: GPURenderPipeline
+webidl.converters.GPURenderPipeline = webidl.createInterfaceConverter(
+ "GPURenderPipeline",
+ GPURenderPipeline.prototype,
+);
+
+// ENUM: GPUVertexStepMode
+webidl.converters["GPUVertexStepMode"] = webidl.createEnumConverter(
+ "GPUVertexStepMode",
+ [
+ "vertex",
+ "instance",
+ ],
+);
+
+// ENUM: GPUVertexFormat
+webidl.converters["GPUVertexFormat"] = webidl.createEnumConverter(
+ "GPUVertexFormat",
+ [
+ "uint8x2",
+ "uint8x4",
+ "sint8x2",
+ "sint8x4",
+ "unorm8x2",
+ "unorm8x4",
+ "snorm8x2",
+ "snorm8x4",
+ "uint16x2",
+ "uint16x4",
+ "sint16x2",
+ "sint16x4",
+ "unorm16x2",
+ "unorm16x4",
+ "snorm16x2",
+ "snorm16x4",
+ "float16x2",
+ "float16x4",
+ "float32",
+ "float32x2",
+ "float32x3",
+ "float32x4",
+ "uint32",
+ "uint32x2",
+ "uint32x3",
+ "uint32x4",
+ "sint32",
+ "sint32x2",
+ "sint32x3",
+ "sint32x4",
+ ],
+);
+
+// DICTIONARY: GPUVertexAttribute
+const dictMembersGPUVertexAttribute = [
+ {
+ key: "format",
+ converter: webidl.converters["GPUVertexFormat"],
+ required: true,
+ },
+ {
+ key: "offset",
+ converter: webidl.converters["GPUSize64"],
+ required: true,
+ },
+ {
+ key: "shaderLocation",
+ converter: webidl.converters["GPUIndex32"],
+ required: true,
+ },
+];
+webidl.converters["GPUVertexAttribute"] = webidl.createDictionaryConverter(
+ "GPUVertexAttribute",
+ dictMembersGPUVertexAttribute,
+);
+
+// DICTIONARY: GPUVertexBufferLayout
+const dictMembersGPUVertexBufferLayout = [
+ {
+ key: "arrayStride",
+ converter: webidl.converters["GPUSize64"],
+ required: true,
+ },
+ {
+ key: "stepMode",
+ converter: webidl.converters["GPUVertexStepMode"],
+ defaultValue: "vertex",
+ },
+ {
+ key: "attributes",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUVertexAttribute"],
+ ),
+ required: true,
+ },
+];
+webidl.converters["GPUVertexBufferLayout"] = webidl.createDictionaryConverter(
+ "GPUVertexBufferLayout",
+ dictMembersGPUVertexBufferLayout,
+);
+
+// DICTIONARY: GPUVertexState
+const dictMembersGPUVertexState = [
+ {
+ key: "buffers",
+ converter: webidl.createSequenceConverter(
+ webidl.createNullableConverter(
+ webidl.converters["GPUVertexBufferLayout"],
+ ),
+ ),
+ get defaultValue() {
+ return [];
+ },
+ },
+];
+webidl.converters["GPUVertexState"] = webidl.createDictionaryConverter(
+ "GPUVertexState",
+ dictMembersGPUProgrammableStage,
+ dictMembersGPUVertexState,
+);
+
+// ENUM: GPUPrimitiveTopology
+webidl.converters["GPUPrimitiveTopology"] = webidl.createEnumConverter(
+ "GPUPrimitiveTopology",
+ [
+ "point-list",
+ "line-list",
+ "line-strip",
+ "triangle-list",
+ "triangle-strip",
+ ],
+);
+
+// ENUM: GPUIndexFormat
+webidl.converters["GPUIndexFormat"] = webidl.createEnumConverter(
+ "GPUIndexFormat",
+ [
+ "uint16",
+ "uint32",
+ ],
+);
+
+// ENUM: GPUFrontFace
+webidl.converters["GPUFrontFace"] = webidl.createEnumConverter(
+ "GPUFrontFace",
+ [
+ "ccw",
+ "cw",
+ ],
+);
+
+// ENUM: GPUCullMode
+webidl.converters["GPUCullMode"] = webidl.createEnumConverter("GPUCullMode", [
+ "none",
+ "front",
+ "back",
+]);
+
+// DICTIONARY: GPUPrimitiveState
+const dictMembersGPUPrimitiveState = [
+ {
+ key: "topology",
+ converter: webidl.converters["GPUPrimitiveTopology"],
+ defaultValue: "triangle-list",
+ },
+ { key: "stripIndexFormat", converter: webidl.converters["GPUIndexFormat"] },
+ {
+ key: "frontFace",
+ converter: webidl.converters["GPUFrontFace"],
+ defaultValue: "ccw",
+ },
+ {
+ key: "cullMode",
+ converter: webidl.converters["GPUCullMode"],
+ defaultValue: "none",
+ },
+ {
+ key: "unclippedDepth",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+];
+webidl.converters["GPUPrimitiveState"] = webidl.createDictionaryConverter(
+ "GPUPrimitiveState",
+ dictMembersGPUPrimitiveState,
+);
+
+// ENUM: GPUStencilOperation
+webidl.converters["GPUStencilOperation"] = webidl.createEnumConverter(
+ "GPUStencilOperation",
+ [
+ "keep",
+ "zero",
+ "replace",
+ "invert",
+ "increment-clamp",
+ "decrement-clamp",
+ "increment-wrap",
+ "decrement-wrap",
+ ],
+);
+
+// DICTIONARY: GPUStencilFaceState
+const dictMembersGPUStencilFaceState = [
+ {
+ key: "compare",
+ converter: webidl.converters["GPUCompareFunction"],
+ defaultValue: "always",
+ },
+ {
+ key: "failOp",
+ converter: webidl.converters["GPUStencilOperation"],
+ defaultValue: "keep",
+ },
+ {
+ key: "depthFailOp",
+ converter: webidl.converters["GPUStencilOperation"],
+ defaultValue: "keep",
+ },
+ {
+ key: "passOp",
+ converter: webidl.converters["GPUStencilOperation"],
+ defaultValue: "keep",
+ },
+];
+webidl.converters["GPUStencilFaceState"] = webidl.createDictionaryConverter(
+ "GPUStencilFaceState",
+ dictMembersGPUStencilFaceState,
+);
+
+// TYPEDEF: GPUStencilValue
+webidl.converters["GPUStencilValue"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// TYPEDEF: GPUDepthBias
+webidl.converters["GPUDepthBias"] = (V, opts) =>
+ webidl.converters["long"](V, { ...opts, enforceRange: true });
+
+// DICTIONARY: GPUDepthStencilState
+const dictMembersGPUDepthStencilState = [
+ {
+ key: "format",
+ converter: webidl.converters["GPUTextureFormat"],
+ required: true,
+ },
+ {
+ key: "depthWriteEnabled",
+ converter: webidl.converters["boolean"],
+ required: true,
+ },
+ {
+ key: "depthCompare",
+ converter: webidl.converters["GPUCompareFunction"],
+ required: true,
+ },
+ {
+ key: "stencilFront",
+ converter: webidl.converters["GPUStencilFaceState"],
+ get defaultValue() {
+ return {};
+ },
+ },
+ {
+ key: "stencilBack",
+ converter: webidl.converters["GPUStencilFaceState"],
+ get defaultValue() {
+ return {};
+ },
+ },
+ {
+ key: "stencilReadMask",
+ converter: webidl.converters["GPUStencilValue"],
+ defaultValue: 0xFFFFFFFF,
+ },
+ {
+ key: "stencilWriteMask",
+ converter: webidl.converters["GPUStencilValue"],
+ defaultValue: 0xFFFFFFFF,
+ },
+ {
+ key: "depthBias",
+ converter: webidl.converters["GPUDepthBias"],
+ defaultValue: 0,
+ },
+ {
+ key: "depthBiasSlopeScale",
+ converter: webidl.converters["float"],
+ defaultValue: 0,
+ },
+ {
+ key: "depthBiasClamp",
+ converter: webidl.converters["float"],
+ defaultValue: 0,
+ },
+];
+webidl.converters["GPUDepthStencilState"] = webidl.createDictionaryConverter(
+ "GPUDepthStencilState",
+ dictMembersGPUDepthStencilState,
+);
+
+// TYPEDEF: GPUSampleMask
+webidl.converters["GPUSampleMask"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// DICTIONARY: GPUMultisampleState
+const dictMembersGPUMultisampleState = [
+ {
+ key: "count",
+ converter: webidl.converters["GPUSize32"],
+ defaultValue: 1,
+ },
+ {
+ key: "mask",
+ converter: webidl.converters["GPUSampleMask"],
+ defaultValue: 0xFFFFFFFF,
+ },
+ {
+ key: "alphaToCoverageEnabled",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+];
+webidl.converters["GPUMultisampleState"] = webidl.createDictionaryConverter(
+ "GPUMultisampleState",
+ dictMembersGPUMultisampleState,
+);
+
+// ENUM: GPUBlendFactor
+webidl.converters["GPUBlendFactor"] = webidl.createEnumConverter(
+ "GPUBlendFactor",
+ [
+ "zero",
+ "one",
+ "src",
+ "one-minus-src",
+ "src-alpha",
+ "one-minus-src-alpha",
+ "dst",
+ "one-minus-dst",
+ "dst-alpha",
+ "one-minus-dst-alpha",
+ "src-alpha-saturated",
+ "constant",
+ "one-minus-constant",
+ ],
+);
+
+// ENUM: GPUBlendOperation
+webidl.converters["GPUBlendOperation"] = webidl.createEnumConverter(
+ "GPUBlendOperation",
+ [
+ "add",
+ "subtract",
+ "reverse-subtract",
+ "min",
+ "max",
+ ],
+);
+
+// DICTIONARY: GPUBlendComponent
+const dictMembersGPUBlendComponent = [
+ {
+ key: "srcFactor",
+ converter: webidl.converters["GPUBlendFactor"],
+ defaultValue: "one",
+ },
+ {
+ key: "dstFactor",
+ converter: webidl.converters["GPUBlendFactor"],
+ defaultValue: "zero",
+ },
+ {
+ key: "operation",
+ converter: webidl.converters["GPUBlendOperation"],
+ defaultValue: "add",
+ },
+];
+webidl.converters["GPUBlendComponent"] = webidl.createDictionaryConverter(
+ "GPUBlendComponent",
+ dictMembersGPUBlendComponent,
+);
+
+// DICTIONARY: GPUBlendState
+const dictMembersGPUBlendState = [
+ {
+ key: "color",
+ converter: webidl.converters["GPUBlendComponent"],
+ required: true,
+ },
+ {
+ key: "alpha",
+ converter: webidl.converters["GPUBlendComponent"],
+ required: true,
+ },
+];
+webidl.converters["GPUBlendState"] = webidl.createDictionaryConverter(
+ "GPUBlendState",
+ dictMembersGPUBlendState,
+);
+
+// TYPEDEF: GPUColorWriteFlags
+webidl.converters["GPUColorWriteFlags"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// DICTIONARY: GPUColorTargetState
+const dictMembersGPUColorTargetState = [
+ {
+ key: "format",
+ converter: webidl.converters["GPUTextureFormat"],
+ required: true,
+ },
+ { key: "blend", converter: webidl.converters["GPUBlendState"] },
+ {
+ key: "writeMask",
+ converter: webidl.converters["GPUColorWriteFlags"],
+ defaultValue: 0xF,
+ },
+];
+webidl.converters["GPUColorTargetState"] = webidl.createDictionaryConverter(
+ "GPUColorTargetState",
+ dictMembersGPUColorTargetState,
+);
+
+// DICTIONARY: GPUFragmentState
+const dictMembersGPUFragmentState = [
+ {
+ key: "targets",
+ converter: webidl.createSequenceConverter(
+ webidl.createNullableConverter(
+ webidl.converters["GPUColorTargetState"],
+ ),
+ ),
+ required: true,
+ },
+];
+webidl.converters["GPUFragmentState"] = webidl.createDictionaryConverter(
+ "GPUFragmentState",
+ dictMembersGPUProgrammableStage,
+ dictMembersGPUFragmentState,
+);
+
+// DICTIONARY: GPURenderPipelineDescriptor
+const dictMembersGPURenderPipelineDescriptor = [
+ {
+ key: "vertex",
+ converter: webidl.converters["GPUVertexState"],
+ required: true,
+ },
+ {
+ key: "primitive",
+ converter: webidl.converters["GPUPrimitiveState"],
+ get defaultValue() {
+ return {};
+ },
+ },
+ {
+ key: "depthStencil",
+ converter: webidl.converters["GPUDepthStencilState"],
+ },
+ {
+ key: "multisample",
+ converter: webidl.converters["GPUMultisampleState"],
+ get defaultValue() {
+ return {};
+ },
+ },
+ { key: "fragment", converter: webidl.converters["GPUFragmentState"] },
+];
+webidl.converters["GPURenderPipelineDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPipelineDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUPipelineDescriptorBase,
+ dictMembersGPURenderPipelineDescriptor,
+ );
+
+// INTERFACE: GPUColorWrite
+webidl.converters.GPUColorWrite = webidl.createInterfaceConverter(
+ "GPUColorWrite",
+ GPUColorWrite.prototype,
+);
+
+// INTERFACE: GPUCommandBuffer
+webidl.converters.GPUCommandBuffer = webidl.createInterfaceConverter(
+ "GPUCommandBuffer",
+ GPUCommandBuffer.prototype,
+);
+webidl.converters["sequence<GPUCommandBuffer>"] = webidl
+ .createSequenceConverter(webidl.converters["GPUCommandBuffer"]);
+
+// DICTIONARY: GPUCommandBufferDescriptor
+const dictMembersGPUCommandBufferDescriptor = [];
+webidl.converters["GPUCommandBufferDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUCommandBufferDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUCommandBufferDescriptor,
+ );
+
+// INTERFACE: GPUCommandEncoder
+webidl.converters.GPUCommandEncoder = webidl.createInterfaceConverter(
+ "GPUCommandEncoder",
+ GPUCommandEncoder.prototype,
+);
+
+// DICTIONARY: GPUCommandEncoderDescriptor
+const dictMembersGPUCommandEncoderDescriptor = [];
+webidl.converters["GPUCommandEncoderDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUCommandEncoderDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUCommandEncoderDescriptor,
+ );
+
+// DICTIONARY: GPUImageDataLayout
+const dictMembersGPUImageDataLayout = [
+ {
+ key: "offset",
+ converter: webidl.converters["GPUSize64"],
+ defaultValue: 0,
+ },
+ { key: "bytesPerRow", converter: webidl.converters["GPUSize32"] },
+ { key: "rowsPerImage", converter: webidl.converters["GPUSize32"] },
+];
+webidl.converters["GPUImageDataLayout"] = webidl.createDictionaryConverter(
+ "GPUImageDataLayout",
+ dictMembersGPUImageDataLayout,
+);
+
+// DICTIONARY: GPUImageCopyBuffer
+const dictMembersGPUImageCopyBuffer = [
+ {
+ key: "buffer",
+ converter: webidl.converters["GPUBuffer"],
+ required: true,
+ },
+];
+webidl.converters["GPUImageCopyBuffer"] = webidl.createDictionaryConverter(
+ "GPUImageCopyBuffer",
+ dictMembersGPUImageDataLayout,
+ dictMembersGPUImageCopyBuffer,
+);
+
+// DICTIONARY: GPUOrigin3DDict
+const dictMembersGPUOrigin3DDict = [
+ {
+ key: "x",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+ {
+ key: "y",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+ {
+ key: "z",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+];
+webidl.converters["GPUOrigin3DDict"] = webidl.createDictionaryConverter(
+ "GPUOrigin3DDict",
+ dictMembersGPUOrigin3DDict,
+);
+
+// TYPEDEF: GPUOrigin3D
+webidl.converters["GPUOrigin3D"] = (V, opts) => {
+ // Union for (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict)
+ if (V === null || V === undefined) {
+ return webidl.converters["GPUOrigin3DDict"](V, opts);
+ }
+ if (typeof V === "object") {
+ const method = V[SymbolIterator];
+ if (method !== undefined) {
+ return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
+ }
+ return webidl.converters["GPUOrigin3DDict"](V, opts);
+ }
+ throw webidl.makeException(
+ TypeError,
+ "can not be converted to sequence<GPUIntegerCoordinate> or GPUOrigin3DDict.",
+ opts,
+ );
+};
+
+// DICTIONARY: GPUImageCopyTexture
+const dictMembersGPUImageCopyTexture = [
+ {
+ key: "texture",
+ converter: webidl.converters["GPUTexture"],
+ required: true,
+ },
+ {
+ key: "mipLevel",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+ {
+ key: "origin",
+ converter: webidl.converters["GPUOrigin3D"],
+ get defaultValue() {
+ return {};
+ },
+ },
+ {
+ key: "aspect",
+ converter: webidl.converters["GPUTextureAspect"],
+ defaultValue: "all",
+ },
+];
+webidl.converters["GPUImageCopyTexture"] = webidl.createDictionaryConverter(
+ "GPUImageCopyTexture",
+ dictMembersGPUImageCopyTexture,
+);
+
+// DICTIONARY: GPUOrigin2DDict
+const dictMembersGPUOrigin2DDict = [
+ {
+ key: "x",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+ {
+ key: "y",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 0,
+ },
+];
+webidl.converters["GPUOrigin2DDict"] = webidl.createDictionaryConverter(
+ "GPUOrigin2DDict",
+ dictMembersGPUOrigin2DDict,
+);
+
+// TYPEDEF: GPUOrigin2D
+webidl.converters["GPUOrigin2D"] = (V, opts) => {
+ // Union for (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict)
+ if (V === null || V === undefined) {
+ return webidl.converters["GPUOrigin2DDict"](V, opts);
+ }
+ if (typeof V === "object") {
+ const method = V[SymbolIterator];
+ if (method !== undefined) {
+ return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
+ }
+ return webidl.converters["GPUOrigin2DDict"](V, opts);
+ }
+ throw webidl.makeException(
+ TypeError,
+ "can not be converted to sequence<GPUIntegerCoordinate> or GPUOrigin2DDict.",
+ opts,
+ );
+};
+
+// INTERFACE: GPUComputePassEncoder
+webidl.converters.GPUComputePassEncoder = webidl.createInterfaceConverter(
+ "GPUComputePassEncoder",
+ GPUComputePassEncoder.prototype,
+);
+
+// DICTIONARY: GPUComputePassTimestampWrites
+webidl.converters["GPUComputePassTimestampWrites"] = webidl
+ .createDictionaryConverter(
+ "GPUComputePassTimestampWrites",
+ [
+ {
+ key: "querySet",
+ converter: webidl.converters["GPUQuerySet"],
+ required: true,
+ },
+ {
+ key: "beginningOfPassWriteIndex",
+ converter: webidl.converters["GPUSize32"],
+ },
+ {
+ key: "endOfPassWriteIndex",
+ converter: webidl.converters["GPUSize32"],
+ },
+ ],
+ );
+
+// DICTIONARY: GPUComputePassDescriptor
+const dictMembersGPUComputePassDescriptor = [
+ {
+ key: "timestampWrites",
+ converter: webidl.converters["GPUComputePassTimestampWrites"],
+ },
+];
+webidl.converters["GPUComputePassDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUComputePassDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUComputePassDescriptor,
+ );
+
+// INTERFACE: GPURenderPassEncoder
+webidl.converters.GPURenderPassEncoder = webidl.createInterfaceConverter(
+ "GPURenderPassEncoder",
+ GPURenderPassEncoder.prototype,
+);
+
+// ENUM: GPULoadOp
+webidl.converters["GPULoadOp"] = webidl.createEnumConverter("GPULoadOp", [
+ "load",
+ "clear",
+]);
+
+// DICTIONARY: GPUColorDict
+const dictMembersGPUColorDict = [
+ { key: "r", converter: webidl.converters["double"], required: true },
+ { key: "g", converter: webidl.converters["double"], required: true },
+ { key: "b", converter: webidl.converters["double"], required: true },
+ { key: "a", converter: webidl.converters["double"], required: true },
+];
+webidl.converters["GPUColorDict"] = webidl.createDictionaryConverter(
+ "GPUColorDict",
+ dictMembersGPUColorDict,
+);
+
+// TYPEDEF: GPUColor
+webidl.converters["GPUColor"] = (V, opts) => {
+ // Union for (sequence<double> or GPUColorDict)
+ if (V === null || V === undefined) {
+ return webidl.converters["GPUColorDict"](V, opts);
+ }
+ if (typeof V === "object") {
+ const method = V[SymbolIterator];
+ if (method !== undefined) {
+ return webidl.converters["sequence<double>"](V, opts);
+ }
+ return webidl.converters["GPUColorDict"](V, opts);
+ }
+ throw webidl.makeException(
+ TypeError,
+ "can not be converted to sequence<double> or GPUColorDict.",
+ opts,
+ );
+};
+
+// ENUM: GPUStoreOp
+webidl.converters["GPUStoreOp"] = webidl.createEnumConverter("GPUStoreOp", [
+ "store",
+ "discard",
+]);
+
+// DICTIONARY: GPURenderPassColorAttachment
+const dictMembersGPURenderPassColorAttachment = [
+ {
+ key: "view",
+ converter: webidl.converters["GPUTextureView"],
+ required: true,
+ },
+ { key: "resolveTarget", converter: webidl.converters["GPUTextureView"] },
+ {
+ key: "clearValue",
+ converter: webidl.converters["GPUColor"],
+ },
+ {
+ key: "loadOp",
+ converter: webidl.converters["GPULoadOp"],
+ required: true,
+ },
+ {
+ key: "storeOp",
+ converter: webidl.converters["GPUStoreOp"],
+ required: true,
+ },
+];
+webidl.converters["GPURenderPassColorAttachment"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassColorAttachment",
+ dictMembersGPURenderPassColorAttachment,
+ );
+
+// DICTIONARY: GPURenderPassDepthStencilAttachment
+const dictMembersGPURenderPassDepthStencilAttachment = [
+ {
+ key: "view",
+ converter: webidl.converters["GPUTextureView"],
+ required: true,
+ },
+ {
+ key: "depthClearValue",
+ converter: webidl.converters["float"],
+ },
+ {
+ key: "depthLoadOp",
+ converter: webidl.converters["GPULoadOp"],
+ },
+ {
+ key: "depthStoreOp",
+ converter: webidl.converters["GPUStoreOp"],
+ },
+ {
+ key: "depthReadOnly",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+ {
+ key: "stencilClearValue",
+ converter: webidl.converters["GPUStencilValue"],
+ defaultValue: 0,
+ },
+ {
+ key: "stencilLoadOp",
+ converter: webidl.converters["GPULoadOp"],
+ },
+ {
+ key: "stencilStoreOp",
+ converter: webidl.converters["GPUStoreOp"],
+ },
+ {
+ key: "stencilReadOnly",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+];
+webidl.converters["GPURenderPassDepthStencilAttachment"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassDepthStencilAttachment",
+ dictMembersGPURenderPassDepthStencilAttachment,
+ );
+
+// INTERFACE: GPUQuerySet
+webidl.converters.GPUQuerySet = webidl.createInterfaceConverter(
+ "GPUQuerySet",
+ GPUQuerySet.prototype,
+);
+
+// DICTIONARY: GPURenderPassTimestampWrites
+webidl.converters["GPURenderPassTimestampWrites"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassTimestampWrites",
+ [
+ {
+ key: "querySet",
+ converter: webidl.converters["GPUQuerySet"],
+ required: true,
+ },
+ {
+ key: "beginningOfPassWriteIndex",
+ converter: webidl.converters["GPUSize32"],
+ },
+ {
+ key: "endOfPassWriteIndex",
+ converter: webidl.converters["GPUSize32"],
+ },
+ ],
+ );
+
+// DICTIONARY: GPURenderPassDescriptor
+const dictMembersGPURenderPassDescriptor = [
+ {
+ key: "colorAttachments",
+ converter: webidl.createSequenceConverter(
+ webidl.createNullableConverter(
+ webidl.converters["GPURenderPassColorAttachment"],
+ ),
+ ),
+ required: true,
+ },
+ {
+ key: "depthStencilAttachment",
+ converter: webidl.converters["GPURenderPassDepthStencilAttachment"],
+ },
+ {
+ key: "occlusionQuerySet",
+ converter: webidl.converters["GPUQuerySet"],
+ },
+ {
+ key: "timestampWrites",
+ converter: webidl.converters["GPURenderPassTimestampWrites"],
+ },
+];
+webidl.converters["GPURenderPassDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPURenderPassDescriptor,
+ );
+
+// INTERFACE: GPURenderBundle
+webidl.converters.GPURenderBundle = webidl.createInterfaceConverter(
+ "GPURenderBundle",
+ GPURenderBundle.prototype,
+);
+webidl.converters["sequence<GPURenderBundle>"] = webidl
+ .createSequenceConverter(webidl.converters["GPURenderBundle"]);
+
+// DICTIONARY: GPURenderBundleDescriptor
+const dictMembersGPURenderBundleDescriptor = [];
+webidl.converters["GPURenderBundleDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderBundleDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPURenderBundleDescriptor,
+ );
+
+// INTERFACE: GPURenderBundleEncoder
+webidl.converters.GPURenderBundleEncoder = webidl.createInterfaceConverter(
+ "GPURenderBundleEncoder",
+ GPURenderBundleEncoder.prototype,
+);
+
+// DICTIONARY: GPURenderPassLayout
+const dictMembersGPURenderPassLayout = [
+ {
+ key: "colorFormats",
+ converter: webidl.createSequenceConverter(
+ webidl.createNullableConverter(webidl.converters["GPUTextureFormat"]),
+ ),
+ required: true,
+ },
+ {
+ key: "depthStencilFormat",
+ converter: webidl.converters["GPUTextureFormat"],
+ },
+ {
+ key: "sampleCount",
+ converter: webidl.converters["GPUSize32"],
+ defaultValue: 1,
+ },
+];
+webidl.converters["GPURenderPassLayout"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassLayout",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPURenderPassLayout,
+ );
+
+// DICTIONARY: GPURenderBundleEncoderDescriptor
+const dictMembersGPURenderBundleEncoderDescriptor = [
+ {
+ key: "depthReadOnly",
+ converter: webidl.converters.boolean,
+ defaultValue: false,
+ },
+ {
+ key: "stencilReadOnly",
+ converter: webidl.converters.boolean,
+ defaultValue: false,
+ },
+];
+webidl.converters["GPURenderBundleEncoderDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderBundleEncoderDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPURenderPassLayout,
+ dictMembersGPURenderBundleEncoderDescriptor,
+ );
+
+// INTERFACE: GPUQueue
+webidl.converters.GPUQueue = webidl.createInterfaceConverter(
+ "GPUQueue",
+ GPUQueue.prototype,
+);
+
+// ENUM: GPUQueryType
+webidl.converters["GPUQueryType"] = webidl.createEnumConverter(
+ "GPUQueryType",
+ [
+ "occlusion",
+ "timestamp",
+ ],
+);
+
+// DICTIONARY: GPUQuerySetDescriptor
+const dictMembersGPUQuerySetDescriptor = [
+ {
+ key: "type",
+ converter: webidl.converters["GPUQueryType"],
+ required: true,
+ },
+ { key: "count", converter: webidl.converters["GPUSize32"], required: true },
+ {
+ key: "pipelineStatistics",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUPipelineStatisticName"],
+ ),
+ get defaultValue() {
+ return [];
+ },
+ },
+];
+webidl.converters["GPUQuerySetDescriptor"] = webidl.createDictionaryConverter(
+ "GPUQuerySetDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUQuerySetDescriptor,
+);
+
+// ENUM: GPUDeviceLostReason
+webidl.converters["GPUDeviceLostReason"] = webidl.createEnumConverter(
+ "GPUDeviceLostReason",
+ [
+ "destroyed",
+ ],
+);
+
+// // INTERFACE: GPUDeviceLostInfo
+// webidl.converters.GPUDeviceLostInfo = webidl.createInterfaceConverter(
+// "GPUDeviceLostInfo",
+// GPUDeviceLostInfo.prototype,
+// );
+
+// ENUM: GPUErrorFilter
+webidl.converters["GPUErrorFilter"] = webidl.createEnumConverter(
+ "GPUErrorFilter",
+ [
+ "out-of-memory",
+ "validation",
+ ],
+);
+
+// INTERFACE: GPUOutOfMemoryError
+webidl.converters.GPUOutOfMemoryError = webidl.createInterfaceConverter(
+ "GPUOutOfMemoryError",
+ GPUOutOfMemoryError.prototype,
+);
+
+// INTERFACE: GPUValidationError
+webidl.converters.GPUValidationError = webidl.createInterfaceConverter(
+ "GPUValidationError",
+ GPUValidationError.prototype,
+);
+
+// TYPEDEF: GPUError
+webidl.converters["GPUError"] = webidl.converters.any /** put union here! **/;
+
+// // INTERFACE: GPUUncapturedErrorEvent
+// webidl.converters.GPUUncapturedErrorEvent = webidl.createInterfaceConverter(
+// "GPUUncapturedErrorEvent",
+// GPUUncapturedErrorEvent.prototype,
+// );
+
+// DICTIONARY: GPUUncapturedErrorEventInit
+const dictMembersGPUUncapturedErrorEventInit = [
+ { key: "error", converter: webidl.converters["GPUError"], required: true },
+];
+webidl.converters["GPUUncapturedErrorEventInit"] = webidl
+ .createDictionaryConverter(
+ "GPUUncapturedErrorEventInit",
+ // dictMembersEventInit,
+ dictMembersGPUUncapturedErrorEventInit,
+ );
+
+// TYPEDEF: GPUBufferDynamicOffset
+webidl.converters["GPUBufferDynamicOffset"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+// TYPEDEF: GPUSignedOffset32
+webidl.converters["GPUSignedOffset32"] = (V, opts) =>
+ webidl.converters["long"](V, { ...opts, enforceRange: true });
+
+// TYPEDEF: GPUFlagsConstant
+webidl.converters["GPUFlagsConstant"] = webidl.converters["unsigned long"];
+
+const gpu = webidl.createBranded(GPU);
+export {
+ _device,
+ assertDevice,
+ createGPUTexture,
+ GPU,
+ gpu,
+ GPUAdapter,
+ GPUAdapterInfo,
+ GPUBindGroup,
+ GPUBindGroupLayout,
+ GPUBuffer,
+ GPUBufferUsage,
+ GPUColorWrite,
+ GPUCommandBuffer,
+ GPUCommandEncoder,
+ GPUComputePassEncoder,
+ GPUComputePipeline,
+ GPUDevice,
+ GPUDeviceLostInfo,
+ GPUError,
+ GPUMapMode,
+ GPUOutOfMemoryError,
+ GPUPipelineLayout,
+ GPUQuerySet,
+ GPUQueue,
+ GPURenderBundle,
+ GPURenderBundleEncoder,
+ GPURenderPassEncoder,
+ GPURenderPipeline,
+ GPUSampler,
+ GPUShaderModule,
+ GPUShaderStage,
+ GPUSupportedFeatures,
+ GPUSupportedLimits,
+ GPUTexture,
+ GPUTextureUsage,
+ GPUTextureView,
+ GPUValidationError,
+};
diff --git a/ext/webgpu/02_surface.js b/ext/webgpu/02_surface.js
new file mode 100644
index 000000000..515f2eec1
--- /dev/null
+++ b/ext/webgpu/02_surface.js
@@ -0,0 +1,235 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+/// <reference path="../../core/lib.deno_core.d.ts" />
+/// <reference path="../web/internal.d.ts" />
+/// <reference path="../web/lib.deno_web.d.ts" />
+/// <reference path="./lib.deno_webgpu.d.ts" />
+
+import { core, primordials } from "ext:core/mod.js";
+const ops = core.ops;
+import * as webidl from "ext:deno_webidl/00_webidl.js";
+import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
+const { Symbol, SymbolFor, ObjectPrototypeIsPrototypeOf } = primordials;
+import {
+ _device,
+ assertDevice,
+ createGPUTexture,
+ GPUTextureUsage,
+} from "ext:deno_webgpu/01_webgpu.js";
+
+const _surfaceRid = Symbol("[[surfaceRid]]");
+const _configuration = Symbol("[[configuration]]");
+const _canvas = Symbol("[[canvas]]");
+const _currentTexture = Symbol("[[currentTexture]]");
+class GPUCanvasContext {
+ /** @type {number} */
+ [_surfaceRid];
+ /** @type {InnerGPUDevice} */
+ [_device];
+ [_configuration];
+ [_canvas];
+ /** @type {GPUTexture | undefined} */
+ [_currentTexture];
+
+ get canvas() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ return this[_canvas];
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ configure(configuration) {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ configuration = webidl.converters.GPUCanvasConfiguration(configuration, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ this[_device] = configuration.device[_device];
+ this[_configuration] = configuration;
+ const device = assertDevice(this, {
+ prefix,
+ context: "configuration.device",
+ });
+
+ const { err } = ops.op_webgpu_surface_configure({
+ surfaceRid: this[_surfaceRid],
+ deviceRid: device.rid,
+ format: configuration.format,
+ viewFormats: configuration.viewFormats,
+ usage: configuration.usage,
+ width: configuration.width,
+ height: configuration.height,
+ alphaMode: configuration.alphaMode,
+ });
+
+ device.pushError(err);
+ }
+
+ unconfigure() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+
+ this[_configuration] = null;
+ this[_device] = null;
+ }
+
+ getCurrentTexture() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ const prefix =
+ "Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
+
+ if (this[_configuration] === null) {
+ throw new DOMException("context is not configured.", "InvalidStateError");
+ }
+
+ const device = assertDevice(this, { prefix, context: "this" });
+
+ if (this[_currentTexture]) {
+ return this[_currentTexture];
+ }
+
+ const { rid } = ops.op_webgpu_surface_get_current_texture(
+ device.rid,
+ this[_surfaceRid],
+ );
+
+ const texture = createGPUTexture(
+ {
+ size: {
+ width: this[_configuration].width,
+ height: this[_configuration].height,
+ depthOrArrayLayers: 1,
+ },
+ mipLevelCount: 1,
+ sampleCount: 1,
+ dimension: "2d",
+ format: this[_configuration].format,
+ usage: this[_configuration].usage,
+ },
+ device,
+ rid,
+ );
+ device.trackResource(texture);
+ this[_currentTexture] = texture;
+ return texture;
+ }
+
+ // Extended from spec. Required to present the texture; browser don't need this.
+ present() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
+ const device = assertDevice(this[_currentTexture], {
+ prefix,
+ context: "this",
+ });
+ ops.op_webgpu_surface_present(device.rid, this[_surfaceRid]);
+ this[_currentTexture].destroy();
+ this[_currentTexture] = undefined;
+ }
+
+ [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ return inspect(
+ createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(GPUCanvasContextPrototype, this),
+ keys: [
+ "canvas",
+ ],
+ }),
+ inspectOptions,
+ );
+ }
+}
+const GPUCanvasContextPrototype = GPUCanvasContext.prototype;
+
+function createCanvasContext(options) {
+ const canvasContext = webidl.createBranded(GPUCanvasContext);
+ canvasContext[_surfaceRid] = options.surfaceRid;
+ canvasContext[_canvas] = options.canvas;
+ return canvasContext;
+}
+
+// Converters
+
+// ENUM: GPUCanvasAlphaMode
+webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
+ "GPUCanvasAlphaMode",
+ [
+ "opaque",
+ "premultiplied",
+ ],
+);
+
+// NON-SPEC: ENUM: GPUPresentMode
+webidl.converters["GPUPresentMode"] = webidl.createEnumConverter(
+ "GPUPresentMode",
+ [
+ "autoVsync",
+ "autoNoVsync",
+ "fifo",
+ "fifoRelaxed",
+ "immediate",
+ "mailbox",
+ ],
+);
+
+// DICT: GPUCanvasConfiguration
+const dictMembersGPUCanvasConfiguration = [
+ { key: "device", converter: webidl.converters.GPUDevice, required: true },
+ {
+ key: "format",
+ converter: webidl.converters.GPUTextureFormat,
+ required: true,
+ },
+ {
+ key: "usage",
+ converter: webidl.converters["GPUTextureUsageFlags"],
+ defaultValue: GPUTextureUsage.RENDER_ATTACHMENT,
+ },
+ {
+ key: "alphaMode",
+ converter: webidl.converters["GPUCanvasAlphaMode"],
+ defaultValue: "opaque",
+ },
+
+ // Extended from spec
+ {
+ key: "presentMode",
+ converter: webidl.converters["GPUPresentMode"],
+ },
+ {
+ key: "width",
+ converter: webidl.converters["long"],
+ required: true,
+ },
+ {
+ key: "height",
+ converter: webidl.converters["long"],
+ required: true,
+ },
+ {
+ key: "viewFormats",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUTextureFormat"],
+ ),
+ get defaultValue() {
+ return [];
+ },
+ },
+];
+webidl.converters["GPUCanvasConfiguration"] = webidl
+ .createDictionaryConverter(
+ "GPUCanvasConfiguration",
+ dictMembersGPUCanvasConfiguration,
+ );
+
+window.__bootstrap.webgpu = {
+ ...window.__bootstrap.webgpu,
+ GPUCanvasContext,
+ createCanvasContext,
+};
diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml
new file mode 100644
index 000000000..b3f68d1d9
--- /dev/null
+++ b/ext/webgpu/Cargo.toml
@@ -0,0 +1,49 @@
+# Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "deno_webgpu"
+version = "0.94.0"
+authors = ["the Deno authors"]
+edition.workspace = true
+license = "MIT"
+readme = "README.md"
+repository = "https://github.com/gfx-rs/wgpu"
+description = "WebGPU implementation for Deno"
+
+[lib]
+path = "lib.rs"
+
+[features]
+surface = ["wgpu-core/raw-window-handle", "dep:raw-window-handle"]
+
+# We make all dependencies conditional on not being wasm,
+# so the whole workspace can built as wasm.
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+deno_core.workspace = true
+serde = { workspace = true, features = ["derive"] }
+tokio = { workspace = true, features = ["full"] }
+wgpu-types = { workspace = true, features = ["trace", "replay", "serde"] }
+raw-window-handle = { workspace = true, optional = true }
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgpu-core]
+workspace = true
+features = ["trace", "replay", "serde", "strict_asserts", "wgsl", "gles"]
+
+# We want the wgpu-core Metal backend on macOS and iOS.
+[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgpu-core]
+workspace = true
+features = ["metal"]
+
+# We want the wgpu-core Direct3D backends on Windows.
+[target.'cfg(windows)'.dependencies.wgpu-core]
+workspace = true
+features = ["dx11", "dx12"]
+
+[target.'cfg(windows)'.dependencies.wgpu-hal]
+workspace = true
+features = ["windows_rs"]
+
+# We want the wgpu-core Vulkan backend on Unix (but not Emscripten) and Windows.
+[target.'cfg(any(windows, all(unix, not(target_os = "emscripten"))))'.dependencies.wgpu-core]
+workspace = true
+features = ["vulkan"]
diff --git a/ext/webgpu/LICENSE.md b/ext/webgpu/LICENSE.md
new file mode 100644
index 000000000..aec557f3a
--- /dev/null
+++ b/ext/webgpu/LICENSE.md
@@ -0,0 +1,20 @@
+MIT License
+
+Copyright 2018-2023 the Deno authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ext/webgpu/README.md b/ext/webgpu/README.md
new file mode 100644
index 000000000..f5ac0d0b5
--- /dev/null
+++ b/ext/webgpu/README.md
@@ -0,0 +1,35 @@
+# deno_webgpu
+
+This op crate implements the WebGPU API as defined in
+https://gpuweb.github.io/gpuweb/ in Deno. The implementation targets the spec
+draft as of October 4, 2023. The spec is still very much in flux. This op crate
+tries to stay up to date with the spec, but is constrained by the features
+implemented in our GPU backend library [wgpu](https://github.com/gfx-rs/wgpu).
+
+The spec is still very bare bones, and is still missing many details. As the
+spec becomes more concrete, we will implement to follow the spec more closely.
+
+In addition, setting the `DENO_WEBGPU_TRACE` environmental variable will output
+a
+[wgpu trace](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications#tracing-infrastructure)
+to the specified directory.
+
+For testing this op crate will make use of the WebGPU conformance tests suite,
+running through our WPT runner. This will be used to validate implementation
+conformance.
+
+GitHub CI doesn't run with GPUs, so testing relies on software like DX WARP &
+Vulkan lavapipe. Currently only using DX WARP works, so tests are only run on
+Windows.
+
+## Links
+
+Specification: https://gpuweb.github.io/gpuweb/
+
+Design documents: https://github.com/gpuweb/gpuweb/tree/main/design
+
+Conformance tests suite: https://github.com/gpuweb/cts
+
+WebGPU examples for Deno: https://github.com/crowlKats/webgpu-examples
+
+wgpu-users matrix channel: https://matrix.to/#/#wgpu-users:matrix.org
diff --git a/ext/webgpu/binding.rs b/ext/webgpu/binding.rs
new file mode 100644
index 000000000..81d1f7e8c
--- /dev/null
+++ b/ext/webgpu/binding.rs
@@ -0,0 +1,340 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::rc::Rc;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuBindGroupLayout(
+ pub(crate) crate::Instance,
+ pub(crate) wgpu_core::id::BindGroupLayoutId,
+);
+impl Resource for WebGpuBindGroupLayout {
+ fn name(&self) -> Cow<str> {
+ "webGPUBindGroupLayout".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.bind_group_layout_drop(self.1));
+ }
+}
+
+pub(crate) struct WebGpuBindGroup(
+ pub(crate) crate::Instance,
+ pub(crate) wgpu_core::id::BindGroupId,
+);
+impl Resource for WebGpuBindGroup {
+ fn name(&self) -> Cow<str> {
+ "webGPUBindGroup".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.bind_group_drop(self.1));
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuBufferBindingLayout {
+ r#type: GpuBufferBindingType,
+ has_dynamic_offset: bool,
+ min_binding_size: u64,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+enum GpuBufferBindingType {
+ Uniform,
+ Storage,
+ ReadOnlyStorage,
+}
+
+impl From<GpuBufferBindingType> for wgpu_types::BufferBindingType {
+ fn from(binding_type: GpuBufferBindingType) -> Self {
+ match binding_type {
+ GpuBufferBindingType::Uniform => wgpu_types::BufferBindingType::Uniform,
+ GpuBufferBindingType::Storage => {
+ wgpu_types::BufferBindingType::Storage { read_only: false }
+ }
+ GpuBufferBindingType::ReadOnlyStorage => {
+ wgpu_types::BufferBindingType::Storage { read_only: true }
+ }
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuSamplerBindingLayout {
+ r#type: wgpu_types::SamplerBindingType,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuTextureBindingLayout {
+ sample_type: GpuTextureSampleType,
+ view_dimension: wgpu_types::TextureViewDimension,
+ multisampled: bool,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+enum GpuTextureSampleType {
+ Float,
+ UnfilterableFloat,
+ Depth,
+ Sint,
+ Uint,
+}
+
+impl From<GpuTextureSampleType> for wgpu_types::TextureSampleType {
+ fn from(sample_type: GpuTextureSampleType) -> Self {
+ match sample_type {
+ GpuTextureSampleType::Float => {
+ wgpu_types::TextureSampleType::Float { filterable: true }
+ }
+ GpuTextureSampleType::UnfilterableFloat => {
+ wgpu_types::TextureSampleType::Float { filterable: false }
+ }
+ GpuTextureSampleType::Depth => wgpu_types::TextureSampleType::Depth,
+ GpuTextureSampleType::Sint => wgpu_types::TextureSampleType::Sint,
+ GpuTextureSampleType::Uint => wgpu_types::TextureSampleType::Uint,
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuStorageTextureBindingLayout {
+ access: GpuStorageTextureAccess,
+ format: wgpu_types::TextureFormat,
+ view_dimension: wgpu_types::TextureViewDimension,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+enum GpuStorageTextureAccess {
+ WriteOnly,
+}
+
+impl From<GpuStorageTextureAccess> for wgpu_types::StorageTextureAccess {
+ fn from(access: GpuStorageTextureAccess) -> Self {
+ match access {
+ GpuStorageTextureAccess::WriteOnly => {
+ wgpu_types::StorageTextureAccess::WriteOnly
+ }
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuBindGroupLayoutEntry {
+ binding: u32,
+ visibility: u32,
+ #[serde(flatten)]
+ binding_type: GpuBindingType,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+enum GpuBindingType {
+ Buffer(GpuBufferBindingLayout),
+ Sampler(GpuSamplerBindingLayout),
+ Texture(GpuTextureBindingLayout),
+ StorageTexture(GpuStorageTextureBindingLayout),
+}
+
+impl From<GpuBindingType> for wgpu_types::BindingType {
+ fn from(binding_type: GpuBindingType) -> wgpu_types::BindingType {
+ match binding_type {
+ GpuBindingType::Buffer(buffer) => wgpu_types::BindingType::Buffer {
+ ty: buffer.r#type.into(),
+ has_dynamic_offset: buffer.has_dynamic_offset,
+ min_binding_size: std::num::NonZeroU64::new(buffer.min_binding_size),
+ },
+ GpuBindingType::Sampler(sampler) => {
+ wgpu_types::BindingType::Sampler(sampler.r#type)
+ }
+ GpuBindingType::Texture(texture) => wgpu_types::BindingType::Texture {
+ sample_type: texture.sample_type.into(),
+ view_dimension: texture.view_dimension,
+ multisampled: texture.multisampled,
+ },
+ GpuBindingType::StorageTexture(storage_texture) => {
+ wgpu_types::BindingType::StorageTexture {
+ access: storage_texture.access.into(),
+ format: storage_texture.format,
+ view_dimension: storage_texture.view_dimension,
+ }
+ }
+ }
+ }
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_bind_group_layout(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[serde] entries: Vec<GpuBindGroupLayoutEntry>,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+
+ let entries = entries
+ .into_iter()
+ .map(|entry| {
+ wgpu_types::BindGroupLayoutEntry {
+ binding: entry.binding,
+ visibility: wgpu_types::ShaderStages::from_bits(entry.visibility)
+ .unwrap(),
+ ty: entry.binding_type.into(),
+ count: None, // native-only
+ }
+ })
+ .collect::<Vec<_>>();
+
+ let descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
+ label: Some(label),
+ entries: Cow::from(entries),
+ };
+
+ gfx_put!(device => instance.device_create_bind_group_layout(
+ device,
+ &descriptor,
+ ()
+ ) => state, WebGpuBindGroupLayout)
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_pipeline_layout(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[serde] bind_group_layouts: Vec<u32>,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+
+ let bind_group_layouts = bind_group_layouts
+ .into_iter()
+ .map(|rid| {
+ let bind_group_layout =
+ state.resource_table.get::<WebGpuBindGroupLayout>(rid)?;
+ Ok(bind_group_layout.1)
+ })
+ .collect::<Result<Vec<_>, AnyError>>()?;
+
+ let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
+ label: Some(label),
+ bind_group_layouts: Cow::from(bind_group_layouts),
+ push_constant_ranges: Default::default(),
+ };
+
+ gfx_put!(device => instance.device_create_pipeline_layout(
+ device,
+ &descriptor,
+ ()
+ ) => state, super::pipeline::WebGpuPipelineLayout)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuBindGroupEntry {
+ binding: u32,
+ kind: String,
+ resource: ResourceId,
+ offset: Option<u64>,
+ size: Option<u64>,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_bind_group(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[smi] layout: ResourceId,
+ #[serde] entries: Vec<GpuBindGroupEntry>,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+
+ let entries = entries
+ .into_iter()
+ .map(|entry| {
+ Ok(wgpu_core::binding_model::BindGroupEntry {
+ binding: entry.binding,
+ resource: match entry.kind.as_str() {
+ "GPUSampler" => {
+ let sampler_resource =
+ state
+ .resource_table
+ .get::<super::sampler::WebGpuSampler>(entry.resource)?;
+ wgpu_core::binding_model::BindingResource::Sampler(
+ sampler_resource.1,
+ )
+ }
+ "GPUTextureView" => {
+ let texture_view_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(entry.resource)?;
+ wgpu_core::binding_model::BindingResource::TextureView(
+ texture_view_resource.1,
+ )
+ }
+ "GPUBufferBinding" => {
+ let buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(entry.resource)?;
+ wgpu_core::binding_model::BindingResource::Buffer(
+ wgpu_core::binding_model::BufferBinding {
+ buffer_id: buffer_resource.1,
+ offset: entry.offset.unwrap_or(0),
+ size: std::num::NonZeroU64::new(entry.size.unwrap_or(0)),
+ },
+ )
+ }
+ _ => unreachable!(),
+ },
+ })
+ })
+ .collect::<Result<Vec<_>, AnyError>>()?;
+
+ let bind_group_layout =
+ state.resource_table.get::<WebGpuBindGroupLayout>(layout)?;
+
+ let descriptor = wgpu_core::binding_model::BindGroupDescriptor {
+ label: Some(label),
+ layout: bind_group_layout.1,
+ entries: Cow::from(entries),
+ };
+
+ gfx_put!(device => instance.device_create_bind_group(
+ device,
+ &descriptor,
+ ()
+ ) => state, WebGpuBindGroup)
+}
diff --git a/ext/webgpu/buffer.rs b/ext/webgpu/buffer.rs
new file mode 100644
index 000000000..2f9724825
--- /dev/null
+++ b/ext/webgpu/buffer.rs
@@ -0,0 +1,205 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::futures::channel::oneshot;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::time::Duration;
+use wgpu_core::resource::BufferAccessResult;
+
+use super::error::DomExceptionOperationError;
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuBuffer(
+ pub(crate) super::Instance,
+ pub(crate) wgpu_core::id::BufferId,
+);
+impl Resource for WebGpuBuffer {
+ fn name(&self) -> Cow<str> {
+ "webGPUBuffer".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.buffer_drop(self.1, true));
+ }
+}
+
+struct WebGpuBufferMapped(*mut u8, usize);
+impl Resource for WebGpuBufferMapped {
+ fn name(&self) -> Cow<str> {
+ "webGPUBufferMapped".into()
+ }
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_buffer(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[number] size: u64,
+ usage: u32,
+ mapped_at_creation: bool,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+
+ let descriptor = wgpu_core::resource::BufferDescriptor {
+ label: Some(label),
+ size,
+ usage: wgpu_types::BufferUsages::from_bits(usage)
+ .ok_or_else(|| type_error("usage is not valid"))?,
+ mapped_at_creation,
+ };
+
+ gfx_put!(device => instance.device_create_buffer(
+ device,
+ &descriptor,
+ ()
+ ) => state, WebGpuBuffer)
+}
+
+#[op2(async)]
+#[serde]
+pub async fn op_webgpu_buffer_get_map_async(
+ state: Rc<RefCell<OpState>>,
+ #[smi] buffer_rid: ResourceId,
+ #[smi] device_rid: ResourceId,
+ mode: u32,
+ #[number] offset: u64,
+ #[number] size: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let (sender, receiver) = oneshot::channel::<BufferAccessResult>();
+
+ let device;
+ {
+ let state_ = state.borrow();
+ let instance = state_.borrow::<super::Instance>();
+ let buffer_resource =
+ state_.resource_table.get::<WebGpuBuffer>(buffer_rid)?;
+ let buffer = buffer_resource.1;
+ let device_resource = state_
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ device = device_resource.1;
+
+ let callback = Box::new(move |status| {
+ sender.send(status).unwrap();
+ });
+
+ // TODO(lucacasonato): error handling
+ let maybe_err = gfx_select!(buffer => instance.buffer_map_async(
+ buffer,
+ offset..(offset + size),
+ wgpu_core::resource::BufferMapOperation {
+ host: match mode {
+ 1 => wgpu_core::device::HostMap::Read,
+ 2 => wgpu_core::device::HostMap::Write,
+ _ => unreachable!(),
+ },
+ callback: wgpu_core::resource::BufferMapCallback::from_rust(callback),
+ }
+ ))
+ .err();
+
+ if maybe_err.is_some() {
+ return Ok(WebGpuResult::maybe_err(maybe_err));
+ }
+ }
+
+ let done = Rc::new(RefCell::new(false));
+ let done_ = done.clone();
+ let device_poll_fut = async move {
+ while !*done.borrow() {
+ {
+ let state = state.borrow();
+ let instance = state.borrow::<super::Instance>();
+ gfx_select!(device => instance.device_poll(device, wgpu_types::Maintain::Wait))
+ .unwrap();
+ }
+ tokio::time::sleep(Duration::from_millis(10)).await;
+ }
+ Ok::<(), AnyError>(())
+ };
+
+ let receiver_fut = async move {
+ receiver.await??;
+ let mut done = done_.borrow_mut();
+ *done = true;
+ Ok::<(), AnyError>(())
+ };
+
+ tokio::try_join!(device_poll_fut, receiver_fut)?;
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_buffer_get_mapped_range(
+ state: &mut OpState,
+ #[smi] buffer_rid: ResourceId,
+ #[number] offset: u64,
+ #[number] size: Option<u64>,
+ #[buffer] buf: &mut [u8],
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource = state.resource_table.get::<WebGpuBuffer>(buffer_rid)?;
+ let buffer = buffer_resource.1;
+
+ let (slice_pointer, range_size) =
+ gfx_select!(buffer => instance.buffer_get_mapped_range(
+ buffer,
+ offset,
+ size
+ ))
+ .map_err(|e| DomExceptionOperationError::new(&e.to_string()))?;
+
+ // SAFETY: guarantee to be safe from wgpu
+ let slice = unsafe {
+ std::slice::from_raw_parts_mut(slice_pointer, range_size as usize)
+ };
+ buf.copy_from_slice(slice);
+
+ let rid = state
+ .resource_table
+ .add(WebGpuBufferMapped(slice_pointer, range_size as usize));
+
+ Ok(WebGpuResult::rid(rid))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_buffer_unmap(
+ state: &mut OpState,
+ #[smi] buffer_rid: ResourceId,
+ #[smi] mapped_rid: ResourceId,
+ #[buffer] buf: Option<&[u8]>,
+) -> Result<WebGpuResult, AnyError> {
+ let mapped_resource = state
+ .resource_table
+ .take::<WebGpuBufferMapped>(mapped_rid)?;
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource = state.resource_table.get::<WebGpuBuffer>(buffer_rid)?;
+ let buffer = buffer_resource.1;
+
+ if let Some(buf) = buf {
+ // SAFETY: guarantee to be safe from wgpu
+ let slice = unsafe {
+ std::slice::from_raw_parts_mut(mapped_resource.0, mapped_resource.1)
+ };
+ slice.copy_from_slice(buf);
+ }
+
+ gfx_ok!(buffer => instance.buffer_unmap(buffer))
+}
diff --git a/ext/webgpu/bundle.rs b/ext/webgpu/bundle.rs
new file mode 100644
index 000000000..59b7c8965
--- /dev/null
+++ b/ext/webgpu/bundle.rs
@@ -0,0 +1,405 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use super::error::WebGpuResult;
+
+struct WebGpuRenderBundleEncoder(
+ RefCell<wgpu_core::command::RenderBundleEncoder>,
+);
+impl Resource for WebGpuRenderBundleEncoder {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderBundleEncoder".into()
+ }
+}
+
+pub(crate) struct WebGpuRenderBundle(
+ pub(crate) super::Instance,
+ pub(crate) wgpu_core::id::RenderBundleId,
+);
+impl Resource for WebGpuRenderBundle {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderBundle".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.render_bundle_drop(self.1));
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateRenderBundleEncoderArgs {
+ device_rid: ResourceId,
+ label: String,
+ color_formats: Vec<Option<wgpu_types::TextureFormat>>,
+ depth_stencil_format: Option<wgpu_types::TextureFormat>,
+ sample_count: u32,
+ depth_read_only: bool,
+ stencil_read_only: bool,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_render_bundle_encoder(
+ state: &mut OpState,
+ #[serde] args: CreateRenderBundleEncoderArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.1;
+
+ let depth_stencil = args.depth_stencil_format.map(|format| {
+ wgpu_types::RenderBundleDepthStencil {
+ format,
+ depth_read_only: args.depth_read_only,
+ stencil_read_only: args.stencil_read_only,
+ }
+ });
+
+ let descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
+ label: Some(Cow::Owned(args.label)),
+ color_formats: Cow::from(args.color_formats),
+ sample_count: args.sample_count,
+ depth_stencil,
+ multiview: None,
+ };
+
+ let res =
+ wgpu_core::command::RenderBundleEncoder::new(&descriptor, device, None);
+ let (render_bundle_encoder, maybe_err) = match res {
+ Ok(encoder) => (encoder, None),
+ Err(e) => (
+ wgpu_core::command::RenderBundleEncoder::dummy(device),
+ Some(e),
+ ),
+ };
+
+ let rid = state
+ .resource_table
+ .add(WebGpuRenderBundleEncoder(RefCell::new(
+ render_bundle_encoder,
+ )));
+
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_finish(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ #[string] label: Cow<str>,
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .take::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+ let render_bundle_encoder = Rc::try_unwrap(render_bundle_encoder_resource)
+ .ok()
+ .expect("unwrapping render_bundle_encoder_resource should succeed")
+ .0
+ .into_inner();
+ let instance = state.borrow::<super::Instance>();
+
+ gfx_put!(render_bundle_encoder.parent() => instance.render_bundle_encoder_finish(
+ render_bundle_encoder,
+ &wgpu_core::command::RenderBundleDescriptor {
+ label: Some(label),
+ },
+ ()
+ ) => state, WebGpuRenderBundle)
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_set_bind_group(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ index: u32,
+ #[smi] bind_group: ResourceId,
+ #[buffer] dynamic_offsets_data: &[u32],
+ #[number] dynamic_offsets_data_start: usize,
+ #[number] dynamic_offsets_data_length: usize,
+) -> Result<WebGpuResult, AnyError> {
+ let bind_group_resource =
+ state
+ .resource_table
+ .get::<super::binding::WebGpuBindGroup>(bind_group)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ let start = dynamic_offsets_data_start;
+ let len = dynamic_offsets_data_length;
+
+ // Assert that length and start are both in bounds
+ assert!(start <= dynamic_offsets_data.len());
+ assert!(len <= dynamic_offsets_data.len() - start);
+
+ let dynamic_offsets_data = &dynamic_offsets_data[start..start + len];
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ index,
+ bind_group_resource.1,
+ dynamic_offsets_data.as_ptr(),
+ dynamic_offsets_data.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_push_debug_group(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ #[string] group_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ let label = std::ffi::CString::new(group_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ label.as_ptr(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_pop_debug_group(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_insert_debug_marker(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ #[string] marker_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ let label = std::ffi::CString::new(marker_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ label.as_ptr(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_set_pipeline(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ #[smi] pipeline: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pipeline_resource =
+ state
+ .resource_table
+ .get::<super::pipeline::WebGpuRenderPipeline>(pipeline)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ render_pipeline_resource.1,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_set_index_buffer(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ #[smi] buffer: ResourceId,
+ #[serde] index_format: wgpu_types::IndexFormat,
+ #[number] offset: u64,
+ #[number] size: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(buffer)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+ let size = Some(
+ std::num::NonZeroU64::new(size)
+ .ok_or_else(|| type_error("size must be larger than 0"))?,
+ );
+
+ render_bundle_encoder_resource
+ .0
+ .borrow_mut()
+ .set_index_buffer(buffer_resource.1, index_format, offset, size);
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ slot: u32,
+ #[smi] buffer: ResourceId,
+ #[number] offset: u64,
+ #[number] size: Option<u64>,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(buffer)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+ let size = if let Some(size) = size {
+ Some(
+ std::num::NonZeroU64::new(size)
+ .ok_or_else(|| type_error("size must be larger than 0"))?,
+ )
+ } else {
+ None
+ };
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ slot,
+ buffer_resource.1,
+ offset,
+ size,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_draw(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ vertex_count,
+ instance_count,
+ first_vertex,
+ first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_draw_indexed(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ index_count,
+ instance_count,
+ first_index,
+ base_vertex,
+ first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_bundle_encoder_draw_indirect(
+ state: &mut OpState,
+ #[smi] render_bundle_encoder_rid: ResourceId,
+ #[smi] indirect_buffer: ResourceId,
+ #[number] indirect_offset: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
+ let render_bundle_encoder_resource =
+ state
+ .resource_table
+ .get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ buffer_resource.1,
+ indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
diff --git a/ext/webgpu/command_encoder.rs b/ext/webgpu/command_encoder.rs
new file mode 100644
index 000000000..c5947abaf
--- /dev/null
+++ b/ext/webgpu/command_encoder.rs
@@ -0,0 +1,633 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use crate::WebGpuQuerySet;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuCommandEncoder(
+ pub(crate) super::Instance,
+ pub(crate) wgpu_core::id::CommandEncoderId, // TODO: should maybe be option?
+);
+impl Resource for WebGpuCommandEncoder {
+ fn name(&self) -> Cow<str> {
+ "webGPUCommandEncoder".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.command_encoder_drop(self.1));
+ }
+}
+
+pub(crate) struct WebGpuCommandBuffer(
+ pub(crate) super::Instance,
+ pub(crate) RefCell<Option<wgpu_core::id::CommandBufferId>>,
+);
+impl Resource for WebGpuCommandBuffer {
+ fn name(&self) -> Cow<str> {
+ "webGPUCommandBuffer".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ if let Some(id) = *self.1.borrow() {
+ let instance = &self.0;
+ gfx_select!(id => instance.command_buffer_drop(id));
+ }
+ }
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_command_encoder(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[string] label: Cow<str>,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+
+ let descriptor = wgpu_types::CommandEncoderDescriptor { label: Some(label) };
+
+ gfx_put!(device => instance.device_create_command_encoder(
+ device,
+ &descriptor,
+ ()
+ ) => state, WebGpuCommandEncoder)
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuRenderPassColorAttachment {
+ view: ResourceId,
+ resolve_target: Option<ResourceId>,
+ clear_value: Option<wgpu_types::Color>,
+ load_op: wgpu_core::command::LoadOp,
+ store_op: wgpu_core::command::StoreOp,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuRenderPassDepthStencilAttachment {
+ view: ResourceId,
+ depth_clear_value: f32,
+ depth_load_op: Option<wgpu_core::command::LoadOp>,
+ depth_store_op: Option<wgpu_core::command::StoreOp>,
+ depth_read_only: bool,
+ stencil_clear_value: u32,
+ stencil_load_op: Option<wgpu_core::command::LoadOp>,
+ stencil_store_op: Option<wgpu_core::command::StoreOp>,
+ stencil_read_only: bool,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPURenderPassTimestampWrites {
+ query_set: ResourceId,
+ beginning_of_pass_write_index: Option<u32>,
+ end_of_pass_write_index: Option<u32>,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_begin_render_pass(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[serde] color_attachments: Vec<Option<GpuRenderPassColorAttachment>>,
+ #[serde] depth_stencil_attachment: Option<
+ GpuRenderPassDepthStencilAttachment,
+ >,
+ #[smi] occlusion_query_set: Option<ResourceId>,
+ #[serde] timestamp_writes: Option<GPURenderPassTimestampWrites>,
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+
+ let color_attachments = color_attachments
+ .into_iter()
+ .map(|color_attachment| {
+ let rp_at = if let Some(at) = color_attachment.as_ref() {
+ let texture_view_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(at.view)?;
+
+ let resolve_target = at
+ .resolve_target
+ .map(|rid| {
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(rid)
+ })
+ .transpose()?
+ .map(|texture| texture.1);
+
+ Some(wgpu_core::command::RenderPassColorAttachment {
+ view: texture_view_resource.1,
+ resolve_target,
+ channel: wgpu_core::command::PassChannel {
+ load_op: at.load_op,
+ store_op: at.store_op,
+ clear_value: at.clear_value.unwrap_or_default(),
+ read_only: false,
+ },
+ })
+ } else {
+ None
+ };
+ Ok(rp_at)
+ })
+ .collect::<Result<Vec<_>, AnyError>>()?;
+
+ let mut processed_depth_stencil_attachment = None;
+
+ if let Some(attachment) = depth_stencil_attachment {
+ let texture_view_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTextureView>(attachment.view)?;
+
+ processed_depth_stencil_attachment =
+ Some(wgpu_core::command::RenderPassDepthStencilAttachment {
+ view: texture_view_resource.1,
+ depth: wgpu_core::command::PassChannel {
+ load_op: attachment
+ .depth_load_op
+ .unwrap_or(wgpu_core::command::LoadOp::Load),
+ store_op: attachment
+ .depth_store_op
+ .unwrap_or(wgpu_core::command::StoreOp::Store),
+ clear_value: attachment.depth_clear_value,
+ read_only: attachment.depth_read_only,
+ },
+ stencil: wgpu_core::command::PassChannel {
+ load_op: attachment
+ .stencil_load_op
+ .unwrap_or(wgpu_core::command::LoadOp::Load),
+ store_op: attachment
+ .stencil_store_op
+ .unwrap_or(wgpu_core::command::StoreOp::Store),
+ clear_value: attachment.stencil_clear_value,
+ read_only: attachment.stencil_read_only,
+ },
+ });
+ }
+
+ let timestamp_writes = if let Some(timestamp_writes) = timestamp_writes {
+ let query_set_resource = state
+ .resource_table
+ .get::<WebGpuQuerySet>(timestamp_writes.query_set)?;
+ let query_set = query_set_resource.1;
+
+ Some(wgpu_core::command::RenderPassTimestampWrites {
+ query_set,
+ beginning_of_pass_write_index: timestamp_writes
+ .beginning_of_pass_write_index,
+ end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,
+ })
+ } else {
+ None
+ };
+
+ let occlusion_query_set_resource = occlusion_query_set
+ .map(|rid| state.resource_table.get::<WebGpuQuerySet>(rid))
+ .transpose()?
+ .map(|query_set| query_set.1);
+
+ let descriptor = wgpu_core::command::RenderPassDescriptor {
+ label: Some(label),
+ color_attachments: Cow::from(color_attachments),
+ depth_stencil_attachment: processed_depth_stencil_attachment.as_ref(),
+ timestamp_writes: timestamp_writes.as_ref(),
+ occlusion_query_set: occlusion_query_set_resource,
+ };
+
+ let render_pass = wgpu_core::command::RenderPass::new(
+ command_encoder_resource.1,
+ &descriptor,
+ );
+
+ let rid = state
+ .resource_table
+ .add(super::render_pass::WebGpuRenderPass(RefCell::new(
+ render_pass,
+ )));
+
+ Ok(WebGpuResult::rid(rid))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPUComputePassTimestampWrites {
+ query_set: ResourceId,
+ beginning_of_pass_write_index: Option<u32>,
+ end_of_pass_write_index: Option<u32>,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_begin_compute_pass(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[serde] timestamp_writes: Option<GPUComputePassTimestampWrites>,
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+
+ let timestamp_writes = if let Some(timestamp_writes) = timestamp_writes {
+ let query_set_resource = state
+ .resource_table
+ .get::<WebGpuQuerySet>(timestamp_writes.query_set)?;
+ let query_set = query_set_resource.1;
+
+ Some(wgpu_core::command::ComputePassTimestampWrites {
+ query_set,
+ beginning_of_pass_write_index: timestamp_writes
+ .beginning_of_pass_write_index,
+ end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,
+ })
+ } else {
+ None
+ };
+
+ let descriptor = wgpu_core::command::ComputePassDescriptor {
+ label: Some(label),
+ timestamp_writes: timestamp_writes.as_ref(),
+ };
+
+ let compute_pass = wgpu_core::command::ComputePass::new(
+ command_encoder_resource.1,
+ &descriptor,
+ );
+
+ let rid = state
+ .resource_table
+ .add(super::compute_pass::WebGpuComputePass(RefCell::new(
+ compute_pass,
+ )));
+
+ Ok(WebGpuResult::rid(rid))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_copy_buffer_to_buffer(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[smi] source: ResourceId,
+ #[number] source_offset: u64,
+ #[smi] destination: ResourceId,
+ #[number] destination_offset: u64,
+ #[number] size: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let source_buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(source)?;
+ let source_buffer = source_buffer_resource.1;
+ let destination_buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(destination)?;
+ let destination_buffer = destination_buffer_resource.1;
+
+ gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_buffer(
+ command_encoder,
+ source_buffer,
+ source_offset,
+ destination_buffer,
+ destination_offset,
+ size
+ ))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuImageCopyBuffer {
+ buffer: ResourceId,
+ offset: u64,
+ bytes_per_row: Option<u32>,
+ rows_per_image: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuImageCopyTexture {
+ pub texture: ResourceId,
+ pub mip_level: u32,
+ pub origin: wgpu_types::Origin3d,
+ pub aspect: wgpu_types::TextureAspect,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_copy_buffer_to_texture(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[serde] source: GpuImageCopyBuffer,
+ #[serde] destination: GpuImageCopyTexture,
+ #[serde] copy_size: wgpu_types::Extent3d,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let source_buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(source.buffer)?;
+ let destination_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(destination.texture)?;
+
+ let source = wgpu_core::command::ImageCopyBuffer {
+ buffer: source_buffer_resource.1,
+ layout: wgpu_types::ImageDataLayout {
+ offset: source.offset,
+ bytes_per_row: source.bytes_per_row,
+ rows_per_image: source.rows_per_image,
+ },
+ };
+ let destination = wgpu_core::command::ImageCopyTexture {
+ texture: destination_texture_resource.id,
+ mip_level: destination.mip_level,
+ origin: destination.origin,
+ aspect: destination.aspect,
+ };
+ gfx_ok!(command_encoder => instance.command_encoder_copy_buffer_to_texture(
+ command_encoder,
+ &source,
+ &destination,
+ &copy_size
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_copy_texture_to_buffer(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[serde] source: GpuImageCopyTexture,
+ #[serde] destination: GpuImageCopyBuffer,
+ #[serde] copy_size: wgpu_types::Extent3d,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let source_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(source.texture)?;
+ let destination_buffer_resource =
+ state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(destination.buffer)?;
+
+ let source = wgpu_core::command::ImageCopyTexture {
+ texture: source_texture_resource.id,
+ mip_level: source.mip_level,
+ origin: source.origin,
+ aspect: source.aspect,
+ };
+ let destination = wgpu_core::command::ImageCopyBuffer {
+ buffer: destination_buffer_resource.1,
+ layout: wgpu_types::ImageDataLayout {
+ offset: destination.offset,
+ bytes_per_row: destination.bytes_per_row,
+ rows_per_image: destination.rows_per_image,
+ },
+ };
+ gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_buffer(
+ command_encoder,
+ &source,
+ &destination,
+ &copy_size
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_copy_texture_to_texture(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[serde] source: GpuImageCopyTexture,
+ #[serde] destination: GpuImageCopyTexture,
+ #[serde] copy_size: wgpu_types::Extent3d,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let source_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(source.texture)?;
+ let destination_texture_resource =
+ state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(destination.texture)?;
+
+ let source = wgpu_core::command::ImageCopyTexture {
+ texture: source_texture_resource.id,
+ mip_level: source.mip_level,
+ origin: source.origin,
+ aspect: source.aspect,
+ };
+ let destination = wgpu_core::command::ImageCopyTexture {
+ texture: destination_texture_resource.id,
+ mip_level: destination.mip_level,
+ origin: destination.origin,
+ aspect: destination.aspect,
+ };
+ gfx_ok!(command_encoder => instance.command_encoder_copy_texture_to_texture(
+ command_encoder,
+ &source,
+ &destination,
+ &copy_size
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_clear_buffer(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[smi] buffer_rid: ResourceId,
+ #[number] offset: u64,
+ #[number] size: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let destination_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(buffer_rid)?;
+
+ gfx_ok!(command_encoder => instance.command_encoder_clear_buffer(
+ command_encoder,
+ destination_resource.1,
+ offset,
+ std::num::NonZeroU64::new(size)
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_push_debug_group(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[string] group_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+
+ gfx_ok!(command_encoder => instance.command_encoder_push_debug_group(command_encoder, group_label))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_pop_debug_group(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+
+ gfx_ok!(command_encoder => instance.command_encoder_pop_debug_group(command_encoder))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_insert_debug_marker(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[string] marker_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+
+ gfx_ok!(command_encoder => instance.command_encoder_insert_debug_marker(
+ command_encoder,
+ marker_label
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_write_timestamp(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[smi] query_set: ResourceId,
+ query_index: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(query_set)?;
+
+ gfx_ok!(command_encoder => instance.command_encoder_write_timestamp(
+ command_encoder,
+ query_set_resource.1,
+ query_index
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_resolve_query_set(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[smi] query_set: ResourceId,
+ first_query: u32,
+ query_count: u32,
+ #[smi] destination: ResourceId,
+ #[number] destination_offset: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGpuQuerySet>(query_set)?;
+ let destination_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(destination)?;
+
+ gfx_ok!(command_encoder => instance.command_encoder_resolve_query_set(
+ command_encoder,
+ query_set_resource.1,
+ first_query,
+ query_count,
+ destination_resource.1,
+ destination_offset
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_command_encoder_finish(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[string] label: Cow<str>,
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .take::<WebGpuCommandEncoder>(command_encoder_rid)?;
+ let command_encoder = command_encoder_resource.1;
+ let instance = state.borrow::<super::Instance>();
+
+ let descriptor = wgpu_types::CommandBufferDescriptor { label: Some(label) };
+
+ let (val, maybe_err) = gfx_select!(command_encoder => instance.command_encoder_finish(
+ command_encoder,
+ &descriptor
+ ));
+
+ let rid = state.resource_table.add(WebGpuCommandBuffer(
+ instance.clone(),
+ RefCell::new(Some(val)),
+ ));
+
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+}
diff --git a/ext/webgpu/compute_pass.rs b/ext/webgpu/compute_pass.rs
new file mode 100644
index 000000000..6dbf16020
--- /dev/null
+++ b/ext/webgpu/compute_pass.rs
@@ -0,0 +1,225 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuComputePass(
+ pub(crate) RefCell<wgpu_core::command::ComputePass>,
+);
+impl Resource for WebGpuComputePass {
+ fn name(&self) -> Cow<str> {
+ "webGPUComputePass".into()
+ }
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_set_pipeline(
+ state: &mut OpState,
+ #[smi] compute_pass_rid: ResourceId,
+ #[smi] pipeline: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pipeline_resource =
+ state
+ .resource_table
+ .get::<super::pipeline::WebGpuComputePipeline>(pipeline)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_set_pipeline(
+ &mut compute_pass_resource.0.borrow_mut(),
+ compute_pipeline_resource.1,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_dispatch_workgroups(
+ state: &mut OpState,
+ #[smi] compute_pass_rid: ResourceId,
+ x: u32,
+ y: u32,
+ z: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch_workgroups(
+ &mut compute_pass_resource.0.borrow_mut(),
+ x,
+ y,
+ z,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_dispatch_workgroups_indirect(
+ state: &mut OpState,
+ #[smi] compute_pass_rid: ResourceId,
+ #[smi] indirect_buffer: ResourceId,
+ #[number] indirect_offset: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch_workgroups_indirect(
+ &mut compute_pass_resource.0.borrow_mut(),
+ buffer_resource.1,
+ indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_end(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[smi] compute_pass_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<super::command_encoder::WebGpuCommandEncoder>(
+ command_encoder_rid,
+ )?;
+ let command_encoder = command_encoder_resource.1;
+ let compute_pass_resource = state
+ .resource_table
+ .take::<WebGpuComputePass>(compute_pass_rid)?;
+ let compute_pass = &compute_pass_resource.0.borrow();
+ let instance = state.borrow::<super::Instance>();
+
+ gfx_ok!(command_encoder => instance.command_encoder_run_compute_pass(
+ command_encoder,
+ compute_pass
+ ))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_set_bind_group(
+ state: &mut OpState,
+ #[smi] compute_pass_rid: ResourceId,
+ index: u32,
+ #[smi] bind_group: ResourceId,
+ #[buffer] dynamic_offsets_data: &[u32],
+ #[number] dynamic_offsets_data_start: usize,
+ #[number] dynamic_offsets_data_length: usize,
+) -> Result<WebGpuResult, AnyError> {
+ let bind_group_resource =
+ state
+ .resource_table
+ .get::<super::binding::WebGpuBindGroup>(bind_group)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(compute_pass_rid)?;
+
+ let start = dynamic_offsets_data_start;
+ let len = dynamic_offsets_data_length;
+
+ // Assert that length and start are both in bounds
+ assert!(start <= dynamic_offsets_data.len());
+ assert!(len <= dynamic_offsets_data.len() - start);
+
+ let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_set_bind_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ index,
+ bind_group_resource.1,
+ dynamic_offsets_data.as_ptr(),
+ dynamic_offsets_data.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_push_debug_group(
+ state: &mut OpState,
+ #[smi] compute_pass_rid: ResourceId,
+ #[string] group_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(compute_pass_rid)?;
+
+ let label = std::ffi::CString::new(group_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_push_debug_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_pop_debug_group(
+ state: &mut OpState,
+ #[smi] compute_pass_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(compute_pass_rid)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_pop_debug_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pass_insert_debug_marker(
+ state: &mut OpState,
+ #[smi] compute_pass_rid: ResourceId,
+ #[string] marker_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGpuComputePass>(compute_pass_rid)?;
+
+ let label = std::ffi::CString::new(marker_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_insert_debug_marker(
+ &mut compute_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
diff --git a/ext/webgpu/error.rs b/ext/webgpu/error.rs
new file mode 100644
index 000000000..53ead09e5
--- /dev/null
+++ b/ext/webgpu/error.rs
@@ -0,0 +1,316 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+use deno_core::error::AnyError;
+use deno_core::ResourceId;
+use serde::Serialize;
+use std::convert::From;
+use std::error::Error;
+use std::fmt;
+use wgpu_core::binding_model::CreateBindGroupError;
+use wgpu_core::binding_model::CreateBindGroupLayoutError;
+use wgpu_core::binding_model::CreatePipelineLayoutError;
+use wgpu_core::binding_model::GetBindGroupLayoutError;
+use wgpu_core::command::ClearError;
+use wgpu_core::command::CommandEncoderError;
+use wgpu_core::command::ComputePassError;
+use wgpu_core::command::CopyError;
+use wgpu_core::command::CreateRenderBundleError;
+use wgpu_core::command::QueryError;
+use wgpu_core::command::RenderBundleError;
+use wgpu_core::command::RenderPassError;
+use wgpu_core::device::queue::QueueSubmitError;
+use wgpu_core::device::queue::QueueWriteError;
+use wgpu_core::device::DeviceError;
+use wgpu_core::pipeline::CreateComputePipelineError;
+use wgpu_core::pipeline::CreateRenderPipelineError;
+use wgpu_core::pipeline::CreateShaderModuleError;
+#[cfg(feature = "surface")]
+use wgpu_core::present::ConfigureSurfaceError;
+use wgpu_core::resource::BufferAccessError;
+use wgpu_core::resource::CreateBufferError;
+use wgpu_core::resource::CreateQuerySetError;
+use wgpu_core::resource::CreateSamplerError;
+use wgpu_core::resource::CreateTextureError;
+use wgpu_core::resource::CreateTextureViewError;
+
+fn fmt_err(err: &(dyn Error + 'static)) -> String {
+ let mut output = err.to_string();
+
+ let mut e = err.source();
+ while let Some(source) = e {
+ output.push_str(&format!(": {source}"));
+ e = source.source();
+ }
+
+ output
+}
+
+#[derive(Serialize)]
+pub struct WebGpuResult {
+ pub rid: Option<ResourceId>,
+ pub err: Option<WebGpuError>,
+}
+
+impl WebGpuResult {
+ pub fn rid(rid: ResourceId) -> Self {
+ Self {
+ rid: Some(rid),
+ err: None,
+ }
+ }
+
+ pub fn rid_err<T: Into<WebGpuError>>(
+ rid: ResourceId,
+ err: Option<T>,
+ ) -> Self {
+ Self {
+ rid: Some(rid),
+ err: err.map(Into::into),
+ }
+ }
+
+ pub fn maybe_err<T: Into<WebGpuError>>(err: Option<T>) -> Self {
+ Self {
+ rid: None,
+ err: err.map(Into::into),
+ }
+ }
+
+ pub fn empty() -> Self {
+ Self {
+ rid: None,
+ err: None,
+ }
+ }
+}
+
+#[derive(Serialize)]
+#[serde(tag = "type", content = "value")]
+#[serde(rename_all = "kebab-case")]
+pub enum WebGpuError {
+ Lost,
+ OutOfMemory,
+ Validation(String),
+}
+
+impl From<CreateBufferError> for WebGpuError {
+ fn from(err: CreateBufferError) -> Self {
+ match err {
+ CreateBufferError::Device(err) => err.into(),
+ CreateBufferError::AccessError(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<DeviceError> for WebGpuError {
+ fn from(err: DeviceError) -> Self {
+ match err {
+ DeviceError::Lost => WebGpuError::Lost,
+ DeviceError::OutOfMemory => WebGpuError::OutOfMemory,
+ DeviceError::ResourceCreationFailed
+ | DeviceError::Invalid
+ | DeviceError::WrongDevice => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<BufferAccessError> for WebGpuError {
+ fn from(err: BufferAccessError) -> Self {
+ match err {
+ BufferAccessError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<CreateBindGroupLayoutError> for WebGpuError {
+ fn from(err: CreateBindGroupLayoutError) -> Self {
+ match err {
+ CreateBindGroupLayoutError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<CreatePipelineLayoutError> for WebGpuError {
+ fn from(err: CreatePipelineLayoutError) -> Self {
+ match err {
+ CreatePipelineLayoutError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<CreateBindGroupError> for WebGpuError {
+ fn from(err: CreateBindGroupError) -> Self {
+ match err {
+ CreateBindGroupError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<RenderBundleError> for WebGpuError {
+ fn from(err: RenderBundleError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<CreateRenderBundleError> for WebGpuError {
+ fn from(err: CreateRenderBundleError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<CopyError> for WebGpuError {
+ fn from(err: CopyError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<CommandEncoderError> for WebGpuError {
+ fn from(err: CommandEncoderError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<QueryError> for WebGpuError {
+ fn from(err: QueryError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<ComputePassError> for WebGpuError {
+ fn from(err: ComputePassError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<CreateComputePipelineError> for WebGpuError {
+ fn from(err: CreateComputePipelineError) -> Self {
+ match err {
+ CreateComputePipelineError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<GetBindGroupLayoutError> for WebGpuError {
+ fn from(err: GetBindGroupLayoutError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<CreateRenderPipelineError> for WebGpuError {
+ fn from(err: CreateRenderPipelineError) -> Self {
+ match err {
+ CreateRenderPipelineError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<RenderPassError> for WebGpuError {
+ fn from(err: RenderPassError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<CreateSamplerError> for WebGpuError {
+ fn from(err: CreateSamplerError) -> Self {
+ match err {
+ CreateSamplerError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<CreateShaderModuleError> for WebGpuError {
+ fn from(err: CreateShaderModuleError) -> Self {
+ match err {
+ CreateShaderModuleError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<CreateTextureError> for WebGpuError {
+ fn from(err: CreateTextureError) -> Self {
+ match err {
+ CreateTextureError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<CreateTextureViewError> for WebGpuError {
+ fn from(err: CreateTextureViewError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+impl From<CreateQuerySetError> for WebGpuError {
+ fn from(err: CreateQuerySetError) -> Self {
+ match err {
+ CreateQuerySetError::Device(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<QueueSubmitError> for WebGpuError {
+ fn from(err: QueueSubmitError) -> Self {
+ match err {
+ QueueSubmitError::Queue(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<QueueWriteError> for WebGpuError {
+ fn from(err: QueueWriteError) -> Self {
+ match err {
+ QueueWriteError::Queue(err) => err.into(),
+ err => WebGpuError::Validation(fmt_err(&err)),
+ }
+ }
+}
+
+impl From<ClearError> for WebGpuError {
+ fn from(err: ClearError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+#[cfg(feature = "surface")]
+impl From<ConfigureSurfaceError> for WebGpuError {
+ fn from(err: ConfigureSurfaceError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
+#[derive(Debug)]
+pub struct DomExceptionOperationError {
+ pub msg: String,
+}
+
+impl DomExceptionOperationError {
+ pub fn new(msg: &str) -> Self {
+ DomExceptionOperationError {
+ msg: msg.to_string(),
+ }
+ }
+}
+
+impl fmt::Display for DomExceptionOperationError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.pad(&self.msg)
+ }
+}
+
+impl std::error::Error for DomExceptionOperationError {}
+
+pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
+ e.downcast_ref::<DomExceptionOperationError>()
+ .map(|_| "DOMExceptionOperationError")
+}
diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs
new file mode 100644
index 000000000..0d0ff633d
--- /dev/null
+++ b/ext/webgpu/lib.rs
@@ -0,0 +1,768 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+#![cfg(not(target_arch = "wasm32"))]
+#![warn(unsafe_op_in_unsafe_fn)]
+
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use serde::Serialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::HashSet;
+use std::rc::Rc;
+pub use wgpu_core;
+pub use wgpu_types;
+
+use error::DomExceptionOperationError;
+use error::WebGpuResult;
+
+pub const UNSTABLE_FEATURE_NAME: &str = "webgpu";
+
+#[macro_use]
+mod macros {
+ macro_rules! gfx_select {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
+ match $id.backend() {
+ #[cfg(any(
+ all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")),
+ feature = "vulkan-portability"
+ ))]
+ wgpu_types::Backend::Vulkan => $global.$method::<wgpu_core::api::Vulkan>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))]
+ wgpu_types::Backend::Metal => $global.$method::<wgpu_core::api::Metal>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
+ wgpu_types::Backend::Dx12 => $global.$method::<wgpu_core::api::Dx12>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
+ wgpu_types::Backend::Dx11 => $global.$method::<wgpu_core::api::Dx11>( $($param),* ),
+ #[cfg(any(
+ all(unix, not(target_os = "macos"), not(target_os = "ios")),
+ feature = "angle",
+ target_arch = "wasm32"
+ ))]
+ wgpu_types::Backend::Gl => $global.$method::<wgpu_core::api::Gles>( $($param),+ ),
+ other => panic!("Unexpected backend {:?}", other),
+ }
+ };
+ }
+
+ macro_rules! gfx_put {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* ) => $state:expr, $rc:expr) => {{
+ let (val, maybe_err) = gfx_select!($id => $global.$method($($param),*));
+ let rid = $state.resource_table.add($rc($global.clone(), val));
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+ }};
+ }
+
+ macro_rules! gfx_ok {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {{
+ let maybe_err = gfx_select!($id => $global.$method($($param),*)).err();
+ Ok(WebGpuResult::maybe_err(maybe_err))
+ }};
+ }
+}
+
+pub mod binding;
+pub mod buffer;
+pub mod bundle;
+pub mod command_encoder;
+pub mod compute_pass;
+pub mod error;
+pub mod pipeline;
+pub mod queue;
+pub mod render_pass;
+pub mod sampler;
+pub mod shader;
+#[cfg(feature = "surface")]
+pub mod surface;
+pub mod texture;
+
+pub type Instance = std::sync::Arc<
+ wgpu_core::global::Global<wgpu_core::identity::IdentityManagerFactory>,
+>;
+
+struct WebGpuAdapter(Instance, wgpu_core::id::AdapterId);
+impl Resource for WebGpuAdapter {
+ fn name(&self) -> Cow<str> {
+ "webGPUAdapter".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.adapter_drop(self.1));
+ }
+}
+
+struct WebGpuDevice(Instance, wgpu_core::id::DeviceId);
+impl Resource for WebGpuDevice {
+ fn name(&self) -> Cow<str> {
+ "webGPUDevice".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.device_drop(self.1));
+ }
+}
+
+struct WebGpuQuerySet(Instance, wgpu_core::id::QuerySetId);
+impl Resource for WebGpuQuerySet {
+ fn name(&self) -> Cow<str> {
+ "webGPUQuerySet".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.query_set_drop(self.1));
+ }
+}
+
+deno_core::extension!(
+ deno_webgpu,
+ deps = [deno_webidl, deno_web],
+ ops = [
+ // Request device/adapter
+ op_webgpu_request_adapter,
+ op_webgpu_request_device,
+ op_webgpu_request_adapter_info,
+ // Query Set
+ op_webgpu_create_query_set,
+ // buffer
+ buffer::op_webgpu_create_buffer,
+ buffer::op_webgpu_buffer_get_mapped_range,
+ buffer::op_webgpu_buffer_unmap,
+ // buffer async
+ buffer::op_webgpu_buffer_get_map_async,
+ // remaining sync ops
+
+ // texture
+ texture::op_webgpu_create_texture,
+ texture::op_webgpu_create_texture_view,
+ // sampler
+ sampler::op_webgpu_create_sampler,
+ // binding
+ binding::op_webgpu_create_bind_group_layout,
+ binding::op_webgpu_create_pipeline_layout,
+ binding::op_webgpu_create_bind_group,
+ // pipeline
+ pipeline::op_webgpu_create_compute_pipeline,
+ pipeline::op_webgpu_compute_pipeline_get_bind_group_layout,
+ pipeline::op_webgpu_create_render_pipeline,
+ pipeline::op_webgpu_render_pipeline_get_bind_group_layout,
+ // command_encoder
+ command_encoder::op_webgpu_create_command_encoder,
+ command_encoder::op_webgpu_command_encoder_begin_render_pass,
+ command_encoder::op_webgpu_command_encoder_begin_compute_pass,
+ command_encoder::op_webgpu_command_encoder_copy_buffer_to_buffer,
+ command_encoder::op_webgpu_command_encoder_copy_buffer_to_texture,
+ command_encoder::op_webgpu_command_encoder_copy_texture_to_buffer,
+ command_encoder::op_webgpu_command_encoder_copy_texture_to_texture,
+ command_encoder::op_webgpu_command_encoder_clear_buffer,
+ command_encoder::op_webgpu_command_encoder_push_debug_group,
+ command_encoder::op_webgpu_command_encoder_pop_debug_group,
+ command_encoder::op_webgpu_command_encoder_insert_debug_marker,
+ command_encoder::op_webgpu_command_encoder_write_timestamp,
+ command_encoder::op_webgpu_command_encoder_resolve_query_set,
+ command_encoder::op_webgpu_command_encoder_finish,
+ render_pass::op_webgpu_render_pass_set_viewport,
+ render_pass::op_webgpu_render_pass_set_scissor_rect,
+ render_pass::op_webgpu_render_pass_set_blend_constant,
+ render_pass::op_webgpu_render_pass_set_stencil_reference,
+ render_pass::op_webgpu_render_pass_begin_occlusion_query,
+ render_pass::op_webgpu_render_pass_end_occlusion_query,
+ render_pass::op_webgpu_render_pass_execute_bundles,
+ render_pass::op_webgpu_render_pass_end,
+ render_pass::op_webgpu_render_pass_set_bind_group,
+ render_pass::op_webgpu_render_pass_push_debug_group,
+ render_pass::op_webgpu_render_pass_pop_debug_group,
+ render_pass::op_webgpu_render_pass_insert_debug_marker,
+ render_pass::op_webgpu_render_pass_set_pipeline,
+ render_pass::op_webgpu_render_pass_set_index_buffer,
+ render_pass::op_webgpu_render_pass_set_vertex_buffer,
+ render_pass::op_webgpu_render_pass_draw,
+ render_pass::op_webgpu_render_pass_draw_indexed,
+ render_pass::op_webgpu_render_pass_draw_indirect,
+ render_pass::op_webgpu_render_pass_draw_indexed_indirect,
+ compute_pass::op_webgpu_compute_pass_set_pipeline,
+ compute_pass::op_webgpu_compute_pass_dispatch_workgroups,
+ compute_pass::op_webgpu_compute_pass_dispatch_workgroups_indirect,
+ compute_pass::op_webgpu_compute_pass_end,
+ compute_pass::op_webgpu_compute_pass_set_bind_group,
+ compute_pass::op_webgpu_compute_pass_push_debug_group,
+ compute_pass::op_webgpu_compute_pass_pop_debug_group,
+ compute_pass::op_webgpu_compute_pass_insert_debug_marker,
+ // bundle
+ bundle::op_webgpu_create_render_bundle_encoder,
+ bundle::op_webgpu_render_bundle_encoder_finish,
+ bundle::op_webgpu_render_bundle_encoder_set_bind_group,
+ bundle::op_webgpu_render_bundle_encoder_push_debug_group,
+ bundle::op_webgpu_render_bundle_encoder_pop_debug_group,
+ bundle::op_webgpu_render_bundle_encoder_insert_debug_marker,
+ bundle::op_webgpu_render_bundle_encoder_set_pipeline,
+ bundle::op_webgpu_render_bundle_encoder_set_index_buffer,
+ bundle::op_webgpu_render_bundle_encoder_set_vertex_buffer,
+ bundle::op_webgpu_render_bundle_encoder_draw,
+ bundle::op_webgpu_render_bundle_encoder_draw_indexed,
+ bundle::op_webgpu_render_bundle_encoder_draw_indirect,
+ // queue
+ queue::op_webgpu_queue_submit,
+ queue::op_webgpu_write_buffer,
+ queue::op_webgpu_write_texture,
+ // shader
+ shader::op_webgpu_create_shader_module,
+ ],
+ lazy_loaded_esm = ["01_webgpu.js"],
+);
+
+fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> {
+ let mut return_features: Vec<&'static str> = vec![];
+
+ // api
+ if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) {
+ return_features.push("depth-clip-control");
+ }
+ if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
+ return_features.push("timestamp-query");
+ }
+ if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) {
+ return_features.push("indirect-first-instance");
+ }
+ // shader
+ if features.contains(wgpu_types::Features::SHADER_F16) {
+ return_features.push("shader-f16");
+ }
+ // texture formats
+ if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) {
+ return_features.push("depth32float-stencil8");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
+ return_features.push("texture-compression-bc");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
+ return_features.push("texture-compression-etc2");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC) {
+ return_features.push("texture-compression-astc");
+ }
+ if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) {
+ return_features.push("rg11b10ufloat-renderable");
+ }
+ if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) {
+ return_features.push("bgra8unorm-storage");
+ }
+
+ // extended from spec
+
+ // texture formats
+ if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) {
+ return_features.push("texture-format-16-bit-norm");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
+ return_features.push("texture-compression-astc-hdr");
+ }
+ if features
+ .contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES)
+ {
+ return_features.push("texture-adapter-specific-format-features");
+ }
+ // api
+ if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
+ return_features.push("pipeline-statistics-query");
+ }
+ if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) {
+ return_features.push("timestamp-query-inside-passes");
+ }
+ if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
+ return_features.push("mappable-primary-buffers");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) {
+ return_features.push("texture-binding-array");
+ }
+ if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) {
+ return_features.push("buffer-binding-array");
+ }
+ if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
+ return_features.push("storage-resource-binding-array");
+ }
+ if features.contains(
+ wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
+ ) {
+ return_features.push("sampled-texture-and-storage-buffer-array-non-uniform-indexing");
+ }
+ if features.contains(
+ wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
+ ) {
+ return_features.push("uniform-buffer-and-storage-texture-array-non-uniform-indexing");
+ }
+ if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
+ return_features.push("partially-bound-binding-array");
+ }
+ if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
+ return_features.push("multi-draw-indirect");
+ }
+ if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
+ return_features.push("multi-draw-indirect-count");
+ }
+ if features.contains(wgpu_types::Features::PUSH_CONSTANTS) {
+ return_features.push("push-constants");
+ }
+ if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) {
+ return_features.push("address-mode-clamp-to-zero");
+ }
+ if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
+ return_features.push("address-mode-clamp-to-border");
+ }
+ if features.contains(wgpu_types::Features::POLYGON_MODE_LINE) {
+ return_features.push("polygon-mode-line");
+ }
+ if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) {
+ return_features.push("polygon-mode-point");
+ }
+ if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) {
+ return_features.push("conservative-rasterization");
+ }
+ if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) {
+ return_features.push("vertex-writable-storage");
+ }
+ if features.contains(wgpu_types::Features::CLEAR_TEXTURE) {
+ return_features.push("clear-texture");
+ }
+ if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) {
+ return_features.push("spirv-shader-passthrough");
+ }
+ if features.contains(wgpu_types::Features::MULTIVIEW) {
+ return_features.push("multiview");
+ }
+ if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
+ return_features.push("vertex-attribute-64-bit");
+ }
+ // shader
+ if features.contains(wgpu_types::Features::SHADER_F64) {
+ return_features.push("shader-f64");
+ }
+ if features.contains(wgpu_types::Features::SHADER_I16) {
+ return_features.push("shader-i16");
+ }
+ if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) {
+ return_features.push("shader-primitive-index");
+ }
+ if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) {
+ return_features.push("shader-early-depth-test");
+ }
+ if features.contains(wgpu_types::Features::SHADER_UNUSED_VERTEX_OUTPUT) {
+ return_features.push("shader-unused-vertex-output");
+ }
+
+ return_features
+}
+
+#[derive(Serialize)]
+#[serde(untagged)]
+pub enum GpuAdapterDeviceOrErr {
+ Error { err: String },
+ Features(GpuAdapterDevice),
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuAdapterDevice {
+ rid: ResourceId,
+ limits: wgpu_types::Limits,
+ features: Vec<&'static str>,
+ is_software: bool,
+}
+
+#[op2(async)]
+#[serde]
+pub async fn op_webgpu_request_adapter(
+ state: Rc<RefCell<OpState>>,
+ #[serde] power_preference: Option<wgpu_types::PowerPreference>,
+ force_fallback_adapter: bool,
+) -> Result<GpuAdapterDeviceOrErr, AnyError> {
+ let mut state = state.borrow_mut();
+
+ // TODO(bartlomieju): replace with `state.feature_checker.check_or_exit`
+ // once we phase out `check_or_exit_with_legacy_fallback`
+ state.feature_checker.check_or_exit_with_legacy_fallback(
+ UNSTABLE_FEATURE_NAME,
+ "navigator.gpu.requestAdapter",
+ );
+
+ let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else(
+ |_| wgpu_types::Backends::all(),
+ |s| wgpu_core::instance::parse_backends_from_comma_list(&s),
+ );
+ let instance = if let Some(instance) = state.try_borrow::<Instance>() {
+ instance
+ } else {
+ state.put(std::sync::Arc::new(wgpu_core::global::Global::new(
+ "webgpu",
+ wgpu_core::identity::IdentityManagerFactory,
+ wgpu_types::InstanceDescriptor {
+ backends,
+ flags: wgpu_types::InstanceFlags::from_build_config(),
+ dx12_shader_compiler: wgpu_types::Dx12Compiler::Fxc,
+ gles_minor_version: wgpu_types::Gles3MinorVersion::default(),
+ },
+ )));
+ state.borrow::<Instance>()
+ };
+
+ let descriptor = wgpu_core::instance::RequestAdapterOptions {
+ power_preference: power_preference.unwrap_or_default(),
+ force_fallback_adapter,
+ compatible_surface: None, // windowless
+ };
+ let res = instance.request_adapter(
+ &descriptor,
+ wgpu_core::instance::AdapterInputs::Mask(backends, |_| ()),
+ );
+
+ let adapter = match res {
+ Ok(adapter) => adapter,
+ Err(err) => {
+ return Ok(GpuAdapterDeviceOrErr::Error {
+ err: err.to_string(),
+ })
+ }
+ };
+ let adapter_features =
+ gfx_select!(adapter => instance.adapter_features(adapter))?;
+ let features = deserialize_features(&adapter_features);
+ let adapter_limits =
+ gfx_select!(adapter => instance.adapter_limits(adapter))?;
+
+ let instance = instance.clone();
+
+ let rid = state.resource_table.add(WebGpuAdapter(instance, adapter));
+
+ Ok(GpuAdapterDeviceOrErr::Features(GpuAdapterDevice {
+ rid,
+ features,
+ limits: adapter_limits,
+ is_software: false,
+ }))
+}
+
+#[derive(Deserialize)]
+pub struct GpuRequiredFeatures(HashSet<String>);
+
+impl From<GpuRequiredFeatures> for wgpu_types::Features {
+ fn from(required_features: GpuRequiredFeatures) -> wgpu_types::Features {
+ let mut features: wgpu_types::Features = wgpu_types::Features::empty();
+ // api
+ features.set(
+ wgpu_types::Features::DEPTH_CLIP_CONTROL,
+ required_features.0.contains("depth-clip-control"),
+ );
+ features.set(
+ wgpu_types::Features::TIMESTAMP_QUERY,
+ required_features.0.contains("timestamp-query"),
+ );
+ features.set(
+ wgpu_types::Features::INDIRECT_FIRST_INSTANCE,
+ required_features.0.contains("indirect-first-instance"),
+ );
+ // shader
+ features.set(
+ wgpu_types::Features::SHADER_F16,
+ required_features.0.contains("shader-f16"),
+ );
+ // texture formats
+ features.set(
+ wgpu_types::Features::DEPTH32FLOAT_STENCIL8,
+ required_features.0.contains("depth32float-stencil8"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_COMPRESSION_BC,
+ required_features.0.contains("texture-compression-bc"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_COMPRESSION_ETC2,
+ required_features.0.contains("texture-compression-etc2"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_COMPRESSION_ASTC,
+ required_features.0.contains("texture-compression-astc"),
+ );
+ features.set(
+ wgpu_types::Features::RG11B10UFLOAT_RENDERABLE,
+ required_features.0.contains("rg11b10ufloat-renderable"),
+ );
+ features.set(
+ wgpu_types::Features::BGRA8UNORM_STORAGE,
+ required_features.0.contains("bgra8unorm-storage"),
+ );
+
+ // extended from spec
+
+ // texture formats
+ features.set(
+ wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM,
+ required_features.0.contains("texture-format-16-bit-norm"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR,
+ required_features.0.contains("texture-compression-astc-hdr"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
+ required_features
+ .0
+ .contains("texture-adapter-specific-format-features"),
+ );
+ // api
+ features.set(
+ wgpu_types::Features::PIPELINE_STATISTICS_QUERY,
+ required_features.0.contains("pipeline-statistics-query"),
+ );
+ features.set(
+ wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
+ required_features
+ .0
+ .contains("timestamp-query-inside-passes"),
+ );
+ features.set(
+ wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS,
+ required_features.0.contains("mappable-primary-buffers"),
+ );
+ features.set(
+ wgpu_types::Features::TEXTURE_BINDING_ARRAY,
+ required_features.0.contains("texture-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::BUFFER_BINDING_ARRAY,
+ required_features.0.contains("buffer-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY,
+ required_features
+ .0
+ .contains("storage-resource-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
+ required_features
+ .0
+ .contains("sampled-texture-and-storage-buffer-array-non-uniform-indexing"),
+ );
+ features.set(
+ wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
+ required_features
+ .0
+ .contains("uniform-buffer-and-storage-texture-array-non-uniform-indexing"),
+ );
+ features.set(
+ wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY,
+ required_features
+ .0
+ .contains("partially-bound-binding-array"),
+ );
+ features.set(
+ wgpu_types::Features::MULTI_DRAW_INDIRECT,
+ required_features.0.contains("multi-draw-indirect"),
+ );
+ features.set(
+ wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT,
+ required_features.0.contains("multi-draw-indirect-count"),
+ );
+ features.set(
+ wgpu_types::Features::PUSH_CONSTANTS,
+ required_features.0.contains("push-constants"),
+ );
+ features.set(
+ wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
+ required_features.0.contains("address-mode-clamp-to-zero"),
+ );
+ features.set(
+ wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
+ required_features.0.contains("address-mode-clamp-to-border"),
+ );
+ features.set(
+ wgpu_types::Features::POLYGON_MODE_LINE,
+ required_features.0.contains("polygon-mode-line"),
+ );
+ features.set(
+ wgpu_types::Features::POLYGON_MODE_POINT,
+ required_features.0.contains("polygon-mode-point"),
+ );
+ features.set(
+ wgpu_types::Features::CONSERVATIVE_RASTERIZATION,
+ required_features.0.contains("conservative-rasterization"),
+ );
+ features.set(
+ wgpu_types::Features::VERTEX_WRITABLE_STORAGE,
+ required_features.0.contains("vertex-writable-storage"),
+ );
+ features.set(
+ wgpu_types::Features::CLEAR_TEXTURE,
+ required_features.0.contains("clear-texture"),
+ );
+ features.set(
+ wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH,
+ required_features.0.contains("spirv-shader-passthrough"),
+ );
+ features.set(
+ wgpu_types::Features::MULTIVIEW,
+ required_features.0.contains("multiview"),
+ );
+ features.set(
+ wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT,
+ required_features.0.contains("vertex-attribute-64-bit"),
+ );
+ // shader
+ features.set(
+ wgpu_types::Features::SHADER_F64,
+ required_features.0.contains("shader-f64"),
+ );
+ features.set(
+ wgpu_types::Features::SHADER_I16,
+ required_features.0.contains("shader-i16"),
+ );
+ features.set(
+ wgpu_types::Features::SHADER_PRIMITIVE_INDEX,
+ required_features.0.contains("shader-primitive-index"),
+ );
+ features.set(
+ wgpu_types::Features::SHADER_EARLY_DEPTH_TEST,
+ required_features.0.contains("shader-early-depth-test"),
+ );
+ features.set(
+ wgpu_types::Features::SHADER_UNUSED_VERTEX_OUTPUT,
+ required_features.0.contains("shader-unused-vertex-output"),
+ );
+
+ features
+ }
+}
+
+#[op2(async)]
+#[serde]
+pub async fn op_webgpu_request_device(
+ state: Rc<RefCell<OpState>>,
+ #[smi] adapter_rid: ResourceId,
+ #[string] label: String,
+ #[serde] required_features: GpuRequiredFeatures,
+ #[serde] required_limits: Option<wgpu_types::Limits>,
+) -> Result<GpuAdapterDevice, AnyError> {
+ let mut state = state.borrow_mut();
+ let adapter_resource =
+ state.resource_table.get::<WebGpuAdapter>(adapter_rid)?;
+ let adapter = adapter_resource.1;
+ let instance = state.borrow::<Instance>();
+
+ let descriptor = wgpu_types::DeviceDescriptor {
+ label: Some(Cow::Owned(label)),
+ features: required_features.into(),
+ limits: required_limits.unwrap_or_default(),
+ };
+
+ let (device, maybe_err) = gfx_select!(adapter => instance.adapter_request_device(
+ adapter,
+ &descriptor,
+ std::env::var("DENO_WEBGPU_TRACE").ok().as_ref().map(std::path::Path::new),
+ ()
+ ));
+ if let Some(err) = maybe_err {
+ return Err(DomExceptionOperationError::new(&err.to_string()).into());
+ }
+
+ let device_features =
+ gfx_select!(device => instance.device_features(device))?;
+ let features = deserialize_features(&device_features);
+ let limits = gfx_select!(device => instance.device_limits(device))?;
+
+ let instance = instance.clone();
+ let rid = state.resource_table.add(WebGpuDevice(instance, device));
+
+ Ok(GpuAdapterDevice {
+ rid,
+ features,
+ limits,
+ // TODO(lucacasonato): report correctly from wgpu
+ is_software: false,
+ })
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPUAdapterInfo {
+ vendor: String,
+ architecture: String,
+ device: String,
+ description: String,
+}
+
+#[op2(async)]
+#[serde]
+pub async fn op_webgpu_request_adapter_info(
+ state: Rc<RefCell<OpState>>,
+ #[smi] adapter_rid: ResourceId,
+) -> Result<GPUAdapterInfo, AnyError> {
+ let state = state.borrow_mut();
+ let adapter_resource =
+ state.resource_table.get::<WebGpuAdapter>(adapter_rid)?;
+ let adapter = adapter_resource.1;
+ let instance = state.borrow::<Instance>();
+
+ let info = gfx_select!(adapter => instance.adapter_get_info(adapter))?;
+
+ Ok(GPUAdapterInfo {
+ vendor: info.vendor.to_string(),
+ architecture: String::new(), // TODO(#2170)
+ device: info.device.to_string(),
+ description: info.name,
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateQuerySetArgs {
+ device_rid: ResourceId,
+ label: String,
+ #[serde(flatten)]
+ r#type: GpuQueryType,
+ count: u32,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case", tag = "type")]
+enum GpuQueryType {
+ Occlusion,
+ Timestamp,
+}
+
+impl From<GpuQueryType> for wgpu_types::QueryType {
+ fn from(query_type: GpuQueryType) -> Self {
+ match query_type {
+ GpuQueryType::Occlusion => wgpu_types::QueryType::Occlusion,
+ GpuQueryType::Timestamp => wgpu_types::QueryType::Timestamp,
+ }
+ }
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_query_set(
+ state: &mut OpState,
+ #[serde] args: CreateQuerySetArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let device_resource =
+ state.resource_table.get::<WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.1;
+ let instance = state.borrow::<Instance>();
+
+ let descriptor = wgpu_types::QuerySetDescriptor {
+ label: Some(Cow::Owned(args.label)),
+ ty: args.r#type.into(),
+ count: args.count,
+ };
+
+ gfx_put!(device => instance.device_create_query_set(
+ device,
+ &descriptor,
+ ()
+ ) => state, WebGpuQuerySet)
+}
diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs
new file mode 100644
index 000000000..9fa975c21
--- /dev/null
+++ b/ext/webgpu/pipeline.rs
@@ -0,0 +1,453 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use serde::Serialize;
+use std::borrow::Cow;
+use std::rc::Rc;
+
+use super::error::WebGpuError;
+use super::error::WebGpuResult;
+
+const MAX_BIND_GROUPS: usize = 8;
+
+pub(crate) struct WebGpuPipelineLayout(
+ pub(crate) crate::Instance,
+ pub(crate) wgpu_core::id::PipelineLayoutId,
+);
+impl Resource for WebGpuPipelineLayout {
+ fn name(&self) -> Cow<str> {
+ "webGPUPipelineLayout".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.pipeline_layout_drop(self.1));
+ }
+}
+
+pub(crate) struct WebGpuComputePipeline(
+ pub(crate) crate::Instance,
+ pub(crate) wgpu_core::id::ComputePipelineId,
+);
+impl Resource for WebGpuComputePipeline {
+ fn name(&self) -> Cow<str> {
+ "webGPUComputePipeline".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.compute_pipeline_drop(self.1));
+ }
+}
+
+pub(crate) struct WebGpuRenderPipeline(
+ pub(crate) crate::Instance,
+ pub(crate) wgpu_core::id::RenderPipelineId,
+);
+impl Resource for WebGpuRenderPipeline {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderPipeline".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.render_pipeline_drop(self.1));
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub enum GPUAutoLayoutMode {
+ Auto,
+}
+
+#[derive(Deserialize)]
+#[serde(untagged)]
+pub enum GPUPipelineLayoutOrGPUAutoLayoutMode {
+ Layout(ResourceId),
+ Auto(GPUAutoLayoutMode),
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuProgrammableStage {
+ module: ResourceId,
+ entry_point: String,
+ // constants: HashMap<String, GPUPipelineConstantValue>
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_compute_pipeline(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[serde] layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
+ #[serde] compute: GpuProgrammableStage,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+
+ let pipeline_layout = match layout {
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => {
+ let id = state.resource_table.get::<WebGpuPipelineLayout>(rid)?;
+ Some(id.1)
+ }
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None,
+ };
+
+ let compute_shader_module_resource =
+ state
+ .resource_table
+ .get::<super::shader::WebGpuShaderModule>(compute.module)?;
+
+ let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
+ label: Some(label),
+ layout: pipeline_layout,
+ stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
+ module: compute_shader_module_resource.1,
+ entry_point: Cow::from(compute.entry_point),
+ // TODO(lucacasonato): support args.compute.constants
+ },
+ };
+ let implicit_pipelines = match layout {
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(_) => None,
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => {
+ Some(wgpu_core::device::ImplicitPipelineIds {
+ root_id: (),
+ group_ids: &[(); MAX_BIND_GROUPS],
+ })
+ }
+ };
+
+ let (compute_pipeline, maybe_err) = gfx_select!(device => instance.device_create_compute_pipeline(
+ device,
+ &descriptor,
+ (),
+ implicit_pipelines
+ ));
+
+ let rid = state
+ .resource_table
+ .add(WebGpuComputePipeline(instance.clone(), compute_pipeline));
+
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+}
+
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct PipelineLayout {
+ rid: ResourceId,
+ label: String,
+ err: Option<WebGpuError>,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_compute_pipeline_get_bind_group_layout(
+ state: &mut OpState,
+ #[smi] compute_pipeline_rid: ResourceId,
+ index: u32,
+) -> Result<PipelineLayout, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let compute_pipeline_resource = state
+ .resource_table
+ .get::<WebGpuComputePipeline>(compute_pipeline_rid)?;
+ let compute_pipeline = compute_pipeline_resource.1;
+
+ let (bind_group_layout, maybe_err) = gfx_select!(compute_pipeline => instance.compute_pipeline_get_bind_group_layout(compute_pipeline, index, ()));
+
+ let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
+
+ let rid = state
+ .resource_table
+ .add(super::binding::WebGpuBindGroupLayout(
+ instance.clone(),
+ bind_group_layout,
+ ));
+
+ Ok(PipelineLayout {
+ rid,
+ label,
+ err: maybe_err.map(WebGpuError::from),
+ })
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum GpuCullMode {
+ None,
+ Front,
+ Back,
+}
+
+impl From<GpuCullMode> for Option<wgpu_types::Face> {
+ fn from(value: GpuCullMode) -> Option<wgpu_types::Face> {
+ match value {
+ GpuCullMode::None => None,
+ GpuCullMode::Front => Some(wgpu_types::Face::Front),
+ GpuCullMode::Back => Some(wgpu_types::Face::Back),
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuPrimitiveState {
+ topology: wgpu_types::PrimitiveTopology,
+ strip_index_format: Option<wgpu_types::IndexFormat>,
+ front_face: wgpu_types::FrontFace,
+ cull_mode: GpuCullMode,
+ unclipped_depth: bool,
+}
+
+impl From<GpuPrimitiveState> for wgpu_types::PrimitiveState {
+ fn from(value: GpuPrimitiveState) -> wgpu_types::PrimitiveState {
+ wgpu_types::PrimitiveState {
+ topology: value.topology,
+ strip_index_format: value.strip_index_format,
+ front_face: value.front_face,
+ cull_mode: value.cull_mode.into(),
+ unclipped_depth: value.unclipped_depth,
+ polygon_mode: Default::default(), // native-only
+ conservative: false, // native-only
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuDepthStencilState {
+ format: wgpu_types::TextureFormat,
+ depth_write_enabled: bool,
+ depth_compare: wgpu_types::CompareFunction,
+ stencil_front: wgpu_types::StencilFaceState,
+ stencil_back: wgpu_types::StencilFaceState,
+ stencil_read_mask: u32,
+ stencil_write_mask: u32,
+ depth_bias: i32,
+ depth_bias_slope_scale: f32,
+ depth_bias_clamp: f32,
+}
+
+impl From<GpuDepthStencilState> for wgpu_types::DepthStencilState {
+ fn from(state: GpuDepthStencilState) -> wgpu_types::DepthStencilState {
+ wgpu_types::DepthStencilState {
+ format: state.format,
+ depth_write_enabled: state.depth_write_enabled,
+ depth_compare: state.depth_compare,
+ stencil: wgpu_types::StencilState {
+ front: state.stencil_front,
+ back: state.stencil_back,
+ read_mask: state.stencil_read_mask,
+ write_mask: state.stencil_write_mask,
+ },
+ bias: wgpu_types::DepthBiasState {
+ constant: state.depth_bias,
+ slope_scale: state.depth_bias_slope_scale,
+ clamp: state.depth_bias_clamp,
+ },
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuVertexBufferLayout {
+ array_stride: u64,
+ step_mode: wgpu_types::VertexStepMode,
+ attributes: Vec<wgpu_types::VertexAttribute>,
+}
+
+impl<'a> From<GpuVertexBufferLayout>
+ for wgpu_core::pipeline::VertexBufferLayout<'a>
+{
+ fn from(
+ layout: GpuVertexBufferLayout,
+ ) -> wgpu_core::pipeline::VertexBufferLayout<'a> {
+ wgpu_core::pipeline::VertexBufferLayout {
+ array_stride: layout.array_stride,
+ step_mode: layout.step_mode,
+ attributes: Cow::Owned(layout.attributes),
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuVertexState {
+ module: ResourceId,
+ entry_point: String,
+ buffers: Vec<Option<GpuVertexBufferLayout>>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuMultisampleState {
+ count: u32,
+ mask: u64,
+ alpha_to_coverage_enabled: bool,
+}
+
+impl From<GpuMultisampleState> for wgpu_types::MultisampleState {
+ fn from(gms: GpuMultisampleState) -> wgpu_types::MultisampleState {
+ wgpu_types::MultisampleState {
+ count: gms.count,
+ mask: gms.mask,
+ alpha_to_coverage_enabled: gms.alpha_to_coverage_enabled,
+ }
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GpuFragmentState {
+ targets: Vec<Option<wgpu_types::ColorTargetState>>,
+ module: u32,
+ entry_point: String,
+ // TODO(lucacasonato): constants
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateRenderPipelineArgs {
+ device_rid: ResourceId,
+ label: String,
+ layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
+ vertex: GpuVertexState,
+ primitive: GpuPrimitiveState,
+ depth_stencil: Option<GpuDepthStencilState>,
+ multisample: wgpu_types::MultisampleState,
+ fragment: Option<GpuFragmentState>,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_render_pipeline(
+ state: &mut OpState,
+ #[serde] args: CreateRenderPipelineArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.1;
+
+ let layout = match args.layout {
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => {
+ let pipeline_layout_resource =
+ state.resource_table.get::<WebGpuPipelineLayout>(rid)?;
+ Some(pipeline_layout_resource.1)
+ }
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None,
+ };
+
+ let vertex_shader_module_resource =
+ state
+ .resource_table
+ .get::<super::shader::WebGpuShaderModule>(args.vertex.module)?;
+
+ let fragment = if let Some(fragment) = args.fragment {
+ let fragment_shader_module_resource =
+ state
+ .resource_table
+ .get::<super::shader::WebGpuShaderModule>(fragment.module)?;
+
+ Some(wgpu_core::pipeline::FragmentState {
+ stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
+ module: fragment_shader_module_resource.1,
+ entry_point: Cow::from(fragment.entry_point),
+ },
+ targets: Cow::from(fragment.targets),
+ })
+ } else {
+ None
+ };
+
+ let vertex_buffers = args
+ .vertex
+ .buffers
+ .into_iter()
+ .flatten()
+ .map(Into::into)
+ .collect();
+
+ let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
+ label: Some(Cow::Owned(args.label)),
+ layout,
+ vertex: wgpu_core::pipeline::VertexState {
+ stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
+ module: vertex_shader_module_resource.1,
+ entry_point: Cow::Owned(args.vertex.entry_point),
+ },
+ buffers: Cow::Owned(vertex_buffers),
+ },
+ primitive: args.primitive.into(),
+ depth_stencil: args.depth_stencil.map(Into::into),
+ multisample: args.multisample,
+ fragment,
+ multiview: None,
+ };
+
+ let implicit_pipelines = match args.layout {
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(_) => None,
+ GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => {
+ Some(wgpu_core::device::ImplicitPipelineIds {
+ root_id: (),
+ group_ids: &[(); MAX_BIND_GROUPS],
+ })
+ }
+ };
+
+ let (render_pipeline, maybe_err) = gfx_select!(device => instance.device_create_render_pipeline(
+ device,
+ &descriptor,
+ (),
+ implicit_pipelines
+ ));
+
+ let rid = state
+ .resource_table
+ .add(WebGpuRenderPipeline(instance.clone(), render_pipeline));
+
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pipeline_get_bind_group_layout(
+ state: &mut OpState,
+ #[smi] render_pipeline_rid: ResourceId,
+ index: u32,
+) -> Result<PipelineLayout, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let render_pipeline_resource = state
+ .resource_table
+ .get::<WebGpuRenderPipeline>(render_pipeline_rid)?;
+ let render_pipeline = render_pipeline_resource.1;
+
+ let (bind_group_layout, maybe_err) = gfx_select!(render_pipeline => instance.render_pipeline_get_bind_group_layout(render_pipeline, index, ()));
+
+ let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
+
+ let rid = state
+ .resource_table
+ .add(super::binding::WebGpuBindGroupLayout(
+ instance.clone(),
+ bind_group_layout,
+ ));
+
+ Ok(PipelineLayout {
+ rid,
+ label,
+ err: maybe_err.map(WebGpuError::from),
+ })
+}
diff --git a/ext/webgpu/queue.rs b/ext/webgpu/queue.rs
new file mode 100644
index 000000000..9e878c7f2
--- /dev/null
+++ b/ext/webgpu/queue.rs
@@ -0,0 +1,131 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use crate::command_encoder::WebGpuCommandBuffer;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+
+use super::error::WebGpuResult;
+
+type WebGpuQueue = super::WebGpuDevice;
+
+#[op2]
+#[serde]
+pub fn op_webgpu_queue_submit(
+ state: &mut OpState,
+ #[smi] queue_rid: ResourceId,
+ #[serde] command_buffers: Vec<ResourceId>,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
+ let queue = queue_resource.1;
+
+ let ids = command_buffers
+ .iter()
+ .map(|rid| {
+ let buffer_resource =
+ state.resource_table.get::<WebGpuCommandBuffer>(*rid)?;
+ let mut id = buffer_resource.1.borrow_mut();
+ Ok(id.take().unwrap())
+ })
+ .collect::<Result<Vec<_>, AnyError>>()?;
+
+ let maybe_err =
+ gfx_select!(queue => instance.queue_submit(queue, &ids)).err();
+
+ for rid in command_buffers {
+ let resource = state.resource_table.take::<WebGpuCommandBuffer>(rid)?;
+ resource.close();
+ }
+
+ Ok(WebGpuResult::maybe_err(maybe_err))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GpuImageDataLayout {
+ offset: u64,
+ bytes_per_row: Option<u32>,
+ rows_per_image: Option<u32>,
+}
+
+impl From<GpuImageDataLayout> for wgpu_types::ImageDataLayout {
+ fn from(layout: GpuImageDataLayout) -> Self {
+ wgpu_types::ImageDataLayout {
+ offset: layout.offset,
+ bytes_per_row: layout.bytes_per_row,
+ rows_per_image: layout.rows_per_image,
+ }
+ }
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_write_buffer(
+ state: &mut OpState,
+ #[smi] queue_rid: ResourceId,
+ #[smi] buffer: ResourceId,
+ #[number] buffer_offset: u64,
+ #[number] data_offset: usize,
+ #[number] size: Option<usize>,
+ #[buffer] buf: &[u8],
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(buffer)?;
+ let buffer = buffer_resource.1;
+ let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
+ let queue = queue_resource.1;
+
+ let data = match size {
+ Some(size) => &buf[data_offset..(data_offset + size)],
+ None => &buf[data_offset..],
+ };
+ let maybe_err = gfx_select!(queue => instance.queue_write_buffer(
+ queue,
+ buffer,
+ buffer_offset,
+ data
+ ))
+ .err();
+
+ Ok(WebGpuResult::maybe_err(maybe_err))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_write_texture(
+ state: &mut OpState,
+ #[smi] queue_rid: ResourceId,
+ #[serde] destination: super::command_encoder::GpuImageCopyTexture,
+ #[serde] data_layout: GpuImageDataLayout,
+ #[serde] size: wgpu_types::Extent3d,
+ #[buffer] buf: &[u8],
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let texture_resource = state
+ .resource_table
+ .get::<super::texture::WebGpuTexture>(destination.texture)?;
+ let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
+ let queue = queue_resource.1;
+
+ let destination = wgpu_core::command::ImageCopyTexture {
+ texture: texture_resource.id,
+ mip_level: destination.mip_level,
+ origin: destination.origin,
+ aspect: destination.aspect,
+ };
+ let data_layout = data_layout.into();
+
+ gfx_ok!(queue => instance.queue_write_texture(
+ queue,
+ &destination,
+ buf,
+ &data_layout,
+ &size
+ ))
+}
diff --git a/ext/webgpu/render_pass.rs b/ext/webgpu/render_pass.rs
new file mode 100644
index 000000000..abb62a0be
--- /dev/null
+++ b/ext/webgpu/render_pass.rs
@@ -0,0 +1,519 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::type_error;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuRenderPass(
+ pub(crate) RefCell<wgpu_core::command::RenderPass>,
+);
+impl Resource for WebGpuRenderPass {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderPass".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetViewportArgs {
+ render_pass_rid: ResourceId,
+ x: f32,
+ y: f32,
+ width: f32,
+ height: f32,
+ min_depth: f32,
+ max_depth: f32,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_viewport(
+ state: &mut OpState,
+ #[serde] args: RenderPassSetViewportArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(args.render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_viewport(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.x,
+ args.y,
+ args.width,
+ args.height,
+ args.min_depth,
+ args.max_depth,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_scissor_rect(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_scissor_rect(
+ &mut render_pass_resource.0.borrow_mut(),
+ x,
+ y,
+ width,
+ height,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_blend_constant(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ #[serde] color: wgpu_types::Color,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_blend_constant(
+ &mut render_pass_resource.0.borrow_mut(),
+ &color,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_stencil_reference(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ reference: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_stencil_reference(
+ &mut render_pass_resource.0.borrow_mut(),
+ reference,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_begin_occlusion_query(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ query_index: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_begin_occlusion_query(
+ &mut render_pass_resource.0.borrow_mut(),
+ query_index,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_end_occlusion_query(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_end_occlusion_query(
+ &mut render_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_execute_bundles(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ #[serde] bundles: Vec<u32>,
+) -> Result<WebGpuResult, AnyError> {
+ let bundles = bundles
+ .iter()
+ .map(|rid| {
+ let render_bundle_resource =
+ state
+ .resource_table
+ .get::<super::bundle::WebGpuRenderBundle>(*rid)?;
+ Ok(render_bundle_resource.1)
+ })
+ .collect::<Result<Vec<_>, AnyError>>()?;
+
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_execute_bundles(
+ &mut render_pass_resource.0.borrow_mut(),
+ bundles.as_ptr(),
+ bundles.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_end(
+ state: &mut OpState,
+ #[smi] command_encoder_rid: ResourceId,
+ #[smi] render_pass_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<super::command_encoder::WebGpuCommandEncoder>(
+ command_encoder_rid,
+ )?;
+ let command_encoder = command_encoder_resource.1;
+ let render_pass_resource = state
+ .resource_table
+ .take::<WebGpuRenderPass>(render_pass_rid)?;
+ let render_pass = &render_pass_resource.0.borrow();
+ let instance = state.borrow::<super::Instance>();
+
+ gfx_ok!(command_encoder => instance.command_encoder_run_render_pass(command_encoder, render_pass))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_bind_group(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ index: u32,
+ bind_group: u32,
+ #[buffer] dynamic_offsets_data: &[u32],
+ #[number] dynamic_offsets_data_start: usize,
+ #[number] dynamic_offsets_data_length: usize,
+) -> Result<WebGpuResult, AnyError> {
+ let bind_group_resource =
+ state
+ .resource_table
+ .get::<super::binding::WebGpuBindGroup>(bind_group)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ let start = dynamic_offsets_data_start;
+ let len = dynamic_offsets_data_length;
+
+ // Assert that length and start are both in bounds
+ assert!(start <= dynamic_offsets_data.len());
+ assert!(len <= dynamic_offsets_data.len() - start);
+
+ let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
+
+ // SAFETY: the raw pointer and length are of the same slice, and that slice
+ // lives longer than the below function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_bind_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ index,
+ bind_group_resource.1,
+ dynamic_offsets_data.as_ptr(),
+ dynamic_offsets_data.len(),
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_push_debug_group(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ #[string] group_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ let label = std::ffi::CString::new(group_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_push_debug_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_pop_debug_group(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_pop_debug_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_insert_debug_marker(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ #[string] marker_label: &str,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ let label = std::ffi::CString::new(marker_label).unwrap();
+ // SAFETY: the string the raw pointer points to lives longer than the below
+ // function invocation.
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_insert_debug_marker(
+ &mut render_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_pipeline(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ pipeline: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pipeline_resource =
+ state
+ .resource_table
+ .get::<super::pipeline::WebGpuRenderPipeline>(pipeline)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_pipeline(
+ &mut render_pass_resource.0.borrow_mut(),
+ render_pipeline_resource.1,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_index_buffer(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ buffer: u32,
+ #[serde] index_format: wgpu_types::IndexFormat,
+ #[number] offset: u64,
+ #[number] size: Option<u64>,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ let size = if let Some(size) = size {
+ Some(
+ std::num::NonZeroU64::new(size)
+ .ok_or_else(|| type_error("size must be larger than 0"))?,
+ )
+ } else {
+ None
+ };
+
+ render_pass_resource.0.borrow_mut().set_index_buffer(
+ buffer_resource.1,
+ index_format,
+ offset,
+ size,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_set_vertex_buffer(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ slot: u32,
+ buffer: u32,
+ #[number] offset: u64,
+ #[number] size: Option<u64>,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ let size = if let Some(size) = size {
+ Some(
+ std::num::NonZeroU64::new(size)
+ .ok_or_else(|| type_error("size must be larger than 0"))?,
+ )
+ } else {
+ None
+ };
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_vertex_buffer(
+ &mut render_pass_resource.0.borrow_mut(),
+ slot,
+ buffer_resource.1,
+ offset,
+ size,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_draw(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw(
+ &mut render_pass_resource.0.borrow_mut(),
+ vertex_count,
+ instance_count,
+ first_vertex,
+ first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_draw_indexed(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+) -> Result<WebGpuResult, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed(
+ &mut render_pass_resource.0.borrow_mut(),
+ index_count,
+ instance_count,
+ first_index,
+ base_vertex,
+ first_instance,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_draw_indirect(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ indirect_buffer: u32,
+ #[number] indirect_offset: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indirect(
+ &mut render_pass_resource.0.borrow_mut(),
+ buffer_resource.1,
+ indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_render_pass_draw_indexed_indirect(
+ state: &mut OpState,
+ #[smi] render_pass_rid: ResourceId,
+ indirect_buffer: u32,
+ #[number] indirect_offset: u64,
+) -> Result<WebGpuResult, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGpuRenderPass>(render_pass_rid)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed_indirect(
+ &mut render_pass_resource.0.borrow_mut(),
+ buffer_resource.1,
+ indirect_offset,
+ );
+
+ Ok(WebGpuResult::empty())
+}
diff --git a/ext/webgpu/sampler.rs b/ext/webgpu/sampler.rs
new file mode 100644
index 000000000..3280a548a
--- /dev/null
+++ b/ext/webgpu/sampler.rs
@@ -0,0 +1,80 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::rc::Rc;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuSampler(
+ pub(crate) crate::Instance,
+ pub(crate) wgpu_core::id::SamplerId,
+);
+impl Resource for WebGpuSampler {
+ fn name(&self) -> Cow<str> {
+ "webGPUSampler".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.sampler_drop(self.1));
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateSamplerArgs {
+ device_rid: ResourceId,
+ label: String,
+ address_mode_u: wgpu_types::AddressMode,
+ address_mode_v: wgpu_types::AddressMode,
+ address_mode_w: wgpu_types::AddressMode,
+ mag_filter: wgpu_types::FilterMode,
+ min_filter: wgpu_types::FilterMode,
+ mipmap_filter: wgpu_types::FilterMode, // TODO: GPUMipmapFilterMode
+ lod_min_clamp: f32,
+ lod_max_clamp: f32,
+ compare: Option<wgpu_types::CompareFunction>,
+ max_anisotropy: u16,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_sampler(
+ state: &mut OpState,
+ #[serde] args: CreateSamplerArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.1;
+
+ let descriptor = wgpu_core::resource::SamplerDescriptor {
+ label: Some(Cow::Owned(args.label)),
+ address_modes: [
+ args.address_mode_u,
+ args.address_mode_v,
+ args.address_mode_w,
+ ],
+ mag_filter: args.mag_filter,
+ min_filter: args.min_filter,
+ mipmap_filter: args.mipmap_filter,
+ lod_min_clamp: args.lod_min_clamp,
+ lod_max_clamp: args.lod_max_clamp,
+ compare: args.compare,
+ anisotropy_clamp: args.max_anisotropy,
+ border_color: None, // native-only
+ };
+
+ gfx_put!(device => instance.device_create_sampler(
+ device,
+ &descriptor,
+ ()
+ ) => state, WebGpuSampler)
+}
diff --git a/ext/webgpu/shader.rs b/ext/webgpu/shader.rs
new file mode 100644
index 000000000..a6850d55a
--- /dev/null
+++ b/ext/webgpu/shader.rs
@@ -0,0 +1,55 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use std::borrow::Cow;
+use std::rc::Rc;
+
+use super::error::WebGpuResult;
+
+pub(crate) struct WebGpuShaderModule(
+ pub(crate) super::Instance,
+ pub(crate) wgpu_core::id::ShaderModuleId,
+);
+impl Resource for WebGpuShaderModule {
+ fn name(&self) -> Cow<str> {
+ "webGPUShaderModule".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.shader_module_drop(self.1));
+ }
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_shader_module(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[string] label: Cow<str>,
+ #[string] code: Cow<str>,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+
+ let source = wgpu_core::pipeline::ShaderModuleSource::Wgsl(code);
+
+ let descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
+ label: Some(label),
+ shader_bound_checks: wgpu_types::ShaderBoundChecks::default(),
+ };
+
+ gfx_put!(device => instance.device_create_shader_module(
+ device,
+ &descriptor,
+ source,
+ ()
+ ) => state, WebGpuShaderModule)
+}
diff --git a/ext/webgpu/surface.rs b/ext/webgpu/surface.rs
new file mode 100644
index 000000000..4e01717a6
--- /dev/null
+++ b/ext/webgpu/surface.rs
@@ -0,0 +1,133 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use super::WebGpuResult;
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::rc::Rc;
+use wgpu_types::SurfaceStatus;
+
+deno_core::extension!(
+ deno_webgpu_surface,
+ deps = [deno_webidl, deno_web, deno_webgpu],
+ ops = [
+ op_webgpu_surface_configure,
+ op_webgpu_surface_get_current_texture,
+ op_webgpu_surface_present,
+ ],
+ esm = ["02_surface.js"],
+);
+
+pub struct WebGpuSurface(pub crate::Instance, pub wgpu_core::id::SurfaceId);
+impl Resource for WebGpuSurface {
+ fn name(&self) -> Cow<str> {
+ "webGPUSurface".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ self.0.surface_drop(self.1);
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SurfaceConfigureArgs {
+ surface_rid: ResourceId,
+ device_rid: ResourceId,
+ format: wgpu_types::TextureFormat,
+ usage: u32,
+ width: u32,
+ height: u32,
+ present_mode: Option<wgpu_types::PresentMode>,
+ alpha_mode: wgpu_types::CompositeAlphaMode,
+ view_formats: Vec<wgpu_types::TextureFormat>,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_surface_configure(
+ state: &mut OpState,
+ #[serde] args: SurfaceConfigureArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.1;
+ let surface_resource = state
+ .resource_table
+ .get::<WebGpuSurface>(args.surface_rid)?;
+ let surface = surface_resource.1;
+
+ let conf = wgpu_types::SurfaceConfiguration::<Vec<wgpu_types::TextureFormat>> {
+ usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
+ format: args.format,
+ width: args.width,
+ height: args.height,
+ present_mode: args.present_mode.unwrap_or_default(),
+ alpha_mode: args.alpha_mode,
+ view_formats: args.view_formats,
+ };
+
+ let err =
+ gfx_select!(device => instance.surface_configure(surface, device, &conf));
+
+ Ok(WebGpuResult::maybe_err(err))
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_surface_get_current_texture(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[smi] surface_rid: ResourceId,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+ let surface_resource =
+ state.resource_table.get::<WebGpuSurface>(surface_rid)?;
+ let surface = surface_resource.1;
+
+ let output =
+ gfx_select!(device => instance.surface_get_current_texture(surface, ()))?;
+
+ match output.status {
+ SurfaceStatus::Good | SurfaceStatus::Suboptimal => {
+ let id = output.texture_id.unwrap();
+ let rid = state.resource_table.add(crate::texture::WebGpuTexture {
+ instance: instance.clone(),
+ id,
+ owned: false,
+ });
+ Ok(WebGpuResult::rid(rid))
+ }
+ _ => Err(AnyError::msg("Invalid Surface Status")),
+ }
+}
+
+#[op2(fast)]
+pub fn op_webgpu_surface_present(
+ state: &mut OpState,
+ #[smi] device_rid: ResourceId,
+ #[smi] surface_rid: ResourceId,
+) -> Result<(), AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(device_rid)?;
+ let device = device_resource.1;
+ let surface_resource =
+ state.resource_table.get::<WebGpuSurface>(surface_rid)?;
+ let surface = surface_resource.1;
+
+ let _ = gfx_select!(device => instance.surface_present(surface))?;
+
+ Ok(())
+}
diff --git a/ext/webgpu/texture.rs b/ext/webgpu/texture.rs
new file mode 100644
index 000000000..81a11a348
--- /dev/null
+++ b/ext/webgpu/texture.rs
@@ -0,0 +1,134 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::op2;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::rc::Rc;
+
+use super::error::WebGpuResult;
+pub(crate) struct WebGpuTexture {
+ pub(crate) instance: crate::Instance,
+ pub(crate) id: wgpu_core::id::TextureId,
+ pub(crate) owned: bool,
+}
+
+impl Resource for WebGpuTexture {
+ fn name(&self) -> Cow<str> {
+ "webGPUTexture".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ if self.owned {
+ let instance = &self.instance;
+ gfx_select!(self.id => instance.texture_drop(self.id, true));
+ }
+ }
+}
+
+pub(crate) struct WebGpuTextureView(
+ pub(crate) crate::Instance,
+ pub(crate) wgpu_core::id::TextureViewId,
+);
+impl Resource for WebGpuTextureView {
+ fn name(&self) -> Cow<str> {
+ "webGPUTextureView".into()
+ }
+
+ fn close(self: Rc<Self>) {
+ let instance = &self.0;
+ gfx_select!(self.1 => instance.texture_view_drop(self.1, true)).unwrap();
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateTextureArgs {
+ device_rid: ResourceId,
+ label: String,
+ size: wgpu_types::Extent3d,
+ mip_level_count: u32,
+ sample_count: u32,
+ dimension: wgpu_types::TextureDimension,
+ format: wgpu_types::TextureFormat,
+ usage: u32,
+ view_formats: Vec<wgpu_types::TextureFormat>,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_texture(
+ state: &mut OpState,
+ #[serde] args: CreateTextureArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGpuDevice>(args.device_rid)?;
+ let device = device_resource.1;
+
+ let descriptor = wgpu_core::resource::TextureDescriptor {
+ label: Some(Cow::Owned(args.label)),
+ size: args.size,
+ mip_level_count: args.mip_level_count,
+ sample_count: args.sample_count,
+ dimension: args.dimension,
+ format: args.format,
+ usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
+ view_formats: args.view_formats,
+ };
+
+ let (val, maybe_err) = gfx_select!(device => instance.device_create_texture(
+ device,
+ &descriptor,
+ ()
+ ));
+
+ let rid = state.resource_table.add(WebGpuTexture {
+ instance: instance.clone(),
+ id: val,
+ owned: true,
+ });
+
+ Ok(WebGpuResult::rid_err(rid, maybe_err))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateTextureViewArgs {
+ texture_rid: ResourceId,
+ label: String,
+ format: Option<wgpu_types::TextureFormat>,
+ dimension: Option<wgpu_types::TextureViewDimension>,
+ #[serde(flatten)]
+ range: wgpu_types::ImageSubresourceRange,
+}
+
+#[op2]
+#[serde]
+pub fn op_webgpu_create_texture_view(
+ state: &mut OpState,
+ #[serde] args: CreateTextureViewArgs,
+) -> Result<WebGpuResult, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let texture_resource = state
+ .resource_table
+ .get::<WebGpuTexture>(args.texture_rid)?;
+ let texture = texture_resource.id;
+
+ let descriptor = wgpu_core::resource::TextureViewDescriptor {
+ label: Some(Cow::Owned(args.label)),
+ format: args.format,
+ dimension: args.dimension,
+ range: args.range,
+ };
+
+ gfx_put!(texture => instance.texture_create_view(
+ texture,
+ &descriptor,
+ ()
+ ) => state, WebGpuTextureView)
+}
diff --git a/ext/webgpu/webgpu.idl b/ext/webgpu/webgpu.idl
new file mode 100644
index 000000000..0b6b04eb4
--- /dev/null
+++ b/ext/webgpu/webgpu.idl
@@ -0,0 +1,1233 @@
+interface mixin GPUObjectBase {
+ attribute USVString label;
+};
+
+dictionary GPUObjectDescriptorBase {
+ USVString label = "";
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUSupportedLimits {
+ readonly attribute unsigned long maxTextureDimension1D;
+ readonly attribute unsigned long maxTextureDimension2D;
+ readonly attribute unsigned long maxTextureDimension3D;
+ readonly attribute unsigned long maxTextureArrayLayers;
+ readonly attribute unsigned long maxBindGroups;
+ readonly attribute unsigned long maxBindingsPerBindGroup;
+ readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout;
+ readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout;
+ readonly attribute unsigned long maxSampledTexturesPerShaderStage;
+ readonly attribute unsigned long maxSamplersPerShaderStage;
+ readonly attribute unsigned long maxStorageBuffersPerShaderStage;
+ readonly attribute unsigned long maxStorageTexturesPerShaderStage;
+ readonly attribute unsigned long maxUniformBuffersPerShaderStage;
+ readonly attribute unsigned long long maxUniformBufferBindingSize;
+ readonly attribute unsigned long long maxStorageBufferBindingSize;
+ readonly attribute unsigned long minUniformBufferOffsetAlignment;
+ readonly attribute unsigned long minStorageBufferOffsetAlignment;
+ readonly attribute unsigned long maxVertexBuffers;
+ readonly attribute unsigned long long maxBufferSize;
+ readonly attribute unsigned long maxVertexAttributes;
+ readonly attribute unsigned long maxVertexBufferArrayStride;
+ readonly attribute unsigned long maxInterStageShaderComponents;
+ readonly attribute unsigned long maxComputeWorkgroupStorageSize;
+ readonly attribute unsigned long maxComputeInvocationsPerWorkgroup;
+ readonly attribute unsigned long maxComputeWorkgroupSizeX;
+ readonly attribute unsigned long maxComputeWorkgroupSizeY;
+ readonly attribute unsigned long maxComputeWorkgroupSizeZ;
+ readonly attribute unsigned long maxComputeWorkgroupsPerDimension;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUSupportedFeatures {
+ readonly setlike<DOMString>;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUAdapterInfo {
+ readonly attribute DOMString vendor;
+ readonly attribute DOMString architecture;
+ readonly attribute DOMString device;
+ readonly attribute DOMString description;
+};
+
+interface mixin NavigatorGPU {
+ [SameObject, SecureContext] readonly attribute GPU gpu;
+};
+Navigator includes NavigatorGPU;
+WorkerNavigator includes NavigatorGPU;
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPU {
+ Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {});
+};
+
+dictionary GPURequestAdapterOptions {
+ GPUPowerPreference powerPreference;
+ boolean forceFallbackAdapter = false;
+};
+
+enum GPUPowerPreference {
+ "low-power",
+ "high-performance",
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUAdapter {
+ [SameObject] readonly attribute GPUSupportedFeatures features;
+ [SameObject] readonly attribute GPUSupportedLimits limits;
+ readonly attribute boolean isFallbackAdapter;
+
+ Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
+ Promise<GPUAdapterInfo> requestAdapterInfo(optional sequence<DOMString> unmaskHints = []);
+};
+
+dictionary GPUDeviceDescriptor
+ : GPUObjectDescriptorBase {
+ sequence<GPUFeatureName> requiredFeatures = [];
+ record<DOMString, GPUSize64> requiredLimits = {};
+};
+
+enum GPUFeatureName {
+ // api
+ "depth-clip-control",
+ // texture formats
+ "depth32float-stencil8",
+ "texture-compression-bc",
+ "texture-compression-etc2",
+ "texture-compression-astc",
+ // api
+ "timestamp-query",
+ "indirect-first-instance",
+ // shader
+ "shader-f16",
+ "rg11b10ufloat-renderable",
+ "bgra8unorm-storage",
+
+ // extended from spec
+
+ // texture formats
+ "texture-format-16-bit-norm",
+ "texture-compression-astc-hdr",
+ "texture-adapter-specific-format-features",
+ // api
+ "pipeline-statistics-query",
+ "timestamp-query-inside-passes",
+ "mappable-primary-buffers",
+ "texture-binding-array",
+ "buffer-binding-array",
+ "storage-resource-binding-array",
+ "sampled-texture-and-storage-buffer-array-non-uniform-indexing",
+ "uniform-buffer-and-storage-texture-array-non-uniform-indexing",
+ "partially-bound-binding-array",
+ "multi-draw-indirect",
+ "multi-draw-indirect-count",
+ "push-constants",
+ "address-mode-clamp-to-zero",
+ "address-mode-clamp-to-border",
+ "polygon-mode-line",
+ "polygon-mode-point",
+ "conservative-rasterization",
+ "vertex-writable-storage",
+ "clear-texture",
+ "spirv-shader-passthrough",
+ "multiview",
+ "vertex-attribute-64-bit",
+ // shader
+ "shader-f64",
+ "shader-i16",
+ "shader-primitive-index",
+ "shader-early-depth-test",
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUDevice : EventTarget {
+ [SameObject] readonly attribute GPUSupportedFeatures features;
+ [SameObject] readonly attribute GPUSupportedLimits limits;
+
+ [SameObject] readonly attribute GPUQueue queue;
+
+ undefined destroy();
+
+ GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
+ GPUTexture createTexture(GPUTextureDescriptor descriptor);
+ GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
+
+ GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
+ GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
+ GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
+
+ GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
+ GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
+ GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
+ Promise<GPUComputePipeline> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor);
+ Promise<GPURenderPipeline> createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor);
+
+ GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
+ GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
+
+ GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor);
+};
+GPUDevice includes GPUObjectBase;
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUBuffer {
+ readonly attribute GPUSize64Out size;
+ readonly attribute GPUFlagsConstant usage;
+
+ readonly attribute GPUBufferMapState mapState;
+
+ Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
+ ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
+ undefined unmap();
+
+ undefined destroy();
+};
+GPUBuffer includes GPUObjectBase;
+
+enum GPUBufferMapState {
+ "unmapped",
+ "pending",
+ "mapped",
+};
+
+dictionary GPUBufferDescriptor
+ : GPUObjectDescriptorBase {
+ required GPUSize64 size;
+ required GPUBufferUsageFlags usage;
+ boolean mappedAtCreation = false;
+};
+
+typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
+[Exposed=(Window, DedicatedWorker), SecureContext]
+namespace GPUBufferUsage {
+ const GPUFlagsConstant MAP_READ = 0x0001;
+ const GPUFlagsConstant MAP_WRITE = 0x0002;
+ const GPUFlagsConstant COPY_SRC = 0x0004;
+ const GPUFlagsConstant COPY_DST = 0x0008;
+ const GPUFlagsConstant INDEX = 0x0010;
+ const GPUFlagsConstant VERTEX = 0x0020;
+ const GPUFlagsConstant UNIFORM = 0x0040;
+ const GPUFlagsConstant STORAGE = 0x0080;
+ const GPUFlagsConstant INDIRECT = 0x0100;
+ const GPUFlagsConstant QUERY_RESOLVE = 0x0200;
+};
+
+typedef [EnforceRange] unsigned long GPUMapModeFlags;
+[Exposed=(Window, DedicatedWorker), SecureContext]
+namespace GPUMapMode {
+ const GPUFlagsConstant READ = 0x0001;
+ const GPUFlagsConstant WRITE = 0x0002;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUTexture {
+ GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {});
+
+ undefined destroy();
+
+ readonly attribute GPUIntegerCoordinateOut width;
+ readonly attribute GPUIntegerCoordinateOut height;
+ readonly attribute GPUIntegerCoordinateOut depthOrArrayLayers;
+ readonly attribute GPUIntegerCoordinateOut mipLevelCount;
+ readonly attribute GPUSize32Out sampleCount;
+ readonly attribute GPUTextureDimension dimension;
+ readonly attribute GPUTextureFormat format;
+ readonly attribute GPUFlagsConstant usage;
+};
+GPUTexture includes GPUObjectBase;
+
+dictionary GPUTextureDescriptor
+ : GPUObjectDescriptorBase {
+ required GPUExtent3D size;
+ GPUIntegerCoordinate mipLevelCount = 1;
+ GPUSize32 sampleCount = 1;
+ GPUTextureDimension dimension = "2d";
+ required GPUTextureFormat format;
+ required GPUTextureUsageFlags usage;
+ sequence<GPUTextureFormat> viewFormats = [];
+};
+
+enum GPUTextureDimension {
+ "1d",
+ "2d",
+ "3d",
+};
+
+typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
+[Exposed=(Window, DedicatedWorker), SecureContext]
+namespace GPUTextureUsage {
+ const GPUFlagsConstant COPY_SRC = 0x01;
+ const GPUFlagsConstant COPY_DST = 0x02;
+ const GPUFlagsConstant TEXTURE_BINDING = 0x04;
+ const GPUFlagsConstant STORAGE_BINDING = 0x08;
+ const GPUFlagsConstant RENDER_ATTACHMENT = 0x10;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUTextureView {
+};
+GPUTextureView includes GPUObjectBase;
+
+dictionary GPUTextureViewDescriptor
+ : GPUObjectDescriptorBase {
+ GPUTextureFormat format;
+ GPUTextureViewDimension dimension;
+ GPUTextureAspect aspect = "all";
+ GPUIntegerCoordinate baseMipLevel = 0;
+ GPUIntegerCoordinate mipLevelCount;
+ GPUIntegerCoordinate baseArrayLayer = 0;
+ GPUIntegerCoordinate arrayLayerCount;
+};
+
+enum GPUTextureViewDimension {
+ "1d",
+ "2d",
+ "2d-array",
+ "cube",
+ "cube-array",
+ "3d",
+};
+
+enum GPUTextureAspect {
+ "all",
+ "stencil-only",
+ "depth-only",
+};
+
+enum GPUTextureFormat {
+ // 8-bit formats
+ "r8unorm",
+ "r8snorm",
+ "r8uint",
+ "r8sint",
+
+ // 16-bit formats
+ "r16uint",
+ "r16sint",
+ "r16float",
+ "rg8unorm",
+ "rg8snorm",
+ "rg8uint",
+ "rg8sint",
+
+ // 32-bit formats
+ "r32uint",
+ "r32sint",
+ "r32float",
+ "rg16uint",
+ "rg16sint",
+ "rg16float",
+ "rgba8unorm",
+ "rgba8unorm-srgb",
+ "rgba8snorm",
+ "rgba8uint",
+ "rgba8sint",
+ "bgra8unorm",
+ "bgra8unorm-srgb",
+ // Packed 32-bit formats
+ "rgb9e5ufloat",
+ "rgb10a2unorm",
+ "rg11b10ufloat",
+
+ // 64-bit formats
+ "rg32uint",
+ "rg32sint",
+ "rg32float",
+ "rgba16uint",
+ "rgba16sint",
+ "rgba16float",
+
+ // 128-bit formats
+ "rgba32uint",
+ "rgba32sint",
+ "rgba32float",
+
+ // Depth/stencil formats
+ "stencil8",
+ "depth16unorm",
+ "depth24plus",
+ "depth24plus-stencil8",
+ "depth32float",
+
+ // "depth32float-stencil8" feature
+ "depth32float-stencil8",
+
+ // BC compressed formats usable if "texture-compression-bc" is both
+ // supported by the device/user agent and enabled in requestDevice.
+ "bc1-rgba-unorm",
+ "bc1-rgba-unorm-srgb",
+ "bc2-rgba-unorm",
+ "bc2-rgba-unorm-srgb",
+ "bc3-rgba-unorm",
+ "bc3-rgba-unorm-srgb",
+ "bc4-r-unorm",
+ "bc4-r-snorm",
+ "bc5-rg-unorm",
+ "bc5-rg-snorm",
+ "bc6h-rgb-ufloat",
+ "bc6h-rgb-float",
+ "bc7-rgba-unorm",
+ "bc7-rgba-unorm-srgb",
+
+ // ETC2 compressed formats usable if "texture-compression-etc2" is both
+ // supported by the device/user agent and enabled in requestDevice.
+ "etc2-rgb8unorm",
+ "etc2-rgb8unorm-srgb",
+ "etc2-rgb8a1unorm",
+ "etc2-rgb8a1unorm-srgb",
+ "etc2-rgba8unorm",
+ "etc2-rgba8unorm-srgb",
+ "eac-r11unorm",
+ "eac-r11snorm",
+ "eac-rg11unorm",
+ "eac-rg11snorm",
+
+ // ASTC compressed formats usable if "texture-compression-astc" is both
+ // supported by the device/user agent and enabled in requestDevice.
+ "astc-4x4-unorm",
+ "astc-4x4-unorm-srgb",
+ "astc-5x4-unorm",
+ "astc-5x4-unorm-srgb",
+ "astc-5x5-unorm",
+ "astc-5x5-unorm-srgb",
+ "astc-6x5-unorm",
+ "astc-6x5-unorm-srgb",
+ "astc-6x6-unorm",
+ "astc-6x6-unorm-srgb",
+ "astc-8x5-unorm",
+ "astc-8x5-unorm-srgb",
+ "astc-8x6-unorm",
+ "astc-8x6-unorm-srgb",
+ "astc-8x8-unorm",
+ "astc-8x8-unorm-srgb",
+ "astc-10x5-unorm",
+ "astc-10x5-unorm-srgb",
+ "astc-10x6-unorm",
+ "astc-10x6-unorm-srgb",
+ "astc-10x8-unorm",
+ "astc-10x8-unorm-srgb",
+ "astc-10x10-unorm",
+ "astc-10x10-unorm-srgb",
+ "astc-12x10-unorm",
+ "astc-12x10-unorm-srgb",
+ "astc-12x12-unorm",
+ "astc-12x12-unorm-srgb",
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUSampler {
+};
+GPUSampler includes GPUObjectBase;
+
+dictionary GPUSamplerDescriptor
+ : GPUObjectDescriptorBase {
+ GPUAddressMode addressModeU = "clamp-to-edge";
+ GPUAddressMode addressModeV = "clamp-to-edge";
+ GPUAddressMode addressModeW = "clamp-to-edge";
+ GPUFilterMode magFilter = "nearest";
+ GPUFilterMode minFilter = "nearest";
+ GPUMipmapFilterMode mipmapFilter = "nearest";
+ float lodMinClamp = 0;
+ float lodMaxClamp = 32;
+ GPUCompareFunction compare;
+ [Clamp] unsigned short maxAnisotropy = 1;
+};
+
+enum GPUAddressMode {
+ "clamp-to-edge",
+ "repeat",
+ "mirror-repeat",
+};
+
+enum GPUFilterMode {
+ "nearest",
+ "linear",
+};
+
+enum GPUMipmapFilterMode {
+ "nearest",
+ "linear",
+};
+
+enum GPUCompareFunction {
+ "never",
+ "less",
+ "equal",
+ "less-equal",
+ "greater",
+ "not-equal",
+ "greater-equal",
+ "always",
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUBindGroupLayout {
+};
+GPUBindGroupLayout includes GPUObjectBase;
+
+dictionary GPUBindGroupLayoutDescriptor
+ : GPUObjectDescriptorBase {
+ required sequence<GPUBindGroupLayoutEntry> entries;
+};
+
+dictionary GPUBindGroupLayoutEntry {
+ required GPUIndex32 binding;
+ required GPUShaderStageFlags visibility;
+
+ GPUBufferBindingLayout buffer;
+ GPUSamplerBindingLayout sampler;
+ GPUTextureBindingLayout texture;
+ GPUStorageTextureBindingLayout storageTexture;
+};
+
+typedef [EnforceRange] unsigned long GPUShaderStageFlags;
+[Exposed=(Window, DedicatedWorker), SecureContext]
+namespace GPUShaderStage {
+ const GPUFlagsConstant VERTEX = 0x1;
+ const GPUFlagsConstant FRAGMENT = 0x2;
+ const GPUFlagsConstant COMPUTE = 0x4;
+};
+
+enum GPUBufferBindingType {
+ "uniform",
+ "storage",
+ "read-only-storage",
+};
+
+dictionary GPUBufferBindingLayout {
+ GPUBufferBindingType type = "uniform";
+ boolean hasDynamicOffset = false;
+ GPUSize64 minBindingSize = 0;
+};
+
+enum GPUSamplerBindingType {
+ "filtering",
+ "non-filtering",
+ "comparison",
+};
+
+dictionary GPUSamplerBindingLayout {
+ GPUSamplerBindingType type = "filtering";
+};
+
+enum GPUTextureSampleType {
+ "float",
+ "unfilterable-float",
+ "depth",
+ "sint",
+ "uint",
+};
+
+dictionary GPUTextureBindingLayout {
+ GPUTextureSampleType sampleType = "float";
+ GPUTextureViewDimension viewDimension = "2d";
+ boolean multisampled = false;
+};
+
+enum GPUStorageTextureAccess {
+ "write-only",
+};
+
+dictionary GPUStorageTextureBindingLayout {
+ GPUStorageTextureAccess access = "write-only";
+ required GPUTextureFormat format;
+ GPUTextureViewDimension viewDimension = "2d";
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUBindGroup {
+};
+GPUBindGroup includes GPUObjectBase;
+
+dictionary GPUBindGroupDescriptor
+ : GPUObjectDescriptorBase {
+ required GPUBindGroupLayout layout;
+ required sequence<GPUBindGroupEntry> entries;
+};
+
+typedef (GPUSampler or GPUTextureView or GPUBufferBinding) GPUBindingResource;
+
+dictionary GPUBindGroupEntry {
+ required GPUIndex32 binding;
+ required GPUBindingResource resource;
+};
+
+dictionary GPUBufferBinding {
+ required GPUBuffer buffer;
+ GPUSize64 offset = 0;
+ GPUSize64 size;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUPipelineLayout {
+};
+GPUPipelineLayout includes GPUObjectBase;
+
+dictionary GPUPipelineLayoutDescriptor
+ : GPUObjectDescriptorBase {
+ required sequence<GPUBindGroupLayout> bindGroupLayouts;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUShaderModule {
+};
+GPUShaderModule includes GPUObjectBase;
+
+dictionary GPUShaderModuleDescriptor
+ : GPUObjectDescriptorBase {
+ required USVString code;
+};
+
+enum GPUCompilationMessageType {
+ "error",
+ "warning",
+ "info",
+};
+
+[Exposed=(Window, DedicatedWorker), Serializable, SecureContext]
+interface GPUCompilationMessage {
+ readonly attribute DOMString message;
+ readonly attribute GPUCompilationMessageType type;
+ readonly attribute unsigned long long lineNum;
+ readonly attribute unsigned long long linePos;
+ readonly attribute unsigned long long offset;
+ readonly attribute unsigned long long length;
+};
+
+[Exposed=(Window, DedicatedWorker), Serializable, SecureContext]
+interface GPUCompilationInfo {
+ readonly attribute FrozenArray<GPUCompilationMessage> messages;
+};
+
+enum GPUAutoLayoutMode {
+ "auto",
+};
+
+dictionary GPUPipelineDescriptorBase
+ : GPUObjectDescriptorBase {
+ required (GPUPipelineLayout or GPUAutoLayoutMode) layout;
+};
+
+interface mixin GPUPipelineBase {
+ [NewObject] GPUBindGroupLayout getBindGroupLayout(unsigned long index);
+};
+
+dictionary GPUProgrammableStage {
+ required GPUShaderModule module;
+ required USVString entryPoint;
+ record<USVString, GPUPipelineConstantValue> constants;
+};
+
+typedef double GPUPipelineConstantValue; // May represent WGSL’s bool, f32, i32, u32, and f16 if enabled.
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUComputePipeline {
+};
+GPUComputePipeline includes GPUObjectBase;
+GPUComputePipeline includes GPUPipelineBase;
+
+dictionary GPUComputePipelineDescriptor
+ : GPUPipelineDescriptorBase {
+ required GPUProgrammableStage compute;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPURenderPipeline {
+};
+GPURenderPipeline includes GPUObjectBase;
+GPURenderPipeline includes GPUPipelineBase;
+
+dictionary GPURenderPipelineDescriptor
+ : GPUPipelineDescriptorBase {
+ required GPUVertexState vertex;
+ GPUPrimitiveState primitive = {};
+ GPUDepthStencilState depthStencil;
+ GPUMultisampleState multisample = {};
+ GPUFragmentState fragment;
+};
+
+dictionary GPUPrimitiveState {
+ GPUPrimitiveTopology topology = "triangle-list";
+ GPUIndexFormat stripIndexFormat;
+ GPUFrontFace frontFace = "ccw";
+ GPUCullMode cullMode = "none";
+
+ // Requires "depth-clip-control" feature.
+ boolean unclippedDepth = false;
+};
+
+enum GPUPrimitiveTopology {
+ "point-list",
+ "line-list",
+ "line-strip",
+ "triangle-list",
+ "triangle-strip",
+};
+
+enum GPUFrontFace {
+ "ccw",
+ "cw",
+};
+
+enum GPUCullMode {
+ "none",
+ "front",
+ "back",
+};
+
+dictionary GPUMultisampleState {
+ GPUSize32 count = 1;
+ GPUSampleMask mask = 0xFFFFFFFF;
+ boolean alphaToCoverageEnabled = false;
+};
+
+dictionary GPUFragmentState
+ : GPUProgrammableStage {
+ required sequence<GPUColorTargetState?> targets;
+};
+
+dictionary GPUColorTargetState {
+ required GPUTextureFormat format;
+
+ GPUBlendState blend;
+ GPUColorWriteFlags writeMask = 0xF; // GPUColorWrite.ALL
+};
+
+dictionary GPUBlendState {
+ required GPUBlendComponent color;
+ required GPUBlendComponent alpha;
+};
+
+typedef [EnforceRange] unsigned long GPUColorWriteFlags;
+[Exposed=(Window, DedicatedWorker), SecureContext]
+namespace GPUColorWrite {
+ const GPUFlagsConstant RED = 0x1;
+ const GPUFlagsConstant GREEN = 0x2;
+ const GPUFlagsConstant BLUE = 0x4;
+ const GPUFlagsConstant ALPHA = 0x8;
+ const GPUFlagsConstant ALL = 0xF;
+};
+
+dictionary GPUBlendComponent {
+ GPUBlendOperation operation = "add";
+ GPUBlendFactor srcFactor = "one";
+ GPUBlendFactor dstFactor = "zero";
+};
+
+enum GPUBlendFactor {
+ "zero",
+ "one",
+ "src",
+ "one-minus-src",
+ "src-alpha",
+ "one-minus-src-alpha",
+ "dst",
+ "one-minus-dst",
+ "dst-alpha",
+ "one-minus-dst-alpha",
+ "src-alpha-saturated",
+ "constant",
+ "one-minus-constant",
+};
+
+enum GPUBlendOperation {
+ "add",
+ "subtract",
+ "reverse-subtract",
+ "min",
+ "max",
+};
+
+dictionary GPUDepthStencilState {
+ required GPUTextureFormat format;
+
+ required boolean depthWriteEnabled;
+ required GPUCompareFunction depthCompare;
+
+ GPUStencilFaceState stencilFront = {};
+ GPUStencilFaceState stencilBack = {};
+
+ GPUStencilValue stencilReadMask = 0xFFFFFFFF;
+ GPUStencilValue stencilWriteMask = 0xFFFFFFFF;
+
+ GPUDepthBias depthBias = 0;
+ float depthBiasSlopeScale = 0;
+ float depthBiasClamp = 0;
+};
+
+dictionary GPUStencilFaceState {
+ GPUCompareFunction compare = "always";
+ GPUStencilOperation failOp = "keep";
+ GPUStencilOperation depthFailOp = "keep";
+ GPUStencilOperation passOp = "keep";
+};
+
+enum GPUStencilOperation {
+ "keep",
+ "zero",
+ "replace",
+ "invert",
+ "increment-clamp",
+ "decrement-clamp",
+ "increment-wrap",
+ "decrement-wrap",
+};
+
+enum GPUIndexFormat {
+ "uint16",
+ "uint32",
+};
+
+enum GPUVertexFormat {
+ "uint8x2",
+ "uint8x4",
+ "sint8x2",
+ "sint8x4",
+ "unorm8x2",
+ "unorm8x4",
+ "snorm8x2",
+ "snorm8x4",
+ "uint16x2",
+ "uint16x4",
+ "sint16x2",
+ "sint16x4",
+ "unorm16x2",
+ "unorm16x4",
+ "snorm16x2",
+ "snorm16x4",
+ "float16x2",
+ "float16x4",
+ "float32",
+ "float32x2",
+ "float32x3",
+ "float32x4",
+ "uint32",
+ "uint32x2",
+ "uint32x3",
+ "uint32x4",
+ "sint32",
+ "sint32x2",
+ "sint32x3",
+ "sint32x4",
+};
+
+enum GPUVertexStepMode {
+ "vertex",
+ "instance",
+};
+
+dictionary GPUVertexState
+ : GPUProgrammableStage {
+ sequence<GPUVertexBufferLayout?> buffers = [];
+};
+
+dictionary GPUVertexBufferLayout {
+ required GPUSize64 arrayStride;
+ GPUVertexStepMode stepMode = "vertex";
+ required sequence<GPUVertexAttribute> attributes;
+};
+
+dictionary GPUVertexAttribute {
+ required GPUVertexFormat format;
+ required GPUSize64 offset;
+
+ required GPUIndex32 shaderLocation;
+};
+
+dictionary GPUImageDataLayout {
+ GPUSize64 offset = 0;
+ GPUSize32 bytesPerRow;
+ GPUSize32 rowsPerImage;
+};
+
+dictionary GPUImageCopyBuffer
+ : GPUImageDataLayout {
+ required GPUBuffer buffer;
+};
+
+dictionary GPUImageCopyTexture {
+ required GPUTexture texture;
+ GPUIntegerCoordinate mipLevel = 0;
+ GPUOrigin3D origin = {};
+ GPUTextureAspect aspect = "all";
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUCommandBuffer {
+};
+GPUCommandBuffer includes GPUObjectBase;
+
+dictionary GPUCommandBufferDescriptor
+ : GPUObjectDescriptorBase {
+};
+
+interface mixin GPUCommandsMixin {
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUCommandEncoder {
+ GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
+ GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});
+
+ undefined copyBufferToBuffer(
+ GPUBuffer source,
+ GPUSize64 sourceOffset,
+ GPUBuffer destination,
+ GPUSize64 destinationOffset,
+ GPUSize64 size);
+
+ undefined copyBufferToTexture(
+ GPUImageCopyBuffer source,
+ GPUImageCopyTexture destination,
+ GPUExtent3D copySize);
+
+ undefined copyTextureToBuffer(
+ GPUImageCopyTexture source,
+ GPUImageCopyBuffer destination,
+ GPUExtent3D copySize);
+
+ undefined copyTextureToTexture(
+ GPUImageCopyTexture source,
+ GPUImageCopyTexture destination,
+ GPUExtent3D copySize);
+
+ undefined clearBuffer(
+ GPUBuffer buffer,
+ optional GPUSize64 offset = 0,
+ optional GPUSize64 size);
+
+ undefined writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);
+
+ undefined resolveQuerySet(
+ GPUQuerySet querySet,
+ GPUSize32 firstQuery,
+ GPUSize32 queryCount,
+ GPUBuffer destination,
+ GPUSize64 destinationOffset);
+
+ GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
+};
+GPUCommandEncoder includes GPUObjectBase;
+GPUCommandEncoder includes GPUCommandsMixin;
+GPUCommandEncoder includes GPUDebugCommandsMixin;
+
+dictionary GPUCommandEncoderDescriptor
+ : GPUObjectDescriptorBase {
+};
+
+interface mixin GPUBindingCommandsMixin {
+ undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
+ optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);
+
+ undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
+ Uint32Array dynamicOffsetsData,
+ GPUSize64 dynamicOffsetsDataStart,
+ GPUSize32 dynamicOffsetsDataLength);
+};
+
+interface mixin GPUDebugCommandsMixin {
+ undefined pushDebugGroup(USVString groupLabel);
+ undefined popDebugGroup();
+ undefined insertDebugMarker(USVString markerLabel);
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUComputePassEncoder {
+ undefined setPipeline(GPUComputePipeline pipeline);
+ undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1);
+ undefined dispatchWorkgroupsIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
+
+ undefined end();
+};
+GPUComputePassEncoder includes GPUObjectBase;
+GPUComputePassEncoder includes GPUCommandsMixin;
+GPUComputePassEncoder includes GPUDebugCommandsMixin;
+GPUComputePassEncoder includes GPUBindingCommandsMixin;
+
+dictionary GPUComputePassTimestampWrites {
+ required GPUQuerySet querySet;
+ GPUSize32 beginningOfPassWriteIndex;
+ GPUSize32 endOfPassWriteIndex;
+};
+
+dictionary GPUComputePassDescriptor
+ : GPUObjectDescriptorBase {
+ GPUComputePassTimestampWrites timestampWrites;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPURenderPassEncoder {
+ undefined setViewport(float x, float y,
+ float width, float height,
+ float minDepth, float maxDepth);
+
+ undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y,
+ GPUIntegerCoordinate width, GPUIntegerCoordinate height);
+
+ undefined setBlendConstant(GPUColor color);
+ undefined setStencilReference(GPUStencilValue reference);
+
+ undefined beginOcclusionQuery(GPUSize32 queryIndex);
+ undefined endOcclusionQuery();
+
+ undefined executeBundles(sequence<GPURenderBundle> bundles);
+ undefined end();
+};
+GPURenderPassEncoder includes GPUObjectBase;
+GPURenderPassEncoder includes GPUCommandsMixin;
+GPURenderPassEncoder includes GPUDebugCommandsMixin;
+GPURenderPassEncoder includes GPUBindingCommandsMixin;
+GPURenderPassEncoder includes GPURenderCommandsMixin;
+
+dictionary GPURenderPassTimestampWrites {
+ required GPUQuerySet querySet;
+ GPUSize32 beginningOfPassWriteIndex;
+ GPUSize32 endOfPassWriteIndex;
+};
+
+dictionary GPURenderPassDescriptor
+ : GPUObjectDescriptorBase {
+ required sequence<GPURenderPassColorAttachment?> colorAttachments;
+ GPURenderPassDepthStencilAttachment depthStencilAttachment;
+ GPUQuerySet occlusionQuerySet;
+ GPURenderPassTimestampWrites timestampWrites;
+};
+
+dictionary GPURenderPassColorAttachment {
+ required GPUTextureView view;
+ GPUTextureView resolveTarget;
+
+ GPUColor clearValue;
+ required GPULoadOp loadOp;
+ required GPUStoreOp storeOp;
+};
+
+dictionary GPURenderPassDepthStencilAttachment {
+ required GPUTextureView view;
+
+ float depthClearValue;
+ GPULoadOp depthLoadOp;
+ GPUStoreOp depthStoreOp;
+ boolean depthReadOnly = false;
+
+ GPUStencilValue stencilClearValue = 0;
+ GPULoadOp stencilLoadOp;
+ GPUStoreOp stencilStoreOp;
+ boolean stencilReadOnly = false;
+};
+
+enum GPULoadOp {
+ "load",
+ "clear",
+};
+
+enum GPUStoreOp {
+ "store",
+ "discard",
+};
+
+dictionary GPURenderPassLayout
+ : GPUObjectDescriptorBase {
+ required sequence<GPUTextureFormat?> colorFormats;
+ GPUTextureFormat depthStencilFormat;
+ GPUSize32 sampleCount = 1;
+};
+
+interface mixin GPURenderCommandsMixin {
+ undefined setPipeline(GPURenderPipeline pipeline);
+
+ undefined setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size);
+ undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer buffer, optional GPUSize64 offset = 0, optional GPUSize64 size);
+
+ undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1,
+ optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0);
+ undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1,
+ optional GPUSize32 firstIndex = 0,
+ optional GPUSignedOffset32 baseVertex = 0,
+ optional GPUSize32 firstInstance = 0);
+
+ undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
+ undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPURenderBundle {
+};
+GPURenderBundle includes GPUObjectBase;
+
+dictionary GPURenderBundleDescriptor
+ : GPUObjectDescriptorBase {
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPURenderBundleEncoder {
+ GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
+};
+GPURenderBundleEncoder includes GPUObjectBase;
+GPURenderBundleEncoder includes GPUCommandsMixin;
+GPURenderBundleEncoder includes GPUDebugCommandsMixin;
+GPURenderBundleEncoder includes GPUBindingCommandsMixin;
+GPURenderBundleEncoder includes GPURenderCommandsMixin;
+
+dictionary GPURenderBundleEncoderDescriptor
+ : GPURenderPassLayout {
+ boolean depthReadOnly = false;
+ boolean stencilReadOnly = false;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUQueue {
+ undefined submit(sequence<GPUCommandBuffer> commandBuffers);
+
+ Promise<undefined> onSubmittedWorkDone();
+
+ undefined writeBuffer(
+ GPUBuffer buffer,
+ GPUSize64 bufferOffset,
+ [AllowShared] BufferSource data,
+ optional GPUSize64 dataOffset = 0,
+ optional GPUSize64 size);
+
+ undefined writeTexture(
+ GPUImageCopyTexture destination,
+ [AllowShared] BufferSource data,
+ GPUImageDataLayout dataLayout,
+ GPUExtent3D size);
+};
+GPUQueue includes GPUObjectBase;
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUQuerySet {
+ undefined destroy();
+
+ readonly attribute GPUQueryType type;
+ readonly attribute GPUSize32Out count;
+};
+GPUQuerySet includes GPUObjectBase;
+
+dictionary GPUQuerySetDescriptor
+ : GPUObjectDescriptorBase {
+ required GPUQueryType type;
+ required GPUSize32 count;
+};
+
+enum GPUQueryType {
+ "occlusion",
+ "timestamp",
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUCanvasContext {
+ readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas;
+
+ undefined configure(GPUCanvasConfiguration configuration);
+ undefined unconfigure();
+
+ GPUTexture getCurrentTexture();
+};
+
+enum GPUCanvasAlphaMode {
+ "opaque",
+ "premultiplied",
+};
+
+dictionary GPUCanvasConfiguration {
+ required GPUDevice device;
+ required GPUTextureFormat format;
+ GPUTextureUsageFlags usage = 0x10; // GPUTextureUsage.RENDER_ATTACHMENT
+ sequence<GPUTextureFormat> viewFormats = [];
+ GPUCanvasAlphaMode alphaMode = "opaque";
+};
+
+enum GPUDeviceLostReason {
+ "unknown",
+ "destroyed",
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUDeviceLostInfo {
+ readonly attribute GPUDeviceLostReason reason;
+ readonly attribute DOMString message;
+};
+
+partial interface GPUDevice {
+ readonly attribute Promise<GPUDeviceLostInfo> lost;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUError {
+ readonly attribute DOMString message;
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUValidationError
+ : GPUError {
+ constructor(DOMString message);
+};
+
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUOutOfMemoryError
+ : GPUError {
+ constructor(DOMString message);
+};
+
+enum GPUErrorFilter {
+ "validation",
+ "out-of-memory",
+};
+
+partial interface GPUDevice {
+ undefined pushErrorScope(GPUErrorFilter filter);
+ Promise<GPUError?> popErrorScope();
+};
+
+partial interface GPUDevice {
+ [Exposed=(Window, DedicatedWorker)]
+ attribute EventHandler onuncapturederror;
+};
+
+typedef [EnforceRange] unsigned long GPUBufferDynamicOffset;
+typedef [EnforceRange] unsigned long GPUStencilValue;
+typedef [EnforceRange] unsigned long GPUSampleMask;
+typedef [EnforceRange] long GPUDepthBias;
+
+typedef [EnforceRange] unsigned long long GPUSize64;
+typedef [EnforceRange] unsigned long GPUIntegerCoordinate;
+typedef [EnforceRange] unsigned long GPUIndex32;
+typedef [EnforceRange] unsigned long GPUSize32;
+typedef [EnforceRange] long GPUSignedOffset32;
+
+typedef unsigned long long GPUSize64Out;
+typedef unsigned long GPUIntegerCoordinateOut;
+typedef unsigned long GPUSize32Out;
+
+typedef unsigned long GPUFlagsConstant;
+
+dictionary GPUColorDict {
+ required double r;
+ required double g;
+ required double b;
+ required double a;
+};
+typedef (sequence<double> or GPUColorDict) GPUColor;
+
+dictionary GPUOrigin2DDict {
+ GPUIntegerCoordinate x = 0;
+ GPUIntegerCoordinate y = 0;
+};
+typedef (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict) GPUOrigin2D;
+
+dictionary GPUOrigin3DDict {
+ GPUIntegerCoordinate x = 0;
+ GPUIntegerCoordinate y = 0;
+ GPUIntegerCoordinate z = 0;
+};
+typedef (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict) GPUOrigin3D;
+
+dictionary GPUExtent3DDict {
+ required GPUIntegerCoordinate width;
+ GPUIntegerCoordinate height = 1;
+ GPUIntegerCoordinate depthOrArrayLayers = 1;
+};
+typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index b8c012f63..39d907a61 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -57,6 +57,7 @@ deno_kv.workspace = true
deno_tls.workspace = true
deno_url.workspace = true
deno_web.workspace = true
+deno_webgpu.workspace = true
deno_webidl.workspace = true
deno_websocket.workspace = true
deno_webstorage.workspace = true
@@ -88,6 +89,7 @@ deno_node.workspace = true
deno_tls.workspace = true
deno_url.workspace = true
deno_web.workspace = true
+deno_webgpu.workspace = true
deno_webidl.workspace = true
deno_websocket.workspace = true
deno_webstorage.workspace = true
diff --git a/runtime/build.rs b/runtime/build.rs
index e0881836b..9975aecee 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -224,6 +224,7 @@ mod startup_snapshot {
Default::default(),
Default::default(),
),
+ deno_webgpu::deno_webgpu::init_ops_and_esm(),
deno_fetch::deno_fetch::init_ops_and_esm::<Permissions>(
Default::default(),
),
diff --git a/runtime/errors.rs b/runtime/errors.rs
index 2061a5e0b..c05e5bd62 100644
--- a/runtime/errors.rs
+++ b/runtime/errors.rs
@@ -167,6 +167,7 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
+ .or_else(|| deno_webgpu::error::get_error_class_name(e))
.or_else(|| deno_web::get_error_class_name(e))
.or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
.or_else(|| deno_websocket::get_network_error_class_name(e))
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index 83c4a4d03..0c771ba83 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -164,7 +164,8 @@ const unstableIds = {
kv: 6,
net: 7,
unsafeProto: 8,
- workerOptions: 9,
+ webgpu: 9,
+ workerOptions: 10,
};
const denoNsUnstableById = {};
@@ -216,6 +217,8 @@ denoNsUnstableById[unstableIds.net] = {
// denoNsUnstableById[unstableIds.unsafeProto] = {}
+// denoNsUnstableById[unstableIds.webgpu] = {}
+
// denoNsUnstableById[unstableIds.workerOptions] = {}
// when editing this list, also update unstableDenoProps in cli/tsc/99_main_compiler.js
diff --git a/runtime/js/98_global_scope.js b/runtime/js/98_global_scope.js
index 65a9c6933..d5ec700db 100644
--- a/runtime/js/98_global_scope.js
+++ b/runtime/js/98_global_scope.js
@@ -151,6 +151,45 @@ unstableForWindowOrWorkerGlobalScope[unstableIds.broadcastChannel] = {
unstableForWindowOrWorkerGlobalScope[unstableIds.net] = {
WebSocketStream: util.nonEnumerable(webSocketStream.WebSocketStream),
};
+unstableForWindowOrWorkerGlobalScope[unstableIds.webgpu] = {
+ GPU: webGPUNonEnumerable(() => webgpu.GPU),
+ GPUAdapter: webGPUNonEnumerable(() => webgpu.GPUAdapter),
+ GPUAdapterInfo: webGPUNonEnumerable(() => webgpu.GPUAdapterInfo),
+ GPUSupportedLimits: webGPUNonEnumerable(() => webgpu.GPUSupportedLimits),
+ GPUSupportedFeatures: webGPUNonEnumerable(() => webgpu.GPUSupportedFeatures),
+ GPUDeviceLostInfo: webGPUNonEnumerable(() => webgpu.GPUDeviceLostInfo),
+ GPUDevice: webGPUNonEnumerable(() => webgpu.GPUDevice),
+ GPUQueue: webGPUNonEnumerable(() => webgpu.GPUQueue),
+ GPUBuffer: webGPUNonEnumerable(() => webgpu.GPUBuffer),
+ GPUBufferUsage: webGPUNonEnumerable(() => webgpu.GPUBufferUsage),
+ GPUMapMode: webGPUNonEnumerable(() => webgpu.GPUMapMode),
+ GPUTextureUsage: webGPUNonEnumerable(() => webgpu.GPUTextureUsage),
+ GPUTexture: webGPUNonEnumerable(() => webgpu.GPUTexture),
+ GPUTextureView: webGPUNonEnumerable(() => webgpu.GPUTextureView),
+ GPUSampler: webGPUNonEnumerable(() => webgpu.GPUSampler),
+ GPUBindGroupLayout: webGPUNonEnumerable(() => webgpu.GPUBindGroupLayout),
+ GPUPipelineLayout: webGPUNonEnumerable(() => webgpu.GPUPipelineLayout),
+ GPUBindGroup: webGPUNonEnumerable(() => webgpu.GPUBindGroup),
+ GPUShaderModule: webGPUNonEnumerable(() => webgpu.GPUShaderModule),
+ GPUShaderStage: webGPUNonEnumerable(() => webgpu.GPUShaderStage),
+ GPUComputePipeline: webGPUNonEnumerable(() => webgpu.GPUComputePipeline),
+ GPURenderPipeline: webGPUNonEnumerable(() => webgpu.GPURenderPipeline),
+ GPUColorWrite: webGPUNonEnumerable(() => webgpu.GPUColorWrite),
+ GPUCommandEncoder: webGPUNonEnumerable(() => webgpu.GPUCommandEncoder),
+ GPURenderPassEncoder: webGPUNonEnumerable(() => webgpu.GPURenderPassEncoder),
+ GPUComputePassEncoder: webGPUNonEnumerable(() =>
+ webgpu.GPUComputePassEncoder
+ ),
+ GPUCommandBuffer: webGPUNonEnumerable(() => webgpu.GPUCommandBuffer),
+ GPURenderBundleEncoder: webGPUNonEnumerable(() =>
+ webgpu.GPURenderBundleEncoder
+ ),
+ GPURenderBundle: webGPUNonEnumerable(() => webgpu.GPURenderBundle),
+ GPUQuerySet: webGPUNonEnumerable(() => webgpu.GPUQuerySet),
+ GPUError: webGPUNonEnumerable(() => webgpu.GPUError),
+ GPUValidationError: webGPUNonEnumerable(() => webgpu.GPUValidationError),
+ GPUOutOfMemoryError: webGPUNonEnumerable(() => webgpu.GPUOutOfMemoryError),
+};
class Navigator {
constructor() {
@@ -190,7 +229,49 @@ const numCpus = memoizeLazy(() => ops.op_bootstrap_numcpus());
const userAgent = memoizeLazy(() => ops.op_bootstrap_user_agent());
const language = memoizeLazy(() => ops.op_bootstrap_language());
+let webgpu;
+
+function webGPUNonEnumerable(getter) {
+ let valueIsSet = false;
+ let value;
+
+ return {
+ get() {
+ loadWebGPU();
+
+ if (valueIsSet) {
+ return value;
+ } else {
+ return getter();
+ }
+ },
+ set(v) {
+ loadWebGPU();
+
+ valueIsSet = true;
+ value = v;
+ },
+ enumerable: false,
+ configurable: true,
+ };
+}
+
+function loadWebGPU() {
+ if (!webgpu) {
+ webgpu = ops.op_lazy_load_esm("ext:deno_webgpu/01_webgpu.js");
+ }
+}
+
ObjectDefineProperties(Navigator.prototype, {
+ gpu: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, NavigatorPrototype);
+ loadWebGPU();
+ return webgpu.gpu;
+ },
+ },
hardwareConcurrency: {
configurable: true,
enumerable: true,
@@ -251,6 +332,15 @@ class WorkerNavigator {
const workerNavigator = webidl.createBranded(WorkerNavigator);
ObjectDefineProperties(WorkerNavigator.prototype, {
+ gpu: {
+ configurable: true,
+ enumerable: true,
+ get() {
+ webidl.assertBranded(this, WorkerNavigatorPrototype);
+ loadWebGPU();
+ return webgpu.gpu;
+ },
+ },
hardwareConcurrency: {
configurable: true,
enumerable: true,
diff --git a/runtime/lib.rs b/runtime/lib.rs
index 6a0a6569c..1a97a3376 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -18,6 +18,7 @@ pub use deno_node;
pub use deno_tls;
pub use deno_url;
pub use deno_web;
+pub use deno_webgpu;
pub use deno_webidl;
pub use deno_websocket;
pub use deno_webstorage;
diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs
index a0f0665b4..c2e8b0df2 100644
--- a/runtime/snapshot.rs
+++ b/runtime/snapshot.rs
@@ -203,6 +203,7 @@ pub fn create_runtime_snapshot(
Default::default(),
Default::default(),
),
+ deno_webgpu::deno_webgpu::init_ops_and_esm(),
deno_fetch::deno_fetch::init_ops_and_esm::<Permissions>(Default::default()),
deno_cache::deno_cache::init_ops_and_esm::<SqliteBackedCache>(None),
deno_websocket::deno_websocket::init_ops_and_esm::<Permissions>(
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 4244f1045..b669b5c2a 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -408,6 +408,7 @@ impl WebWorker {
options.blob_store.clone(),
Some(main_module.clone()),
),
+ deno_webgpu::deno_webgpu::init_ops_and_esm(),
deno_fetch::deno_fetch::init_ops_and_esm::<PermissionsContainer>(
deno_fetch::Options {
user_agent: options.bootstrap.user_agent.clone(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 2a7e82c54..0bfb9305c 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -307,6 +307,7 @@ impl MainWorker {
options.blob_store.clone(),
options.bootstrap.location.clone(),
),
+ deno_webgpu::deno_webgpu::init_ops_and_esm(),
deno_fetch::deno_fetch::init_ops_and_esm::<PermissionsContainer>(
deno_fetch::Options {
user_agent: options.bootstrap.user_agent.clone(),
diff --git a/tools/README.md b/tools/README.md
index f30e16b8f..012302733 100644
--- a/tools/README.md
+++ b/tools/README.md
@@ -31,6 +31,19 @@ executable
cargo run -- run --allow-read --allow-write --allow-run --unstable ./tools/<script>
```
+## wgpu_sync.js
+
+`wgpu_sync.js` streamlines updating `deno_webgpu` from
+[gfx-rs/wgpu](https://github.com/gfx-rs/wgpu/).
+
+It essentially vendors the `deno_webgpu` tree with a few minor patches applied
+on top, somewhat similar to `git subtree`.
+
+1. Update `COMMIT` or `V_WGPU` in `./tools/wgpu_sync.js`
+2. Run `./tools/wgpu_sync.js`
+3. Double check changes, possibly patch
+4. Commit & send a PR with the updates
+
## copyright_checker.js
`copyright_checker.js` is used to check copyright headers in the codebase.
diff --git a/tools/wgpu_sync.js b/tools/wgpu_sync.js
new file mode 100644
index 000000000..590324465
--- /dev/null
+++ b/tools/wgpu_sync.js
@@ -0,0 +1,92 @@
+#!/usr/bin/env -S deno run --unstable --allow-read --allow-write --allow-run
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+import { join, ROOT_PATH } from "./util.js";
+
+const COMMIT = "49b7ec97c164bac9ee877f45cdd806fbefecc5a4";
+const REPO = "gfx-rs/wgpu";
+const V_WGPU = "0.18";
+const TARGET_DIR = join(ROOT_PATH, "ext", "webgpu");
+
+async function bash(subcmd, opts = {}) {
+ const { success, code } = await new Deno.Command("bash", {
+ ...opts,
+ args: ["-c", subcmd],
+ stdout: "inherit",
+ sdterr: "inherit",
+ }).output();
+
+ // Exit process on failure
+ if (!success) {
+ Deno.exit(code);
+ }
+}
+
+async function clearTargetDir() {
+ await bash(`rm -r ${TARGET_DIR}/*`);
+}
+
+async function checkoutUpstream() {
+ // Path of deno_webgpu inside the TAR
+ const tarPrefix = `${REPO.replace("/", "-")}-${
+ COMMIT.slice(0, 7)
+ }/deno_webgpu/`;
+ const cmd =
+ `curl -L https://api.github.com/repos/${REPO}/tarball/${COMMIT} | tar -C '${TARGET_DIR}' -xzvf - --strip=2 '${tarPrefix}'`;
+ // console.log(cmd);
+ await bash(cmd);
+}
+
+async function denoWebgpuVersion() {
+ const coreCargo = join(ROOT_PATH, "Cargo.toml");
+ const contents = await Deno.readTextFile(coreCargo);
+ return contents.match(
+ /^deno_webgpu = { version = "(\d+\.\d+\.\d+)", path = ".\/ext\/webgpu" }$/m,
+ )[1];
+}
+
+async function patchFile(path, patcher) {
+ const data = await Deno.readTextFile(path);
+ const patched = patcher(data);
+ await Deno.writeTextFile(path, patched);
+}
+
+async function patchCargo() {
+ const vDenoWebgpu = await denoWebgpuVersion();
+ await patchFile(
+ join(TARGET_DIR, "Cargo.toml"),
+ (data) =>
+ data
+ .replace(/^version = .*/m, `version = "${vDenoWebgpu}"`)
+ .replace(
+ /^repository.workspace = true/m,
+ `repository = "https://github.com/gfx-rs/wgpu"`,
+ )
+ .replace(
+ /^serde = { workspace = true, features = ["derive"] }/m,
+ `serde.workspace = true`,
+ )
+ .replace(
+ /^tokio = { workspace = true, features = ["full"] }/m,
+ `tokio.workspace = true`,
+ ),
+ );
+
+ await patchFile(
+ join(ROOT_PATH, "Cargo.toml"),
+ (data) =>
+ data
+ .replace(/^wgpu-core = .*/m, `wgpu-core = "${V_WGPU}"`)
+ .replace(/^wgpu-types = .*/m, `wgpu-types = "${V_WGPU}"`)
+ .replace(/^wgpu-hal = .*/m, `wgpu-hal = "${V_WGPU}"`),
+ );
+}
+
+async function main() {
+ await clearTargetDir();
+ await checkoutUpstream();
+ await patchCargo();
+ await bash(join(ROOT_PATH, "tools", "format.js"));
+}
+
+await main();