From 393abed3873d83019feb5bcebb10a6929133862a Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Sat, 9 Dec 2023 01:19:16 +0100 Subject: [PATCH] feat: bring back WebGPU (#20812) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Leo Kettmeir Co-authored-by: Kenta Moriuchi Co-authored-by: Bartek Iwańczuk --- .cargo/config.toml | 11 +- Cargo.lock | 630 +- Cargo.toml | 8 + cli/args/flags.rs | 1 + cli/build.rs | 9 + cli/js/40_testing.js | 5 +- cli/main.rs | 7 +- cli/tests/integration/js_unit_tests.rs | 1 + cli/tests/integration/run_tests.rs | 11 + cli/tests/integration/shared_library_tests.rs | 27 +- .../testdata/run/unstable_webgpu.disabled.out | 2 + .../testdata/run/unstable_webgpu.enabled.out | 2 + cli/tests/testdata/run/unstable_webgpu.js | 10 + .../testdata/webgpu/computepass_shader.wgsl | 38 + cli/tests/testdata/webgpu/hellotriangle.out | Bin 0 -> 204800 bytes .../testdata/webgpu/hellotriangle_shader.wgsl | 11 + cli/tests/unit/webgpu_test.ts | 242 + cli/tsc/dts/lib.deno.window.d.ts | 2 + cli/tsc/dts/lib.deno.worker.d.ts | 1 + cli/tsc/dts/lib.deno_webgpu.d.ts | 1315 +++ cli/tsc/mod.rs | 1 + ext/webgpu/01_webgpu.js | 7087 +++++++++++++++++ ext/webgpu/02_surface.js | 235 + ext/webgpu/Cargo.toml | 49 + ext/webgpu/LICENSE.md | 20 + ext/webgpu/README.md | 35 + ext/webgpu/binding.rs | 340 + ext/webgpu/buffer.rs | 205 + ext/webgpu/bundle.rs | 405 + ext/webgpu/command_encoder.rs | 633 ++ ext/webgpu/compute_pass.rs | 225 + ext/webgpu/error.rs | 316 + ext/webgpu/lib.rs | 768 ++ ext/webgpu/pipeline.rs | 453 ++ ext/webgpu/queue.rs | 131 + ext/webgpu/render_pass.rs | 519 ++ ext/webgpu/sampler.rs | 80 + ext/webgpu/shader.rs | 55 + ext/webgpu/surface.rs | 133 + ext/webgpu/texture.rs | 134 + ext/webgpu/webgpu.idl | 1233 +++ runtime/Cargo.toml | 2 + runtime/build.rs | 1 + runtime/errors.rs | 1 + runtime/js/90_deno_ns.js | 5 +- runtime/js/98_global_scope.js | 90 + runtime/lib.rs | 1 + runtime/snapshot.rs | 1 + runtime/web_worker.rs | 1 + runtime/worker.rs | 1 + tools/README.md | 13 + tools/wgpu_sync.js | 92 + 52 files changed, 15491 insertions(+), 107 deletions(-) create mode 100644 cli/tests/testdata/run/unstable_webgpu.disabled.out create mode 100644 cli/tests/testdata/run/unstable_webgpu.enabled.out create mode 100644 cli/tests/testdata/run/unstable_webgpu.js create mode 100644 cli/tests/testdata/webgpu/computepass_shader.wgsl create mode 100644 cli/tests/testdata/webgpu/hellotriangle.out create mode 100644 cli/tests/testdata/webgpu/hellotriangle_shader.wgsl create mode 100644 cli/tests/unit/webgpu_test.ts create mode 100644 cli/tsc/dts/lib.deno_webgpu.d.ts create mode 100644 ext/webgpu/01_webgpu.js create mode 100644 ext/webgpu/02_surface.js create mode 100644 ext/webgpu/Cargo.toml create mode 100644 ext/webgpu/LICENSE.md create mode 100644 ext/webgpu/README.md create mode 100644 ext/webgpu/binding.rs create mode 100644 ext/webgpu/buffer.rs create mode 100644 ext/webgpu/bundle.rs create mode 100644 ext/webgpu/command_encoder.rs create mode 100644 ext/webgpu/compute_pass.rs create mode 100644 ext/webgpu/error.rs create mode 100644 ext/webgpu/lib.rs create mode 100644 ext/webgpu/pipeline.rs create mode 100644 ext/webgpu/queue.rs create mode 100644 ext/webgpu/render_pass.rs create mode 100644 ext/webgpu/sampler.rs create mode 100644 ext/webgpu/shader.rs create mode 100644 ext/webgpu/surface.rs create mode 100644 ext/webgpu/texture.rs create mode 100644 ext/webgpu/webgpu.idl create mode 100644 tools/wgpu_sync.js diff --git a/.cargo/config.toml b/.cargo/config.toml index 590f033582..f5b2f124bc 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 7f84bf380b..ee1be7d032 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,6 +127,15 @@ version = "0.2.16" 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" @@ -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" @@ -597,6 +627,16 @@ dependencies = [ "cc", ] +[[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" @@ -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" @@ -778,6 +829,17 @@ dependencies = [ "syn 2.0.39", ] +[[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" @@ -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", @@ -1658,6 +1721,19 @@ dependencies = [ "windows-sys 0.48.0", ] +[[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" @@ -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 = "form_urlencoded" -version = "1.2.0" +name = "foreign-types" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] [[package]] name = "glibc_version" @@ -2549,6 +2663,80 @@ version = "0.3.1" 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" @@ -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]] @@ -2622,6 +2810,12 @@ version = "0.4.3" 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" @@ -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", ] @@ -3029,6 +3223,23 @@ dependencies = [ "signature", ] +[[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" @@ -3116,6 +3327,16 @@ dependencies = [ "winapi", ] +[[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" @@ -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" @@ -3199,6 +3420,15 @@ dependencies = [ "url", ] +[[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" @@ -3260,6 +3490,21 @@ dependencies = [ "autocfg", ] +[[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" @@ -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", @@ -3305,6 +3550,27 @@ version = "0.8.3" 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" @@ -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]] @@ -3506,6 +3772,25 @@ dependencies = [ "libc", ] +[[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" @@ -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" @@ -3662,6 +3947,12 @@ dependencies = [ "subtle", ] +[[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" @@ -3860,6 +4151,12 @@ version = "0.2.17" 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" @@ -3938,13 +4235,19 @@ 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" @@ -4104,6 +4407,18 @@ dependencies = [ "getrandom", ] +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.2.16" @@ -4113,15 +4428,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -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 = "rsa" -version = "0.9.3" +name = "ron" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +dependencies = [ + "base64 0.21.5", + "bitflags 2.4.1", + "serde", + "serde_derive", +] + +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +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", @@ -4739,6 +5057,15 @@ dependencies = [ "autocfg", ] +[[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" @@ -4831,10 +5158,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] -name = "spki" -version = "0.7.2" +name = "spirv" +version = "0.2.0+1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" +dependencies = [ + "bitflags 1.3.2", + "num-traits", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +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 = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" +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 = "0d5ed5f0edf0de351fe311c53304986315ce866f394a2e6df0c4b3c70774bcdd" +dependencies = [ + "bitflags 2.4.1", + "js-sys", + "serde", + "web-sys", +] [[package]] name = "which" @@ -6270,6 +6685,25 @@ version = "0.4.0" 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" @@ -6459,6 +6893,12 @@ dependencies = [ "libc", ] +[[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" @@ -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 f3a02cd5ec..555fdf7cb8 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 9d7e513f72..8b4fa445e2 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 d7f373b99d..4adeba9443 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 5b51ac1692..5ceff938c1 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 c95e5dc37d..321f32976f 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -322,10 +322,15 @@ pub(crate) static UNSTABLE_GRANULAR_FLAGS: &[( // for "unstableFeatures" to see where it's used. 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 165ab25bf4..10bd137d9c 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 c96d68b937..32df04483a 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 506c537f6c..3e05f8efcb 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 0000000000..7758663527 --- /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 0000000000..e2cc915ba2 --- /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 0000000000..a796b1c4d7 --- /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 0000000000..41af4363a2 --- /dev/null +++ b/cli/tests/testdata/webgpu/computepass_shader.wgsl @@ -0,0 +1,38 @@ +@group(0) +@binding(0) +var v_indices: array; // 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) { + 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 0000000000000000000000000000000000000000..52972ec9e05fbb995e2efd1ed651077d4cd226a8 GIT binary patch literal 204800 zcmeIu0Sy2E0K%a6Pi+qe5hx58Fkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj zFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r z3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@ z0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VK zfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5 zV8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM z7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b* z1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd z0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwA zz<>b*1`HT5V8DO@0|pEjFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*1`HT5V8DO@0|pEj fFkrxd0RsjM7%*VKfB^#r3>YwAz<>b*2A&26AT0m^ literal 0 HcmV?d00001 diff --git a/cli/tests/testdata/webgpu/hellotriangle_shader.wgsl b/cli/tests/testdata/webgpu/hellotriangle_shader.wgsl new file mode 100644 index 0000000000..f84ccfe94d --- /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 { + let x = f32(i32(in_vertex_index) - 1); + let y = f32(i32(in_vertex_index & 1u) * 2 - 1); + return vec4(x, y, 0.0, 1.0); +} + +@fragment +fn fs_main() -> @location(0) vec4 { + return vec4(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 0000000000..2d98167cfd --- /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 58b57e52ca..0e6acf1e7b 100644 --- a/cli/tsc/dts/lib.deno.window.d.ts +++ b/cli/tsc/dts/lib.deno.window.d.ts @@ -3,6 +3,7 @@ /// /// /// +/// /// /// /// @@ -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 b165f2086b..1d95dd483e 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 0000000000..9545fdc9e2 --- /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 + +/// +/// + +/** @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, + ) => void, + thisArg?: any, + ): void; + has(value: GPUFeatureName): boolean; + size: number; + [Symbol.iterator](): IterableIterator; + entries(): IterableIterator<[GPUFeatureName, GPUFeatureName]>; + keys(): IterableIterator; + values(): IterableIterator; +} + +/** @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; +} + +/** @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; + requestAdapterInfo(unmaskHints?: string[]): Promise; +} + +/** @category WebGPU */ +declare interface GPUDeviceDescriptor extends GPUObjectDescriptorBase { + requiredFeatures?: GPUFeatureName[]; + requiredLimits?: Record; +} + +/** @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; + pushErrorScope(filter: GPUErrorFilter): undefined; + popErrorScope(): Promise; + + 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; + createRenderPipelineAsync( + descriptor: GPURenderPipelineDescriptor, + ): Promise; + + 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; + 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; +} + +/** @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; + + 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 0c3a5602ee..2a820a6eea 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 0000000000..264eaa4433 --- /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 +/// +/// +/// +/// + +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} + */ + 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} + */ + async requestAdapterInfo(unmaskHints = []) { + webidl.assertBranded(this, GPUAdapterPrototype); + const prefix = "Failed to execute 'requestAdapterInfo' on 'GPUAdapter'"; + unmaskHints = webidl.converters["sequence"]( + 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[]} 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[]} */ + resources; + /** @type {boolean} */ + isLost; + /** @type {Promise} */ + 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} + */ + // 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"]( + 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[]} */ + [_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[]} */ + [_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"]( + 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"] = 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 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"](V, opts); + } + return webidl.converters["GPUExtent3DDict"](V, opts); + } + throw webidl.makeException( + TypeError, + "can not be converted to sequence 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"] = 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"], + }, +]; +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"] = 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 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"](V, opts); + } + return webidl.converters["GPUOrigin3DDict"](V, opts); + } + throw webidl.makeException( + TypeError, + "can not be converted to sequence 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 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"](V, opts); + } + return webidl.converters["GPUOrigin2DDict"](V, opts); + } + throw webidl.makeException( + TypeError, + "can not be converted to sequence 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 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"](V, opts); + } + return webidl.converters["GPUColorDict"](V, opts); + } + throw webidl.makeException( + TypeError, + "can not be converted to sequence 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"] = 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 0000000000..515f2eec1f --- /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 +/// +/// +/// +/// + +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 0000000000..b3f68d1d94 --- /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 0000000000..aec557f3a0 --- /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 0000000000..f5ac0d0b5d --- /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 0000000000..81d1f7e8c2 --- /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 { + "webGPUBindGroupLayout".into() + } + + fn close(self: Rc) { + 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 { + "webGPUBindGroup".into() + } + + fn close(self: Rc) { + 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 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 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 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 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, + #[serde] entries: Vec, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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::>(); + + 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, + #[serde] bind_group_layouts: Vec, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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::(rid)?; + Ok(bind_group_layout.1) + }) + .collect::, 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, + size: Option, +} + +#[op2] +#[serde] +pub fn op_webgpu_create_bind_group( + state: &mut OpState, + #[smi] device_rid: ResourceId, + #[string] label: Cow, + #[smi] layout: ResourceId, + #[serde] entries: Vec, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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::(entry.resource)?; + wgpu_core::binding_model::BindingResource::Sampler( + sampler_resource.1, + ) + } + "GPUTextureView" => { + let texture_view_resource = + state + .resource_table + .get::(entry.resource)?; + wgpu_core::binding_model::BindingResource::TextureView( + texture_view_resource.1, + ) + } + "GPUBufferBinding" => { + let buffer_resource = + state + .resource_table + .get::(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::, AnyError>>()?; + + let bind_group_layout = + state.resource_table.get::(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 0000000000..2f97248253 --- /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 { + "webGPUBuffer".into() + } + + fn close(self: Rc) { + 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 { + "webGPUBufferMapped".into() + } +} + +#[op2] +#[serde] +pub fn op_webgpu_create_buffer( + state: &mut OpState, + #[smi] device_rid: ResourceId, + #[string] label: Cow, + #[number] size: u64, + usage: u32, + mapped_at_creation: bool, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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>, + #[smi] buffer_rid: ResourceId, + #[smi] device_rid: ResourceId, + mode: u32, + #[number] offset: u64, + #[number] size: u64, +) -> Result { + let (sender, receiver) = oneshot::channel::(); + + let device; + { + let state_ = state.borrow(); + let instance = state_.borrow::(); + let buffer_resource = + state_.resource_table.get::(buffer_rid)?; + let buffer = buffer_resource.1; + let device_resource = state_ + .resource_table + .get::(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::(); + 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, + #[buffer] buf: &mut [u8], +) -> Result { + let instance = state.borrow::(); + let buffer_resource = state.resource_table.get::(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 { + let mapped_resource = state + .resource_table + .take::(mapped_rid)?; + let instance = state.borrow::(); + let buffer_resource = state.resource_table.get::(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 0000000000..59b7c8965f --- /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, +); +impl Resource for WebGpuRenderBundleEncoder { + fn name(&self) -> Cow { + "webGPURenderBundleEncoder".into() + } +} + +pub(crate) struct WebGpuRenderBundle( + pub(crate) super::Instance, + pub(crate) wgpu_core::id::RenderBundleId, +); +impl Resource for WebGpuRenderBundle { + fn name(&self) -> Cow { + "webGPURenderBundle".into() + } + + fn close(self: Rc) { + 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>, + depth_stencil_format: Option, + 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 { + let device_resource = state + .resource_table + .get::(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, +) -> Result { + let render_bundle_encoder_resource = + state + .resource_table + .take::(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::(); + + 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 { + let bind_group_resource = + state + .resource_table + .get::(bind_group)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let render_pipeline_resource = + state + .resource_table + .get::(pipeline)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let buffer_resource = state + .resource_table + .get::(buffer)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(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, +) -> Result { + let buffer_resource = state + .resource_table + .get::(buffer)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 { + let buffer_resource = state + .resource_table + .get::(indirect_buffer)?; + let render_bundle_encoder_resource = + state + .resource_table + .get::(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 0000000000..c5947abafa --- /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 { + "webGPUCommandEncoder".into() + } + + fn close(self: Rc) { + 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>, +); +impl Resource for WebGpuCommandBuffer { + fn name(&self) -> Cow { + "webGPUCommandBuffer".into() + } + + fn close(self: Rc) { + 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, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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, + clear_value: Option, + 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, + depth_store_op: Option, + depth_read_only: bool, + stencil_clear_value: u32, + stencil_load_op: Option, + stencil_store_op: Option, + stencil_read_only: bool, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GPURenderPassTimestampWrites { + query_set: ResourceId, + beginning_of_pass_write_index: Option, + end_of_pass_write_index: Option, +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_begin_render_pass( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[string] label: Cow, + #[serde] color_attachments: Vec>, + #[serde] depth_stencil_attachment: Option< + GpuRenderPassDepthStencilAttachment, + >, + #[smi] occlusion_query_set: Option, + #[serde] timestamp_writes: Option, +) -> Result { + let command_encoder_resource = state + .resource_table + .get::(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::(at.view)?; + + let resolve_target = at + .resolve_target + .map(|rid| { + state + .resource_table + .get::(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::, AnyError>>()?; + + let mut processed_depth_stencil_attachment = None; + + if let Some(attachment) = depth_stencil_attachment { + let texture_view_resource = + state + .resource_table + .get::(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::(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::(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, + end_of_pass_write_index: Option, +} + +#[op2] +#[serde] +pub fn op_webgpu_command_encoder_begin_compute_pass( + state: &mut OpState, + #[smi] command_encoder_rid: ResourceId, + #[string] label: Cow, + #[serde] timestamp_writes: Option, +) -> Result { + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + + let timestamp_writes = if let Some(timestamp_writes) = timestamp_writes { + let query_set_resource = state + .resource_table + .get::(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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_buffer_resource = state + .resource_table + .get::(source)?; + let source_buffer = source_buffer_resource.1; + let destination_buffer_resource = + state + .resource_table + .get::(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, + rows_per_image: Option, +} + +#[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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_buffer_resource = + state + .resource_table + .get::(source.buffer)?; + let destination_texture_resource = + state + .resource_table + .get::(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, + ©_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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_texture_resource = + state + .resource_table + .get::(source.texture)?; + let destination_buffer_resource = + state + .resource_table + .get::(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, + ©_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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let source_texture_resource = + state + .resource_table + .get::(source.texture)?; + let destination_texture_resource = + state + .resource_table + .get::(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, + ©_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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let destination_resource = state + .resource_table + .get::(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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let query_set_resource = state + .resource_table + .get::(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 { + let instance = state.borrow::(); + let command_encoder_resource = state + .resource_table + .get::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let query_set_resource = state + .resource_table + .get::(query_set)?; + let destination_resource = state + .resource_table + .get::(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, +) -> Result { + let command_encoder_resource = state + .resource_table + .take::(command_encoder_rid)?; + let command_encoder = command_encoder_resource.1; + let instance = state.borrow::(); + + 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 0000000000..6dbf160206 --- /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, +); +impl Resource for WebGpuComputePass { + fn name(&self) -> Cow { + "webGPUComputePass".into() + } +} + +#[op2] +#[serde] +pub fn op_webgpu_compute_pass_set_pipeline( + state: &mut OpState, + #[smi] compute_pass_rid: ResourceId, + #[smi] pipeline: ResourceId, +) -> Result { + let compute_pipeline_resource = + state + .resource_table + .get::(pipeline)?; + let compute_pass_resource = state + .resource_table + .get::(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 { + let compute_pass_resource = state + .resource_table + .get::(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 { + let buffer_resource = state + .resource_table + .get::(indirect_buffer)?; + let compute_pass_resource = state + .resource_table + .get::(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 { + let command_encoder_resource = state + .resource_table + .get::( + command_encoder_rid, + )?; + let command_encoder = command_encoder_resource.1; + let compute_pass_resource = state + .resource_table + .take::(compute_pass_rid)?; + let compute_pass = &compute_pass_resource.0.borrow(); + let instance = state.borrow::(); + + 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 { + let bind_group_resource = + state + .resource_table + .get::(bind_group)?; + let compute_pass_resource = state + .resource_table + .get::(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 { + let compute_pass_resource = state + .resource_table + .get::(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 { + let compute_pass_resource = state + .resource_table + .get::(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 { + let compute_pass_resource = state + .resource_table + .get::(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 0000000000..53ead09e5c --- /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, + pub err: Option, +} + +impl WebGpuResult { + pub fn rid(rid: ResourceId) -> Self { + Self { + rid: Some(rid), + err: None, + } + } + + pub fn rid_err>( + rid: ResourceId, + err: Option, + ) -> Self { + Self { + rid: Some(rid), + err: err.map(Into::into), + } + } + + pub fn maybe_err>(err: Option) -> 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 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 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 for WebGpuError { + fn from(err: BufferAccessError) -> Self { + match err { + BufferAccessError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: CreateBindGroupLayoutError) -> Self { + match err { + CreateBindGroupLayoutError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: CreatePipelineLayoutError) -> Self { + match err { + CreatePipelineLayoutError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: CreateBindGroupError) -> Self { + match err { + CreateBindGroupError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: RenderBundleError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: CreateRenderBundleError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: CopyError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: CommandEncoderError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: QueryError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: ComputePassError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: CreateComputePipelineError) -> Self { + match err { + CreateComputePipelineError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: GetBindGroupLayoutError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: CreateRenderPipelineError) -> Self { + match err { + CreateRenderPipelineError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: RenderPassError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: CreateSamplerError) -> Self { + match err { + CreateSamplerError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: CreateShaderModuleError) -> Self { + match err { + CreateShaderModuleError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: CreateTextureError) -> Self { + match err { + CreateTextureError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: CreateTextureViewError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +impl From for WebGpuError { + fn from(err: CreateQuerySetError) -> Self { + match err { + CreateQuerySetError::Device(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: QueueSubmitError) -> Self { + match err { + QueueSubmitError::Queue(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: QueueWriteError) -> Self { + match err { + QueueWriteError::Queue(err) => err.into(), + err => WebGpuError::Validation(fmt_err(&err)), + } + } +} + +impl From for WebGpuError { + fn from(err: ClearError) -> Self { + WebGpuError::Validation(fmt_err(&err)) + } +} + +#[cfg(feature = "surface")] +impl From 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::() + .map(|_| "DOMExceptionOperationError") +} diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs new file mode 100644 index 0000000000..0d0ff633da --- /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::( $($param),* ), + #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))] + wgpu_types::Backend::Metal => $global.$method::( $($param),* ), + #[cfg(all(not(target_arch = "wasm32"), windows))] + wgpu_types::Backend::Dx12 => $global.$method::( $($param),* ), + #[cfg(all(not(target_arch = "wasm32"), windows))] + wgpu_types::Backend::Dx11 => $global.$method::( $($param),* ), + #[cfg(any( + all(unix, not(target_os = "macos"), not(target_os = "ios")), + feature = "angle", + target_arch = "wasm32" + ))] + wgpu_types::Backend::Gl => $global.$method::( $($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, +>; + +struct WebGpuAdapter(Instance, wgpu_core::id::AdapterId); +impl Resource for WebGpuAdapter { + fn name(&self) -> Cow { + "webGPUAdapter".into() + } + + fn close(self: Rc) { + 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 { + "webGPUDevice".into() + } + + fn close(self: Rc) { + 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 { + "webGPUQuerySet".into() + } + + fn close(self: Rc) { + 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>, + #[serde] power_preference: Option, + force_fallback_adapter: bool, +) -> Result { + 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 + } 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::() + }; + + 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); + +impl From 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>, + #[smi] adapter_rid: ResourceId, + #[string] label: String, + #[serde] required_features: GpuRequiredFeatures, + #[serde] required_limits: Option, +) -> Result { + let mut state = state.borrow_mut(); + let adapter_resource = + state.resource_table.get::(adapter_rid)?; + let adapter = adapter_resource.1; + let instance = state.borrow::(); + + 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>, + #[smi] adapter_rid: ResourceId, +) -> Result { + let state = state.borrow_mut(); + let adapter_resource = + state.resource_table.get::(adapter_rid)?; + let adapter = adapter_resource.1; + let instance = state.borrow::(); + + 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 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 { + let device_resource = + state.resource_table.get::(args.device_rid)?; + let device = device_resource.1; + let instance = state.borrow::(); + + 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 0000000000..9fa975c215 --- /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 { + "webGPUPipelineLayout".into() + } + + fn close(self: Rc) { + 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 { + "webGPUComputePipeline".into() + } + + fn close(self: Rc) { + 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 { + "webGPURenderPipeline".into() + } + + fn close(self: Rc) { + 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 +} + +#[op2] +#[serde] +pub fn op_webgpu_create_compute_pipeline( + state: &mut OpState, + #[smi] device_rid: ResourceId, + #[string] label: Cow, + #[serde] layout: GPUPipelineLayoutOrGPUAutoLayoutMode, + #[serde] compute: GpuProgrammableStage, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(device_rid)?; + let device = device_resource.1; + + let pipeline_layout = match layout { + GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => { + let id = state.resource_table.get::(rid)?; + Some(id.1) + } + GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None, + }; + + let compute_shader_module_resource = + state + .resource_table + .get::(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, +} + +#[op2] +#[serde] +pub fn op_webgpu_compute_pipeline_get_bind_group_layout( + state: &mut OpState, + #[smi] compute_pipeline_rid: ResourceId, + index: u32, +) -> Result { + let instance = state.borrow::(); + let compute_pipeline_resource = state + .resource_table + .get::(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 for Option { + fn from(value: GpuCullMode) -> Option { + 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, + front_face: wgpu_types::FrontFace, + cull_mode: GpuCullMode, + unclipped_depth: bool, +} + +impl From 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 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, +} + +impl<'a> From + 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>, +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct GpuMultisampleState { + count: u32, + mask: u64, + alpha_to_coverage_enabled: bool, +} + +impl From 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>, + 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, + multisample: wgpu_types::MultisampleState, + fragment: Option, +} + +#[op2] +#[serde] +pub fn op_webgpu_create_render_pipeline( + state: &mut OpState, + #[serde] args: CreateRenderPipelineArgs, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(args.device_rid)?; + let device = device_resource.1; + + let layout = match args.layout { + GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => { + let pipeline_layout_resource = + state.resource_table.get::(rid)?; + Some(pipeline_layout_resource.1) + } + GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None, + }; + + let vertex_shader_module_resource = + state + .resource_table + .get::(args.vertex.module)?; + + let fragment = if let Some(fragment) = args.fragment { + let fragment_shader_module_resource = + state + .resource_table + .get::(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 { + let instance = state.borrow::(); + let render_pipeline_resource = state + .resource_table + .get::(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 0000000000..9e878c7f26 --- /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, +) -> Result { + let instance = state.borrow::(); + let queue_resource = state.resource_table.get::(queue_rid)?; + let queue = queue_resource.1; + + let ids = command_buffers + .iter() + .map(|rid| { + let buffer_resource = + state.resource_table.get::(*rid)?; + let mut id = buffer_resource.1.borrow_mut(); + Ok(id.take().unwrap()) + }) + .collect::, AnyError>>()?; + + let maybe_err = + gfx_select!(queue => instance.queue_submit(queue, &ids)).err(); + + for rid in command_buffers { + let resource = state.resource_table.take::(rid)?; + resource.close(); + } + + Ok(WebGpuResult::maybe_err(maybe_err)) +} + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct GpuImageDataLayout { + offset: u64, + bytes_per_row: Option, + rows_per_image: Option, +} + +impl From 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, + #[buffer] buf: &[u8], +) -> Result { + let instance = state.borrow::(); + let buffer_resource = state + .resource_table + .get::(buffer)?; + let buffer = buffer_resource.1; + let queue_resource = state.resource_table.get::(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 { + let instance = state.borrow::(); + let texture_resource = state + .resource_table + .get::(destination.texture)?; + let queue_resource = state.resource_table.get::(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 0000000000..abb62a0be6 --- /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, +); +impl Resource for WebGpuRenderPass { + fn name(&self) -> Cow { + "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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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, +) -> Result { + let bundles = bundles + .iter() + .map(|rid| { + let render_bundle_resource = + state + .resource_table + .get::(*rid)?; + Ok(render_bundle_resource.1) + }) + .collect::, AnyError>>()?; + + let render_pass_resource = state + .resource_table + .get::(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 { + let command_encoder_resource = state + .resource_table + .get::( + command_encoder_rid, + )?; + let command_encoder = command_encoder_resource.1; + let render_pass_resource = state + .resource_table + .take::(render_pass_rid)?; + let render_pass = &render_pass_resource.0.borrow(); + let instance = state.borrow::(); + + 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 { + let bind_group_resource = + state + .resource_table + .get::(bind_group)?; + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pipeline_resource = + state + .resource_table + .get::(pipeline)?; + let render_pass_resource = state + .resource_table + .get::(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, +) -> Result { + let buffer_resource = state + .resource_table + .get::(buffer)?; + let render_pass_resource = state + .resource_table + .get::(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, +) -> Result { + let buffer_resource = state + .resource_table + .get::(buffer)?; + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let render_pass_resource = state + .resource_table + .get::(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 { + let buffer_resource = state + .resource_table + .get::(indirect_buffer)?; + let render_pass_resource = state + .resource_table + .get::(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 { + let buffer_resource = state + .resource_table + .get::(indirect_buffer)?; + let render_pass_resource = state + .resource_table + .get::(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 0000000000..3280a548a2 --- /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 { + "webGPUSampler".into() + } + + fn close(self: Rc) { + 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, + max_anisotropy: u16, +} + +#[op2] +#[serde] +pub fn op_webgpu_create_sampler( + state: &mut OpState, + #[serde] args: CreateSamplerArgs, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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 0000000000..a6850d55a0 --- /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 { + "webGPUShaderModule".into() + } + + fn close(self: Rc) { + 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, + #[string] code: Cow, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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 0000000000..4e01717a65 --- /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 { + "webGPUSurface".into() + } + + fn close(self: Rc) { + 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, + alpha_mode: wgpu_types::CompositeAlphaMode, + view_formats: Vec, +} + +#[op2] +#[serde] +pub fn op_webgpu_surface_configure( + state: &mut OpState, + #[serde] args: SurfaceConfigureArgs, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(args.device_rid)?; + let device = device_resource.1; + let surface_resource = state + .resource_table + .get::(args.surface_rid)?; + let surface = surface_resource.1; + + let conf = wgpu_types::SurfaceConfiguration::> { + 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 { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(device_rid)?; + let device = device_resource.1; + let surface_resource = + state.resource_table.get::(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::(); + let device_resource = state + .resource_table + .get::(device_rid)?; + let device = device_resource.1; + let surface_resource = + state.resource_table.get::(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 0000000000..81a11a348f --- /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 { + "webGPUTexture".into() + } + + fn close(self: Rc) { + 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 { + "webGPUTextureView".into() + } + + fn close(self: Rc) { + 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, +} + +#[op2] +#[serde] +pub fn op_webgpu_create_texture( + state: &mut OpState, + #[serde] args: CreateTextureArgs, +) -> Result { + let instance = state.borrow::(); + let device_resource = state + .resource_table + .get::(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, + dimension: Option, + #[serde(flatten)] + range: wgpu_types::ImageSubresourceRange, +} + +#[op2] +#[serde] +pub fn op_webgpu_create_texture_view( + state: &mut OpState, + #[serde] args: CreateTextureViewArgs, +) -> Result { + let instance = state.borrow::(); + let texture_resource = state + .resource_table + .get::(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 0000000000..0b6b04eb4e --- /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; +}; + +[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 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 requestDevice(optional GPUDeviceDescriptor descriptor = {}); + Promise requestAdapterInfo(optional sequence unmaskHints = []); +}; + +dictionary GPUDeviceDescriptor + : GPUObjectDescriptorBase { + sequence requiredFeatures = []; + record 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 createComputePipelineAsync(GPUComputePipelineDescriptor descriptor); + Promise 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 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 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 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 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 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 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 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 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 buffers = []; +}; + +dictionary GPUVertexBufferLayout { + required GPUSize64 arrayStride; + GPUVertexStepMode stepMode = "vertex"; + required sequence 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 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 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 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 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 commandBuffers); + + Promise 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 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 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 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 or GPUColorDict) GPUColor; + +dictionary GPUOrigin2DDict { + GPUIntegerCoordinate x = 0; + GPUIntegerCoordinate y = 0; +}; +typedef (sequence or GPUOrigin2DDict) GPUOrigin2D; + +dictionary GPUOrigin3DDict { + GPUIntegerCoordinate x = 0; + GPUIntegerCoordinate y = 0; + GPUIntegerCoordinate z = 0; +}; +typedef (sequence or GPUOrigin3DDict) GPUOrigin3D; + +dictionary GPUExtent3DDict { + required GPUIntegerCoordinate width; + GPUIntegerCoordinate height = 1; + GPUIntegerCoordinate depthOrArrayLayers = 1; +}; +typedef (sequence or GPUExtent3DDict) GPUExtent3D; diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index b8c012f63b..39d907a612 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 e0881836b1..9975aecee8 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::( Default::default(), ), diff --git a/runtime/errors.rs b/runtime/errors.rs index 2061a5e0b9..c05e5bd622 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 83c4a4d03e..0c771ba835 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 65a9c69331..d5ec700db7 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 6a0a6569c6..1a97a33768 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 a0f0665b45..c2e8b0df24 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::(Default::default()), deno_cache::deno_cache::init_ops_and_esm::(None), deno_websocket::deno_websocket::init_ops_and_esm::( diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 4244f1045c..b669b5c2a1 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::( deno_fetch::Options { user_agent: options.bootstrap.user_agent.clone(), diff --git a/runtime/worker.rs b/runtime/worker.rs index 2a7e82c545..0bfb9305ca 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::( deno_fetch::Options { user_agent: options.bootstrap.user_agent.clone(), diff --git a/tools/README.md b/tools/README.md index f30e16b8f1..012302733b 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/