diff --git a/Cargo.lock b/Cargo.lock index 7a6108b2e7..4be228f921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,11 +212,11 @@ dependencies = [ [[package]] name = "ash" -version = "0.37.3+1.3.251" +version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "libloading 0.7.4", + "libloading 0.8.5", ] [[package]] @@ -231,7 +231,7 @@ dependencies = [ "nom 7.1.3", "num-traits", "rusticata-macros", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", ] @@ -525,7 +525,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -534,6 +543,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -646,9 +661,9 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] name = "byteorder" @@ -1157,17 +1172,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "d3d12" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b28bfe653d79bd16c77f659305b195b82bb5ce0c0eb2a4846b82ddbd77586813" -dependencies = [ - "bitflags 2.6.0", - "libloading 0.8.5", - "winapi", -] - [[package]] name = "darling" version = "0.20.10" @@ -1482,7 +1486,7 @@ dependencies = [ "serde_json", "sha2", "sys_traits", - "thiserror 1.0.64", + "thiserror 1.0.69", "url", ] @@ -1535,14 +1539,12 @@ dependencies = [ [[package]] name = "deno_core" version = "0.331.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2d1779358cad2bc56d71176298767be628d707bb75585f6f8a4be2da8ccda1" dependencies = [ "anyhow", "az", "bincode", - "bit-set", - "bit-vec", + "bit-set 0.5.3", + "bit-vec 0.6.3", "bytes", "capacity_builder 0.1.3", "cooked-waker", @@ -2163,8 +2165,6 @@ dependencies = [ [[package]] name = "deno_ops" version = "0.207.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96f000a21f6969b4c945bc8e9e785aa439f11ca4fd3fbddcd5bebc102167eb37" dependencies = [ "indexmap 2.3.0", "proc-macro-rules", @@ -2409,7 +2409,7 @@ dependencies = [ "nix", "os_pipe", "path-dedot", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "windows-sys 0.59.0", ] @@ -2550,8 +2550,10 @@ version = "0.153.0" dependencies = [ "deno_core", "deno_error", + "indexmap 2.3.0", "raw-window-handle", "serde", + "serde_json", "thiserror 2.0.3", "tokio", "wgpu-core", @@ -2918,9 +2920,9 @@ dependencies = [ [[package]] name = "document-features" -version = "0.2.8" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef5282ad69563b5fc40319526ba27e0e7363d552a896f0297d54f767717f9b95" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" dependencies = [ "litrs", ] @@ -3157,7 +3159,7 @@ dependencies = [ "debug-ignore", "indexmap 2.3.0", "log", - "thiserror 1.0.64", + "thiserror 1.0.69", "zerocopy", ] @@ -3283,7 +3285,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0678ab2d46fa5195aaf59ad034c083d351377d4af57f3e073c074d0da3e3c766" dependencies = [ - "bit-set", + "bit-set 0.5.3", "regex", ] @@ -3296,7 +3298,7 @@ dependencies = [ "anyhow", "async-trait", "log", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -3331,7 +3333,7 @@ dependencies = [ "rand", "sha1", "simdutf8", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "utf-8", ] @@ -3389,7 +3391,7 @@ dependencies = [ "deno_terminal 0.1.1", "parking_lot", "regex", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -3738,9 +3740,9 @@ dependencies = [ [[package]] name = "glow" -version = "0.13.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" dependencies = [ "js-sys", "slotmap", @@ -3750,9 +3752,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" dependencies = [ "gl_generator", ] @@ -3776,6 +3778,18 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.69", + "windows 0.58.0", +] + [[package]] name = "gpu-descriptor" version = "0.3.0" @@ -3876,7 +3890,7 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -4647,10 +4661,11 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -4682,7 +4697,7 @@ dependencies = [ "anyhow", "serde", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "uuid", ] @@ -5056,9 +5071,9 @@ dependencies = [ [[package]] name = "metal" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5637e166ea14be6063a3f8ba5ccb9a4159df7d8f6d61c02fc3d480b1f90dcfcb" +checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ "bitflags 2.6.0", "block", @@ -5124,7 +5139,7 @@ dependencies = [ "rustc_version 0.4.0", "smallvec", "tagptr", - "thiserror 1.0.64", + "thiserror 1.0.69", "uuid", ] @@ -5142,23 +5157,23 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "naga" -version = "0.20.0" +version = "23.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e536ae46fcab0876853bd4a632ede5df4b1c2527a58f6c5a4150fe86be858231" +checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f" dependencies = [ "arrayvec", - "bit-set", + "bit-set 0.8.0", "bitflags 2.6.0", + "cfg_aliases 0.1.1", "codespan-reporting", "hexf-parse", "indexmap 2.3.0", "log", - "num-traits", "rustc-hash 1.1.0", "serde", "spirv", "termcolor", - "thiserror 1.0.64", + "thiserror 1.0.69", "unicode-xid", ] @@ -5457,7 +5472,7 @@ dependencies = [ "js-sys", "once_cell", "pin-project-lite", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -5487,7 +5502,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tonic", "tracing", @@ -5529,7 +5544,7 @@ dependencies = [ "percent-encoding", "rand", "serde_json", - "thiserror 1.0.64", + "thiserror 1.0.69", "tracing", ] @@ -5719,7 +5734,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "311fb059dee1a7b802f036316d790138c613a4e8b180c822e3925a662e9f0c95" dependencies = [ "memchr", - "thiserror 1.0.64", + "thiserror 1.0.69", "ucd-trie", ] @@ -5938,6 +5953,12 @@ dependencies = [ "num-traits", ] +[[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" @@ -6149,7 +6170,7 @@ dependencies = [ "indexmap 2.3.0", "quick-xml", "strip-ansi-escapes", - "thiserror 1.0.64", + "thiserror 1.0.69", "uuid", ] @@ -6345,7 +6366,7 @@ checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] @@ -6894,9 +6915,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.205" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33aedb1a7135da52b7c21791455563facbbcc43d0f0f66165b42c21b3dfb150" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -6933,9 +6954,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.205" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692d6f5ac90220161d6774db30c662202721e64aed9058d2c394f451261420c1" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -6981,8 +7002,6 @@ dependencies = [ [[package]] name = "serde_v8" version = "0.240.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0494d74c40ab94f53a19485de359ea6a55f05341b817b93440b673c1ce8ec6" dependencies = [ "deno_error", "num-bigint", @@ -8021,11 +8040,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.64", + "thiserror-impl 1.0.69", ] [[package]] @@ -8039,9 +8058,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -8211,7 +8230,7 @@ checksum = "51165dfa029d2a65969413a6cc96f354b86b464498702f174a4efa13608fd8c0" dependencies = [ "either", "futures-util", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", ] @@ -8548,9 +8567,9 @@ checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unicode-xid" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "unicode_categories" @@ -8667,7 +8686,7 @@ dependencies = [ "indexmap 2.3.0", "num-bigint", "serde", - "thiserror 1.0.64", + "thiserror 1.0.69", "wtf8", ] @@ -8762,23 +8781,24 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.87", @@ -8799,9 +8819,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -8809,9 +8829,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -8822,9 +8842,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -8851,9 +8874,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -8889,15 +8912,14 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.21.1" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50819ab545b867d8a454d1d756b90cd5f15da1f2943334ca314af10583c9d39" +checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" dependencies = [ "arrayvec", - "bit-vec", + "bit-vec 0.8.0", "bitflags 2.6.0", "cfg_aliases 0.1.1", - "codespan-reporting", "document-features", "indexmap 2.3.0", "log", @@ -8910,30 +8932,30 @@ dependencies = [ "rustc-hash 1.1.0", "serde", "smallvec", - "thiserror 1.0.64", - "web-sys", + "thiserror 1.0.69", "wgpu-hal", "wgpu-types", ] [[package]] name = "wgpu-hal" -version = "0.21.1" +version = "23.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172e490a87295564f3fcc0f165798d87386f6231b04d4548bca458cbbfd63222" +checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" dependencies = [ "android_system_properties", "arrayvec", "ash", - "bit-set", + "bit-set 0.8.0", "bitflags 2.6.0", "block", + "bytemuck", "cfg_aliases 0.1.1", "core-graphics-types", - "d3d12", "glow", "glutin_wgl_sys", "gpu-alloc", + "gpu-allocator", "gpu-descriptor", "js-sys", "khronos-egl", @@ -8951,18 +8973,19 @@ dependencies = [ "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 1.0.64", + "thiserror 1.0.69", "wasm-bindgen", "web-sys", "wgpu-types", - "winapi", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "0.20.0" +version = "23.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1353d9a46bff7f955a680577f34c69122628cc2076e1d6f3a9be6ef00ae793ef" +checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" dependencies = [ "bitflags 2.6.0", "js-sys", @@ -9005,7 +9028,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b2b1bf557d947847a30eb73f79aa6cdb3eaf3ce02f5e9599438f77896a62b3c" dependencies = [ - "thiserror 1.0.64", + "thiserror 1.0.69", "windows 0.52.0", ] @@ -9367,7 +9390,7 @@ dependencies = [ "nom 7.1.3", "oid-registry", "rusticata-macros", - "thiserror 1.0.64", + "thiserror 1.0.69", "time", ] @@ -9511,7 +9534,7 @@ dependencies = [ "parking_lot", "rand", "regex", - "thiserror 1.0.64", + "thiserror 1.0.69", "tokio", "tokio-util", "uuid", @@ -9552,7 +9575,7 @@ dependencies = [ "flate2", "indexmap 2.3.0", "memchr", - "thiserror 1.0.64", + "thiserror 1.0.69", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 4ee2abe993..6137505aa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -237,8 +237,8 @@ rsa = { version = "0.9.3", default-features = false, features = ["std", "pem", " # webgpu raw-window-handle = "0.6.0" -wgpu-core = "0.21.1" -wgpu-types = "0.20" +wgpu-core = "23.0.1" +wgpu-types = "23.0.0" # macros quote = "1" @@ -350,3 +350,8 @@ opt-level = 3 opt-level = 3 [profile.release.package.zstd-sys] opt-level = 3 + +[patch.crates-io] +deno_core = { path = "../deno_core/core" } +deno_ops = { path = "../deno_core/ops" } +serde_v8 = { path = "../deno_core/serde_v8" } diff --git a/ext/webgpu/01_webgpu.js b/ext/webgpu/01_webgpu.js index 5ce34a5e7b..bb6531aa02 100644 --- a/ext/webgpu/01_webgpu.js +++ b/ext/webgpu/01_webgpu.js @@ -7,121 +7,40 @@ /// import { core, primordials } from "ext:core/mod.js"; -const { - isDataView, - isTypedArray, -} = core; import { - op_webgpu_buffer_get_map_async, - op_webgpu_buffer_get_mapped_range, - op_webgpu_buffer_unmap, - op_webgpu_command_encoder_begin_compute_pass, - op_webgpu_command_encoder_begin_render_pass, - op_webgpu_command_encoder_clear_buffer, - op_webgpu_command_encoder_copy_buffer_to_buffer, - op_webgpu_command_encoder_copy_buffer_to_texture, - op_webgpu_command_encoder_copy_texture_to_buffer, - op_webgpu_command_encoder_copy_texture_to_texture, - op_webgpu_command_encoder_finish, - op_webgpu_command_encoder_insert_debug_marker, - op_webgpu_command_encoder_pop_debug_group, - op_webgpu_command_encoder_push_debug_group, - op_webgpu_command_encoder_resolve_query_set, - op_webgpu_command_encoder_write_timestamp, - op_webgpu_compute_pass_dispatch_workgroups, - op_webgpu_compute_pass_dispatch_workgroups_indirect, - op_webgpu_compute_pass_end, - op_webgpu_compute_pass_insert_debug_marker, - op_webgpu_compute_pass_pop_debug_group, - op_webgpu_compute_pass_push_debug_group, - op_webgpu_compute_pass_set_bind_group, - op_webgpu_compute_pass_set_pipeline, - op_webgpu_compute_pipeline_get_bind_group_layout, - op_webgpu_create_bind_group, - op_webgpu_create_bind_group_layout, - op_webgpu_create_buffer, - op_webgpu_create_command_encoder, - op_webgpu_create_compute_pipeline, - op_webgpu_create_pipeline_layout, - op_webgpu_create_query_set, - op_webgpu_create_render_bundle_encoder, - op_webgpu_create_render_pipeline, - op_webgpu_create_sampler, - op_webgpu_create_shader_module, - op_webgpu_create_texture, - op_webgpu_create_texture_view, - op_webgpu_queue_submit, - op_webgpu_render_bundle_encoder_draw, - op_webgpu_render_bundle_encoder_draw_indexed, - op_webgpu_render_bundle_encoder_draw_indirect, - op_webgpu_render_bundle_encoder_finish, - op_webgpu_render_bundle_encoder_insert_debug_marker, - op_webgpu_render_bundle_encoder_pop_debug_group, - op_webgpu_render_bundle_encoder_push_debug_group, - op_webgpu_render_bundle_encoder_set_bind_group, - op_webgpu_render_bundle_encoder_set_index_buffer, - op_webgpu_render_bundle_encoder_set_pipeline, - op_webgpu_render_bundle_encoder_set_vertex_buffer, - op_webgpu_render_pass_begin_occlusion_query, - op_webgpu_render_pass_draw, - op_webgpu_render_pass_draw_indexed, - op_webgpu_render_pass_draw_indexed_indirect, - op_webgpu_render_pass_draw_indirect, - op_webgpu_render_pass_end, - op_webgpu_render_pass_end_occlusion_query, - op_webgpu_render_pass_execute_bundles, - op_webgpu_render_pass_insert_debug_marker, - op_webgpu_render_pass_pop_debug_group, - op_webgpu_render_pass_push_debug_group, - op_webgpu_render_pass_set_bind_group, - op_webgpu_render_pass_set_blend_constant, - op_webgpu_render_pass_set_index_buffer, - op_webgpu_render_pass_set_pipeline, - op_webgpu_render_pass_set_scissor_rect, - op_webgpu_render_pass_set_stencil_reference, - op_webgpu_render_pass_set_vertex_buffer, - op_webgpu_render_pass_set_viewport, - op_webgpu_render_pipeline_get_bind_group_layout, - op_webgpu_request_adapter, - op_webgpu_request_adapter_info, - op_webgpu_request_device, - op_webgpu_write_buffer, - op_webgpu_write_texture, + create_gpu, + GPU, + GPUAdapter, + GPUAdapterInfo, + GPUBindGroup, + GPUBindGroupLayout, + GPUBuffer, + GPUCommandBuffer, + GPUCommandEncoder, + GPUComputePassEncoder, + GPUComputePipeline, + GPUDevice, + GPUDeviceLostInfo, + GPUPipelineLayout, + GPUQuerySet, + GPUQueue, + GPURenderBundle, + GPURenderBundleEncoder, + GPURenderPassEncoder, + GPURenderPipeline, + GPUSampler, + GPUShaderModule, + GPUSupportedFeatures, + GPUSupportedLimits, + GPUTexture, + GPUTextureView, } from "ext:core/ops"; const { - ArrayBuffer, - ArrayBufferPrototypeGetByteLength, - ArrayIsArray, - ArrayPrototypeFindLast, - ArrayPrototypeMap, - ArrayPrototypePop, - ArrayPrototypePush, - DataViewPrototypeGetBuffer, Error, - Number, - NumberPOSITIVE_INFINITY, - NumberMAX_SAFE_INTEGER, - NumberNEGATIVE_INFINITY, - NumberMIN_SAFE_INTEGER, - MathMax, ObjectDefineProperty, - ObjectHasOwn, ObjectPrototypeIsPrototypeOf, - Promise, - PromiseReject, - PromiseResolve, - SafeArrayIterator, - SafeSet, - SafeWeakRef, - SetPrototypeHas, Symbol, SymbolFor, - SymbolIterator, - TypeError, - TypedArrayPrototypeGetBuffer, - TypedArrayPrototypeGetSymbolToStringTag, - Uint32Array, - Uint8Array, } = primordials; import * as webidl from "ext:deno_webidl/00_webidl.js"; @@ -134,150 +53,8 @@ import { import { DOMException } from "ext:deno_web/01_dom_exception.js"; import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; -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 _adapterInfo = Symbol("[[adapterInfo]]"); -const _invalid = Symbol("[[invalid]]"); -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 customInspect = SymbolFor("Deno.privateCustomInspect"); 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 different 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) { @@ -307,6 +84,7 @@ class GPUValidationError extends GPUError { this[_message] = message; } } +core.registerErrorClass("GPUValidationError", GPUValidationError); class GPUOutOfMemoryError extends GPUError { name = "GPUOutOfMemoryError"; @@ -319,6 +97,7 @@ class GPUOutOfMemoryError extends GPUError { this[_message] = message; } } +core.registerErrorClass("GPUOutOfMemoryError", GPUOutOfMemoryError); class GPUInternalError extends GPUError { name = "GPUInternalError"; @@ -327,6 +106,30 @@ class GPUInternalError extends GPUError { this[webidl.brand] = webidl.brand; } } +core.registerErrorClass("GPUInternalError", GPUInternalError); + +class GPUPipelineError extends DOMException { + #reason; + + constructor(message = "", options = { __proto__: null }) { + const prefix = "Failed to construct 'GPUPipelineError'"; + message = webidl.converters.DOMString(message, prefix, "Argument 1"); + options = webidl.converters.GPUPipelineErrorInit( + options, + prefix, + "Argument 2", + ); + super(message, "GPUPipelineError"); + + this.#reason = options.reason; + } + + get reason() { + webidl.assertBranded(this, GPUPipelineErrorPrototype); + return this.#reason; + } +} +const GPUPipelineErrorPrototype = GPUPipelineError.prototype; class GPUUncapturedErrorEvent extends Event { #error; @@ -354,196 +157,14 @@ class GPUUncapturedErrorEvent extends Event { } const GPUUncapturedErrorEventPrototype = GPUUncapturedErrorEvent.prototype; -class GPU { - [webidl.brand] = webidl.brand; - - constructor() { - webidl.illegalConstructor(); - } - - /** - * @param {GPURequestAdapterOptions} options - */ - // deno-lint-ignore require-await - async requestAdapter(options = { __proto__: null }) { - webidl.assertBranded(this, GPUPrototype); - options = webidl.converters.GPURequestAdapterOptions( - options, - "Failed to execute 'requestAdapter' on 'GPU'", - "Argument 1", - ); - - const { err, ...data } = op_webgpu_request_adapter( - options.powerPreference, - options.forceFallbackAdapter, - ); - - if (err) { - return null; - } else { - return createGPUAdapter(data); - } - } - - getPreferredCanvasFormat() { - // Same as Gecko. - // - // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47 - if (core.build.os == "android") { - return "rgba8unorm"; - } - return "bgra8unorm"; - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { +ObjectDefineProperty(GPU, customInspect, { + value(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), - }; - adapter[_adapterInfo] = undefined; - adapter[_invalid] = false; - return adapter; -} - -class GPUAdapter { - /** @type {InnerGPUAdapter} */ - [_adapter]; - /** @type {GPUAdapterInfo | undefined} */ - [_adapterInfo]; - /** @type {boolean} */ - [_invalid]; - - /** @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].isFallback; - } - - constructor() { - webidl.illegalConstructor(); - } - - /** - * @param {GPUDeviceDescriptor} descriptor - * @returns {Promise} - */ - // deno-lint-ignore require-await - async requestDevice(descriptor = { __proto__: null }) { - 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`, - ); - } - } - - if (this[_invalid]) { - throw new TypeError( - "The adapter cannot be reused, as it has been invalidated by a device creation", - ); - } - - const { rid, queueRid, features, limits } = op_webgpu_request_device( - this[_adapter].rid, - descriptor.label, - requiredFeatures, - descriptor.requiredLimits, - ); - - this[_invalid] = true; - - const inner = new InnerGPUDevice({ - rid, - adapter: this, - features: createGPUSupportedFeatures(features), - limits: createGPUSupportedLimits(limits), - }); - const queue = createGPUQueue(descriptor.label, inner, queueRid); - inner.trackResource(queue); - const device = createGPUDevice( - descriptor.label, - inner, - queue, - ); - inner.device = device; - return device; - } - - /** - * @returns {GPUAdapterInfo} - */ - get info() { - webidl.assertBranded(this, GPUAdapterPrototype); - - if (this[_adapterInfo] !== undefined) { - return this[_adapterInfo]; - } - - if (this[_invalid]) { - throw new TypeError( - "The adapter cannot be reused, as it has been invalidated by a device creation", - ); - } - - const { - vendor, - architecture, - device, - description, - } = op_webgpu_request_adapter_info(this[_adapter].rid); - - const adapterInfo = webidl.createBranded(GPUAdapterInfo); - adapterInfo[_vendor] = vendor; - adapterInfo[_architecture] = architecture; - adapterInfo[_device] = device; - adapterInfo[_description] = description; - this[_adapterInfo] = adapterInfo; - return adapterInfo; - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { +ObjectDefineProperty(GPUAdapter, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -557,44 +178,12 @@ class GPUAdapter { }), 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) { +ObjectDefineProperty(GPUAdapterInfo, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -608,257 +197,12 @@ class GPUAdapterInfo { }), inspectOptions, ); - } -} + }, +}); const GPUAdapterInfoPrototype = GPUAdapterInfo.prototype; -function createGPUSupportedLimits(limits) { - /** @type {GPUSupportedLimits} */ - const adapterFeatures = webidl.createBranded(GPUSupportedLimits); - adapterFeatures[_limits] = limits; - return adapterFeatures; -} - -function normalizeLimit(limit) { - if (typeof limit === "bigint") { - limit = Number(limit); - if (limit === NumberPOSITIVE_INFINITY) { - limit = NumberMAX_SAFE_INTEGER; - } else if (limit === NumberNEGATIVE_INFINITY) { - limit = NumberMIN_SAFE_INTEGER; - } - } - - return limit; -} - -/** - * @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 normalizeLimit(this[_limits].maxTextureDimension1D); - } - get maxTextureDimension2D() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxTextureDimension2D); - } - get maxTextureDimension3D() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxTextureDimension3D); - } - get maxTextureArrayLayers() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxTextureArrayLayers); - } - get maxBindGroups() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxBindGroups); - } - get maxBindingsPerBindGroup() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxBindingsPerBindGroup); - } - get maxBufferSize() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxBufferSize); - } - get maxDynamicUniformBuffersPerPipelineLayout() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit( - this[_limits].maxDynamicUniformBuffersPerPipelineLayout, - ); - } - get maxDynamicStorageBuffersPerPipelineLayout() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit( - this[_limits].maxDynamicStorageBuffersPerPipelineLayout, - ); - } - get maxSampledTexturesPerShaderStage() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxSampledTexturesPerShaderStage); - } - get maxSamplersPerShaderStage() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxSamplersPerShaderStage); - } - get maxStorageBuffersPerShaderStage() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxStorageBuffersPerShaderStage); - } - get maxStorageTexturesPerShaderStage() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxStorageTexturesPerShaderStage); - } - get maxUniformBuffersPerShaderStage() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxUniformBuffersPerShaderStage); - } - get maxUniformBufferBindingSize() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxUniformBufferBindingSize); - } - get maxStorageBufferBindingSize() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxStorageBufferBindingSize); - } - get minUniformBufferOffsetAlignment() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].minUniformBufferOffsetAlignment); - } - get minStorageBufferOffsetAlignment() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].minStorageBufferOffsetAlignment); - } - get maxVertexBuffers() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxVertexBuffers); - } - get maxVertexAttributes() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxVertexAttributes); - } - get maxVertexBufferArrayStride() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxVertexBufferArrayStride); - } - get maxInterStageShaderComponents() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxInterStageShaderComponents); - } - get maxColorAttachments() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxColorAttachments); - } - get maxColorAttachmentBytesPerSample() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxColorAttachmentBytesPerSample); - } - get maxComputeWorkgroupStorageSize() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxComputeWorkgroupStorageSize); - } - get maxComputeInvocationsPerWorkgroup() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxComputeInvocationsPerWorkgroup); - } - get maxComputeWorkgroupSizeX() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxComputeWorkgroupSizeX); - } - get maxComputeWorkgroupSizeY() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxComputeWorkgroupSizeY); - } - get maxComputeWorkgroupSizeZ() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(this[_limits].maxComputeWorkgroupSizeZ); - } - get maxComputeWorkgroupsPerDimension() { - webidl.assertBranded(this, GPUSupportedLimitsPrototype); - return normalizeLimit(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", - "maxColorAttachments", - "maxColorAttachmentBytesPerSample", - "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) { +ObjectDefineProperty(GPUSupportedFeatures, customInspect, { + value(inspect, inspectOptions) { if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) { return `${this.constructor.name} ${ // deno-lint-ignore prefer-primordials @@ -866,43 +210,13 @@ class GPUSupportedFeatures { } else { return `${this.constructor.name} ${inspect({}, inspectOptions)}`; } - } -} + }, +}); const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype; +webidl.setlike(GPUSupportedFeatures, GPUSupportedFeaturesPrototype, true); -/** - * @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) { +ObjectDefineProperty(GPUDeviceLostInfo, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -917,943 +231,12 @@ class GPUDeviceLostInfo { }), inspectOptions, ); - } -} - + }, +}); const GPUDeviceLostInfoPrototype = GPUDeviceLostInfo.prototype; -/** - * @param {string} name - * @param {any} type - */ -function GPUObjectBaseMixin(name, type) { - type.prototype[_label] = null; - ObjectDefineProperty(type.prototype, "label", { - __proto__: null, - /** - * @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 {GPUError[]} errors - */ - -/** - * @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; - /** @type {GPUDevice} */ - device; - - /** - * @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)); - } - - // Ref: https://gpuweb.github.io/gpuweb/#abstract-opdef-dispatch-error - /** @param {{ type: string, value: string | null } | undefined} error */ - pushError(error) { - if (!error) { - return; - } - - let constructedError; - switch (error.type) { - case "lost": - this.isLost = true; - this.resolveLost( - createGPUDeviceLostInfo(undefined, "device was lost"), - ); - return; - case "validation": - constructedError = new GPUValidationError( - error.value ?? "validation error", - ); - break; - case "out-of-memory": - constructedError = new GPUOutOfMemoryError("not enough memory left"); - break; - case "internal": - constructedError = new GPUInternalError(); - break; - } - - if (this.isLost) { - return; - } - - const scope = ArrayPrototypeFindLast( - this.errorScopeStack, - ({ filter }) => filter === error.type, - ); - if (scope) { - ArrayPrototypePush(scope.errors, constructedError); - } else { - this.device.dispatchEvent( - new GPUUncapturedErrorEvent("uncapturederror", { - error: constructedError, - }), - ); - } - } -} - -/** - * @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; - setEventTargetData(device); - 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 } = 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"); - // assign normalized size to descriptor due to createGPUTexture needs it - descriptor.size = normalizeGPUExtent3D(descriptor.size); - const { rid, err } = op_webgpu_create_texture({ - deviceRid: device.rid, - ...descriptor, - }); - device.pushError(err); - - const texture = createGPUTexture( - descriptor, - device, - rid, - ); - device.trackResource(texture); - return texture; - } - - /** - * @param {GPUSamplerDescriptor} descriptor - * @returns {GPUSampler} - */ - createSampler(descriptor = { __proto__: null }) { - 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 } = 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 } = 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 } = 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); - return { - binding: entry.binding, - kind: "GPUSampler", - resource: rid, - }; - } else if ( - ObjectPrototypeIsPrototypeOf(GPUTextureViewPrototype, resource) - ) { - const rid = assertResource(resource, prefix, context); - assertResource(resource[_texture], prefix, context); - return { - binding: entry.binding, - kind: "GPUTextureView", - resource: rid, - }; - } else { - // deno-lint-ignore prefer-primordials - const rid = assertResource(resource.buffer, prefix, context); - return { - binding: entry.binding, - kind: "GPUBufferBinding", - resource: rid, - offset: entry.resource.offset, - size: entry.resource.size, - }; - } - }); - - const { rid, err } = 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 } = 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 } = 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, - constants: descriptor.fragment.constants, - targets: descriptor.fragment.targets, - }; - } - - const { rid, err } = op_webgpu_create_render_pipeline({ - deviceRid: device.rid, - label: descriptor.label, - layout, - vertex: { - module, - entryPoint: descriptor.vertex.entryPoint, - constants: descriptor.vertex.constants, - 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 - - webidl.assertBranded(this, GPUDevicePrototype); - const prefix = - "Failed to execute 'createComputePipelineAsync' 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 } = op_webgpu_create_compute_pipeline( - device.rid, - descriptor.label, - layout, - { - module, - entryPoint: descriptor.compute.entryPoint, - constants: descriptor.compute.constants, - }, - ); - device.pushError(err); - if (err) { - switch (err.type) { - case "validation": - return PromiseReject( - new GPUPipelineError(err.value ?? "validation error", { - reason: "validation", - }), - ); - case "internal": - return PromiseReject( - new GPUPipelineError("internal error", { - reason: "validation", - }), - ); - } - } - - const computePipeline = createGPUComputePipeline( - descriptor.label, - device, - rid, - ); - device.trackResource(computePipeline); - return PromiseResolve(computePipeline); - } - - createRenderPipelineAsync(descriptor) { - // TODO(lucacasonato): this should be real async - - webidl.assertBranded(this, GPUDevicePrototype); - const prefix = - "Failed to execute 'createRenderPipelineAsync' 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, - constants: descriptor.fragment.constants, - targets: descriptor.fragment.targets, - }; - } - - const { rid, err } = op_webgpu_create_render_pipeline({ - deviceRid: device.rid, - label: descriptor.label, - layout, - vertex: { - module, - entryPoint: descriptor.vertex.entryPoint, - constants: descriptor.vertex.constants, - buffers: descriptor.vertex.buffers, - }, - primitive: descriptor.primitive, - depthStencil: descriptor.depthStencil, - multisample: descriptor.multisample, - fragment, - }); - device.pushError(err); - if (err) { - switch (err.type) { - case "validation": - return PromiseReject( - new GPUPipelineError(err.value ?? "validation error", { - reason: "validation", - }), - ); - case "internal": - return PromiseReject( - new GPUPipelineError("internal error", { - reason: "validation", - }), - ); - } - } - - const renderPipeline = createGPURenderPipeline( - descriptor.label, - device, - rid, - ); - device.trackResource(renderPipeline); - return PromiseResolve(renderPipeline); - } - - /** - * @param {GPUCommandEncoderDescriptor} descriptor - * @returns {GPUCommandEncoder} - */ - createCommandEncoder(descriptor = { __proto__: null }) { - 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 } = 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 } = 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 } = 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, errors: [] }); - } - - /** - * @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", - ); - } - return PromiseResolve(scope.errors[0] ?? null); - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { +ObjectDefineProperty(GPUDevice, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -1870,221 +253,13 @@ class GPUDevice extends EventTarget { }), inspectOptions, ); - } -} -GPUObjectBaseMixin("GPUDevice", GPUDevice); + }, +}); const GPUDevicePrototype = GPUDevice.prototype; defineEventHandler(GPUDevice.prototype, "uncapturederror"); -class GPUPipelineError extends DOMException { - #reason; - - constructor(message = "", options = { __proto__: null }) { - const prefix = "Failed to construct 'GPUPipelineError'"; - message = webidl.converters.DOMString(message, prefix, "Argument 1"); - options = webidl.converters.GPUPipelineErrorInit( - options, - prefix, - "Argument 2", - ); - super(message, "GPUPipelineError"); - - this.#reason = options.reason; - } - - get reason() { - webidl.assertBranded(this, GPUPipelineErrorPrototype); - return this.#reason; - } -} -const GPUPipelineErrorPrototype = GPUPipelineError.prototype; - -/** - * @param {string | null} label - * @param {InnerGPUDevice} device - * @param {number} rid - * @returns {GPUQueue} - */ -function createGPUQueue(label, device, rid) { - /** @type {GPUQueue} */ - const queue = webidl.createBranded(GPUQueue); - queue[_label] = label; - queue[_device] = device; - queue[_rid] = rid; - return queue; -} - -class GPUQueue { - /** @type {InnerGPUDevice} */ - [_device]; - /** @type {number} */ - [_rid]; - - [_cleanup]() { - const rid = this[_rid]; - if (rid !== undefined) { - core.close(rid); - /** @type {number | undefined} */ - this[_rid] = undefined; - } - } - - 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 } = op_webgpu_queue_submit(this[_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 (isTypedArray(data)) { - abLike = TypedArrayPrototypeGetBuffer( - /** @type {Uint8Array} */ (data), - ); - } else if (isDataView(data)) { - abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data)); - } - - const { err } = op_webgpu_write_buffer( - this[_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 (isTypedArray(data)) { - abLike = TypedArrayPrototypeGetBuffer( - /** @type {Uint8Array} */ (data), - ); - } else if (isDataView(data)) { - abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data)); - } - - const { err } = op_webgpu_write_texture( - this[_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) { +ObjectDefineProperty(GPUQueue, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2095,307 +270,12 @@ class GPUQueue { }), 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, received ${offset}`, - "OperationError", - ); - } - if ((rangeSize % 4) !== 0) { - throw new DOMException( - `${prefix}: rangeSize must be a multiple of 4, received ${rangeSize}`, - "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 { err } = await op_webgpu_buffer_get_map_async( - bufferRid, - device.rid, - mode, - offset, - rangeSize, - ); - if (err) { - device.pushError(err); - throw new DOMException("validation error occurred", "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 } = 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 } = 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) { +ObjectDefineProperty(GPUBuffer, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2409,9 +289,8 @@ class GPUBuffer { }), inspectOptions, ); - } -} -GPUObjectBaseMixin("GPUBuffer", GPUBuffer); + }, +}); const GPUBufferPrototype = GPUBuffer.prototype; class GPUBufferUsage { @@ -2464,150 +343,8 @@ class GPUMapMode { } } -/** - * @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 = { __proto__: null }) { - 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 } = 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) { +ObjectDefineProperty(GPUTexture, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2626,9 +363,8 @@ class GPUTexture { }), inspectOptions, ); - } -} -GPUObjectBaseMixin("GPUTexture", GPUTexture); + }, +}); const GPUTexturePrototype = GPUTexture.prototype; class GPUTextureUsage { @@ -2653,40 +389,8 @@ class GPUTextureUsage { } } -/** - * @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) { +ObjectDefineProperty(GPUTextureView, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2697,87 +401,22 @@ class GPUTextureView { }), 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) { +ObjectDefineProperty(GPUSampler, customInspect, { + value(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) { +ObjectDefineProperty(GPUBindGroupLayout, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2791,44 +430,12 @@ class GPUBindGroupLayout { }), 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) { +ObjectDefineProperty(GPUPipelineLayout, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2842,45 +449,12 @@ class GPUPipelineLayout { }), 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) { +ObjectDefineProperty(GPUBindGroup, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2891,44 +465,12 @@ class GPUBindGroup { }), 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) { +ObjectDefineProperty(GPUShaderModule, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -2939,10 +481,10 @@ class GPUShaderModule { }), inspectOptions, ); - } -} -GPUObjectBaseMixin("GPUShaderModule", GPUShaderModule); + }, +}); const GPUShaderModulePrototype = GPUShaderModule.prototype; + class GPUShaderStage { constructor() { webidl.illegalConstructor(); @@ -2961,68 +503,8 @@ class GPUShaderStage { } } -/** - * @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 } = - 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) { +ObjectDefineProperty(GPUComputePipeline, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -3036,71 +518,12 @@ class GPUComputePipeline { }), 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 } = 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) { +ObjectDefineProperty(GPURenderPipeline, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -3114,9 +537,8 @@ class GPURenderPipeline { }), inspectOptions, ); - } -} -GPUObjectBaseMixin("GPURenderPipeline", GPURenderPipeline); + }, +}); const GPURenderPipelinePrototype = GPURenderPipeline.prototype; class GPUColorWrite { @@ -3141,687 +563,8 @@ class GPUColorWrite { } } -/** - * @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 } = 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 = { __proto__: null }) { - 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 } = 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 } = 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 } = 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 } = 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 } = 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 } = 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 } = 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 } = 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 } = 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 } = 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 } = op_webgpu_command_encoder_resolve_query_set( - commandEncoderRid, - querySetRid, - firstQuery, - queryCount, - destinationRid, - destinationOffset, - ); - device.pushError(err); - } - - /** - * @param {GPUCommandBufferDescriptor} descriptor - * @returns {GPUCommandBuffer} - */ - finish(descriptor = { __proto__: null }) { - 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 } = 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) { +ObjectDefineProperty(GPUCommandEncoder, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -3835,601 +578,12 @@ class GPUCommandEncoder { }), 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"); - 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"); - 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"); - 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"); - 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"); - 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"); - 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; - }); - 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 } = 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 ( - TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !== - "Uint32Array" - ) { - dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []); - dynamicOffsetsDataStart = 0; - dynamicOffsetsDataLength = dynamicOffsetsData.length; - } - 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"); - 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"); - 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"); - 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", - }); - 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", - }); - 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", - }); - 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"); - 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"); - 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", - }); - 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", - }); - op_webgpu_render_pass_draw_indexed_indirect( - renderPassRid, - indirectBufferRid, - indirectOffset, - ); - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { +ObjectDefineProperty(GPURenderPassEncoder, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -4443,269 +597,12 @@ class GPURenderPassEncoder { }), 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", - }); - 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"); - 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", - }); - 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 } = 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 ( - TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !== - "Uint32Array" - ) { - dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []); - dynamicOffsetsDataStart = 0; - dynamicOffsetsDataLength = dynamicOffsetsData.length; - } - 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"); - 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"); - 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"); - op_webgpu_compute_pass_insert_debug_marker( - computePassRid, - markerLabel, - ); - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { +ObjectDefineProperty(GPUComputePassEncoder, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -4719,46 +616,12 @@ class GPUComputePassEncoder { }), 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) { +ObjectDefineProperty(GPUCommandBuffer, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -4769,384 +632,12 @@ class GPUCommandBuffer { }), 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 = { __proto__: null }) { - 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 } = 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 ( - TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !== - "Uint32Array" - ) { - dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []); - dynamicOffsetsDataStart = 0; - dynamicOffsetsDataLength = dynamicOffsetsData.length; - } - 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"); - 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"); - 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"); - 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", - }); - 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", - }); - 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", - }); - 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"); - 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"); - 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", - }); - op_webgpu_render_bundle_encoder_draw_indirect( - renderBundleEncoderRid, - indirectBufferRid, - indirectOffset, - ); - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { +ObjectDefineProperty(GPURenderBundleEncoder, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -5160,46 +651,12 @@ class GPURenderBundleEncoder { }), 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) { +ObjectDefineProperty(GPURenderBundle, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -5210,67 +667,12 @@ class GPURenderBundle { }), 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) { +ObjectDefineProperty(GPUQuerySet, customInspect, { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -5283,130 +685,12 @@ class GPUQuerySet { }), 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", - "float32-filterable", - - // 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", - ], -); - -// DICTIONARY: GPUPipelineErrorInit webidl.converters["GPUPipelineErrorInit"] = webidl.createDictionaryConverter( "GPUPipelineErrorInit", [ @@ -5418,7 +702,6 @@ webidl.converters["GPUPipelineErrorInit"] = webidl.createDictionaryConverter( ], ); -// ENUM: GPUPipelineErrorReason webidl.converters["GPUPipelineErrorReason"] = webidl.createEnumConverter( "GPUPipelineErrorReason", [ @@ -5427,1968 +710,8 @@ webidl.converters["GPUPipelineErrorReason"] = webidl.createEnumConverter( ], ); -// TYPEDEF: GPUSize32 -webidl.converters["GPUSize32"] = (V, opts) => - webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }); +webidl.converters["GPUError"] = webidl.converters.any /* put union here! */; -// 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) { - // validate length of GPUExtent3D - const min = 1; - const max = 3; - if (V.length < min || V.length > max) { - throw webidl.makeException( - TypeError, - `A sequence of number used as a GPUExtent3D must have between ${min} and ${max} elements, received ${V.length} elements`, - opts, - ); - } - 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", - "rgb10a2uint", - "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", - "read-only", - "read-write", - ], -); - -// 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"], - }, - { - 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", - "unorm10-10-10-2", - ], -); - -// 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) { - // validate length of GPUOrigin3D - const length = 3; - if (V.length > length) { - throw webidl.makeException( - TypeError, - `A sequence of number used as a GPUOrigin3D must have at most ${length} elements, received ${V.length} elements`, - opts, - ); - } - 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) { - // validate length of GPUOrigin2D - const length = 2; - if (V.length > length) { - throw webidl.makeException( - TypeError, - `A sequence of number used as a GPUOrigin2D must have at most ${length} elements, received ${V.length} elements`, - opts, - ); - } - 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, -); - -// INTERFACE: GPUQuerySet -webidl.converters.GPUQuerySet = webidl.createInterfaceConverter( - "GPUQuerySet", - GPUQuerySet.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) { - // validate length of GPUColor - const length = 4; - if (V.length !== length) { - throw webidl.makeException( - TypeError, - `A sequence of number used as a GPUColor must have exactly ${length} elements, received ${V.length} elements`, - opts, - ); - } - 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, - ); - -// 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", - "internal", - ], -); - -// 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 }, ]; @@ -7399,84 +722,14 @@ webidl.converters["GPUUncapturedErrorEventInit"] = webidl dictMembersGPUUncapturedErrorEventInit, ); -// TYPEDEF: GPUBufferDynamicOffset -webidl.converters["GPUBufferDynamicOffset"] = (V, opts) => - webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }); +let gpu; +function initGPU() { + if (!gpu) { + gpu = create_gpu(); + } +} -// TYPEDEF: GPUSignedOffset32 -webidl.converters["GPUSignedOffset32"] = (V, opts) => - webidl.converters["long"](V, { ...opts, enforceRange: true }); - -// TYPEDEF: GPUFlagsConstant -webidl.converters["GPUFlagsConstant"] = webidl.converters["unsigned long"]; - -// 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: "viewFormats", - converter: webidl.createSequenceConverter( - webidl.converters["GPUTextureFormat"], - ), - get defaultValue() { - return []; - }, - }, -]; -webidl.converters["GPUCanvasConfiguration"] = webidl - .createDictionaryConverter( - "GPUCanvasConfiguration", - dictMembersGPUCanvasConfiguration, - ); - -const gpu = webidl.createBranded(GPU); export { - _device, - assertDevice, - createGPUTexture, GPU, gpu, GPUAdapter, @@ -7513,4 +766,5 @@ export { GPUTextureView, GPUUncapturedErrorEvent, GPUValidationError, + initGPU, }; diff --git a/ext/webgpu/02_surface.js b/ext/webgpu/02_surface.js index b0561469a9..763c4fc56c 100644 --- a/ext/webgpu/02_surface.js +++ b/ext/webgpu/02_surface.js @@ -7,146 +7,16 @@ /// import { primordials } from "ext:core/mod.js"; -import { - op_webgpu_surface_configure, - op_webgpu_surface_create, - op_webgpu_surface_get_current_texture, - op_webgpu_surface_present, -} from "ext:core/ops"; +import { GPUCanvasContext, UnsafeWindowSurface } from "ext:core/ops"; const { + ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, - Symbol, SymbolFor, - TypeError, } = primordials; - -import * as webidl from "ext:deno_webidl/00_webidl.js"; import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; -import { loadWebGPU } from "ext:deno_webgpu/00_init.js"; -const _surfaceRid = Symbol("[[surfaceRid]]"); -const _configuration = Symbol("[[configuration]]"); -const _canvas = Symbol("[[canvas]]"); -const _currentTexture = Symbol("[[currentTexture]]"); -const _present = Symbol("[[present]]"); -const _dim = Symbol("[[dimensions]]"); - -class GPUCanvasContext { - /** @type {number} */ - [_surfaceRid]; - [_configuration]; - [_canvas]; - /** @type {GPUTexture | undefined} */ - [_currentTexture]; - [_dim]; - - 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", - }); - - const { _device, assertDevice } = loadWebGPU(); - this[_device] = configuration.device[_device]; - this[_configuration] = configuration; - const device = assertDevice(this, { - prefix, - context: "configuration.device", - }); - - const { err } = op_webgpu_surface_configure({ - surfaceRid: this[_surfaceRid], - deviceRid: device.rid, - format: configuration.format, - viewFormats: configuration.viewFormats, - usage: configuration.usage, - width: this[_dim].width, - height: this[_dim].height, - alphaMode: configuration.alphaMode, - }); - - device.pushError(err); - } - - unconfigure() { - const { _device } = loadWebGPU(); - - 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 { createGPUTexture, assertDevice } = loadWebGPU(); - - const device = assertDevice(this, { prefix, context: "this" }); - - if (this[_currentTexture]) { - return this[_currentTexture]; - } - - const { rid } = op_webgpu_surface_get_current_texture( - device.rid, - this[_surfaceRid], - ); - - const texture = createGPUTexture( - { - size: { - width: this[_dim].width, - height: this[_dim].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; - } - - // Required to present the texture; browser don't need this. - [_present]() { - const { assertDevice } = loadWebGPU(); - - webidl.assertBranded(this, GPUCanvasContextPrototype); - const prefix = "Failed to execute 'present' on 'GPUCanvasContext'"; - const device = assertDevice(this[_currentTexture], { - prefix, - context: "this", - }); - op_webgpu_surface_present(device.rid, this[_surfaceRid]); - this[_currentTexture].destroy(); - this[_currentTexture] = undefined; - } - - [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { +ObjectDefineProperty(GPUCanvasContext, SymbolFor("Deno.privateCustomInspect"), { + value(inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -157,60 +27,8 @@ class GPUCanvasContext { }), inspectOptions, ); - } -} + }, +}); const GPUCanvasContextPrototype = GPUCanvasContext.prototype; -function createCanvasContext(options) { - // lazy load webgpu if needed - const canvasContext = webidl.createBranded(GPUCanvasContext); - canvasContext[_surfaceRid] = options.surfaceRid; - canvasContext[_canvas] = options.canvas; - canvasContext[_dim] = { width: options.width, height: options.height }; - - return canvasContext; -} - -// External webgpu surfaces - -// TODO(@littledivy): This will extend `OffscreenCanvas` when we add it. -class UnsafeWindowSurface { - #ctx; - #surfaceRid; - #options; - - constructor(options) { - if (typeof options !== "object") { - throw new TypeError("options must be provided."); - } - if ( - typeof options.width !== "number" || typeof options.height !== "number" - ) { - throw new TypeError("width and height must be provided."); - } - - this.#surfaceRid = op_webgpu_surface_create( - options.system, - options.windowHandle, - options.displayHandle, - ); - this.#options = options; - } - - getContext(context) { - if (context !== "webgpu") { - throw new TypeError("Only 'webgpu' context is supported"); - } - this.#ctx = createCanvasContext({ - surfaceRid: this.#surfaceRid, - ...this.#options, - }); - return this.#ctx; - } - - present() { - this.#ctx[_present](); - } -} - export { GPUCanvasContext, UnsafeWindowSurface }; diff --git a/ext/webgpu/Cargo.toml b/ext/webgpu/Cargo.toml index 7e8973cadd..32141e891e 100644 --- a/ext/webgpu/Cargo.toml +++ b/ext/webgpu/Cargo.toml @@ -27,6 +27,8 @@ tokio = { workspace = true, features = ["full"] } wgpu-types = { workspace = true, features = ["serde"] } raw-window-handle = { workspace = true } thiserror.workspace = true +indexmap.workspace = true +serde_json.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgpu-core] workspace = true diff --git a/ext/webgpu/adapter.rs b/ext/webgpu/adapter.rs new file mode 100644 index 0000000000..c0e531d519 --- /dev/null +++ b/ext/webgpu/adapter.rs @@ -0,0 +1,386 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::collections::HashSet; +use std::rc::Rc; +use std::sync::Arc; + +use deno_core::cppgc::SameObject; +use deno_core::op2; +use deno_core::v8; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use tokio::sync::Mutex; + +use super::device::GPUDevice; +use crate::webidl::features_to_feature_names; +use crate::webidl::GPUFeatureName; +use crate::Instance; + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURequestAdapterOptions { + pub power_preference: Option, + #[webidl(default = false)] + pub force_fallback_adapter: bool, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUPowerPreference { + LowPower, + HighPerformance, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +struct GPUDeviceDescriptor { + #[webidl(default = String::new())] + label: String, + + #[webidl(default = vec![])] + required_features: Vec, + #[webidl(default = Default::default())] + #[options(enforce_range = true)] + required_limits: indexmap::IndexMap, +} + +pub struct GPUAdapter { + pub instance: Instance, + pub id: wgpu_core::id::AdapterId, + + pub features: SameObject, + pub limits: SameObject, + pub info: Rc>, +} + +impl Drop for GPUAdapter { + fn drop(&mut self) { + self.instance.adapter_drop(self.id); + } +} + +impl GarbageCollected for GPUAdapter {} + +#[op2] +impl GPUAdapter { + #[getter] + #[global] + fn info(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.info.get(scope, |_| { + let info = self.instance.adapter_get_info(self.id); + GPUAdapterInfo(info) + }) + } + + #[getter] + #[global] + fn features(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.features.get(scope, |scope| { + let features = self.instance.adapter_features(self.id); + let features = features_to_feature_names(features); + GPUSupportedFeatures::new(scope, features) + }) + } + #[getter] + #[global] + fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.limits.get(scope, |_| { + let adapter_limits = self.instance.adapter_limits(self.id); + GPUSupportedLimits(adapter_limits) + }) + } + #[getter] + fn is_fallback_adapter(&self) -> bool { + // TODO(lucacasonato): report correctly from wgpu + false + } + + #[async_method] + #[cppgc] + async fn request_device( + &self, + #[webidl] descriptor: GPUDeviceDescriptor, + ) -> Result { + let features = self.instance.adapter_features(self.id); + let supported_features = features_to_feature_names(features); + let required_features = descriptor + .required_features + .iter() + .cloned() + .collect::>(); + + if !required_features.is_subset(&supported_features) { + return Err(CreateDeviceError::RequiredFeaturesNotASubset); + } + + let required_limits = serde_json::from_value(serde_json::to_value( + descriptor.required_limits, + )?)?; + + let wgpu_descriptor = wgpu_types::DeviceDescriptor { + label: Some(std::borrow::Cow::Owned(descriptor.label.clone())), + required_features: super::webidl::feature_names_to_features( + descriptor.required_features, + ), + required_limits, + memory_hints: Default::default(), + }; + + let (device, queue) = self.instance.adapter_request_device( + self.id, + &wgpu_descriptor, + std::env::var("DENO_WEBGPU_TRACE") + .ok() + .as_ref() + .map(std::path::Path::new), + None, + None, + )?; + + let (sender, receiver) = tokio::sync::oneshot::channel(); + + Ok(GPUDevice { + instance: self.instance.clone(), + id: device, + queue, + label: descriptor.label, + queue_obj: SameObject::new(), + adapter_info: self.info.clone(), + error_handler: Arc::new(super::error::DeviceErrorHandler::new(sender)), + adapter: self.id, + lost_receiver: Mutex::new(Some(receiver)), + limits: SameObject::new(), + features: SameObject::new(), + }) + } +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum CreateDeviceError { + #[class(type)] + #[error("requiredFeatures must be a subset of the adapter features")] + RequiredFeaturesNotASubset, + #[class(inherit)] + #[error(transparent)] + Serde(#[from] serde_json::Error), + #[class(type)] + #[error(transparent)] + Device(#[from] wgpu_core::instance::RequestDeviceError), +} + +pub struct GPUSupportedLimits(pub wgpu_types::Limits); + +impl GarbageCollected for GPUSupportedLimits {} + +#[op2] +impl GPUSupportedLimits { + #[getter] + fn maxTextureDimension1D(&self) -> u32 { + self.0.max_texture_dimension_1d + } + + #[getter] + fn maxTextureDimension2D(&self) -> u32 { + self.0.max_texture_dimension_2d + } + + #[getter] + fn maxTextureDimension3D(&self) -> u32 { + self.0.max_texture_dimension_3d + } + + #[getter] + fn maxTextureArrayLayers(&self) -> u32 { + self.0.max_texture_array_layers + } + + #[getter] + fn maxBindGroups(&self) -> u32 { + self.0.max_bind_groups + } + + // TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers + + #[getter] + fn maxBindingsPerBindGroup(&self) -> u32 { + self.0.max_bindings_per_bind_group + } + + #[getter] + fn maxDynamicUniformBuffersPerPipelineLayout(&self) -> u32 { + self.0.max_dynamic_uniform_buffers_per_pipeline_layout + } + + #[getter] + fn maxDynamicStorageBuffersPerPipelineLayout(&self) -> u32 { + self.0.max_dynamic_storage_buffers_per_pipeline_layout + } + + #[getter] + fn maxSampledTexturesPerShaderStage(&self) -> u32 { + self.0.max_sampled_textures_per_shader_stage + } + + #[getter] + fn maxSamplersPerShaderStage(&self) -> u32 { + self.0.max_samplers_per_shader_stage + } + + #[getter] + fn maxStorageBuffersPerShaderStage(&self) -> u32 { + self.0.max_storage_buffers_per_shader_stage + } + + #[getter] + fn maxStorageTexturesPerShaderStage(&self) -> u32 { + self.0.max_storage_textures_per_shader_stage + } + + #[getter] + fn maxUniformBuffersPerShaderStage(&self) -> u32 { + self.0.max_uniform_buffers_per_shader_stage + } + + #[getter] + fn maxUniformBufferBindingSize(&self) -> u32 { + self.0.max_uniform_buffer_binding_size + } + + #[getter] + fn maxStorageBufferBindingSize(&self) -> u32 { + self.0.max_storage_buffer_binding_size + } + + #[getter] + fn minUniformBufferOffsetAlignment(&self) -> u32 { + self.0.min_uniform_buffer_offset_alignment + } + + #[getter] + fn minStorageBufferOffsetAlignment(&self) -> u32 { + self.0.min_storage_buffer_offset_alignment + } + + #[getter] + fn maxVertexBuffers(&self) -> u32 { + self.0.max_vertex_buffers + } + + #[getter] + #[number] + fn maxBufferSize(&self) -> u64 { + self.0.max_buffer_size + } + + #[getter] + fn maxVertexAttributes(&self) -> u32 { + self.0.max_vertex_attributes + } + + #[getter] + fn maxVertexBufferArrayStride(&self) -> u32 { + self.0.max_vertex_buffer_array_stride + } + + // TODO(@crowlKats): support max_inter_stage_shader_variables + + #[getter] + fn maxColorAttachments(&self) -> u32 { + self.0.max_color_attachments + } + + #[getter] + fn maxColorAttachmentBytesPerSample(&self) -> u32 { + self.0.max_color_attachment_bytes_per_sample + } + + #[getter] + fn maxComputeWorkgroupStorageSize(&self) -> u32 { + self.0.max_compute_workgroup_storage_size + } + + #[getter] + fn maxComputeInvocationsPerWorkgroup(&self) -> u32 { + self.0.max_compute_invocations_per_workgroup + } + + #[getter] + fn maxComputeWorkgroupSizeX(&self) -> u32 { + self.0.max_compute_workgroup_size_x + } + + #[getter] + fn maxComputeWorkgroupSizeY(&self) -> u32 { + self.0.max_compute_workgroup_size_y + } + + #[getter] + fn maxComputeWorkgroupSizeZ(&self) -> u32 { + self.0.max_compute_workgroup_size_z + } + + #[getter] + fn maxComputeWorkgroupsPerDimension(&self) -> u32 { + self.0.max_compute_workgroups_per_dimension + } +} + +pub struct GPUSupportedFeatures(v8::Global); + +impl GarbageCollected for GPUSupportedFeatures {} + +impl GPUSupportedFeatures { + pub fn new( + scope: &mut v8::HandleScope, + features: HashSet, + ) -> Self { + let set = v8::Set::new(scope); + + for feature in features { + let key = v8::String::new(scope, feature.as_str()).unwrap(); + set.add(scope, key.into()); + } + + Self(v8::Global::new(scope, >::from(set))) + } +} + +#[op2] +impl GPUSupportedFeatures { + #[global] + #[symbol("setlike_set")] + fn set(&self) -> v8::Global { + self.0.clone() + } +} + +pub struct GPUAdapterInfo(pub wgpu_types::AdapterInfo); + +impl GarbageCollected for GPUAdapterInfo {} + +#[op2] +impl GPUAdapterInfo { + #[getter] + #[string] + fn vendor(&self) -> String { + self.0.vendor.to_string() + } + + #[getter] + #[string] + fn architecture(&self) -> &'static str { + "" // TODO: wgpu#2170 + } + + #[getter] + #[string] + fn device(&self) -> String { + self.0.device.to_string() + } + + #[getter] + #[string] + fn description(&self) -> String { + self.0.name.clone() + } +} diff --git a/ext/webgpu/bind_group.rs b/ext/webgpu/bind_group.rs new file mode 100644 index 0000000000..80efba6efd --- /dev/null +++ b/ext/webgpu/bind_group.rs @@ -0,0 +1,122 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; + +use deno_core::cppgc::Ptr; +use deno_core::op2; +use deno_core::v8::HandleScope; +use deno_core::v8::Local; +use deno_core::v8::Value; +use deno_core::webidl::ContextFn; +use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; + +use crate::buffer::GPUBuffer; +use crate::sampler::GPUSampler; +use crate::texture::GPUTextureView; +use crate::Instance; + +pub struct GPUBindGroup { + pub instance: Instance, + pub id: wgpu_core::id::BindGroupId, + pub label: String, +} + +impl Drop for GPUBindGroup { + fn drop(&mut self) { + self.instance.bind_group_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPUBindGroup { + const NAME: &'static str = "GPUBindGroup"; +} + +impl GarbageCollected for GPUBindGroup {} + +#[op2] +impl GPUBindGroup { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBindGroupDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub layout: Ptr, + pub entries: Vec, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBindGroupEntry { + #[options(enforce_range = true)] + pub binding: u32, + pub resource: GPUBindingResource, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBufferBinding { + pub buffer: Ptr, + #[webidl(default = 0)] + #[options(enforce_range = true)] + pub offset: u64, + #[options(enforce_range = true)] + pub size: Option, +} + +pub(crate) enum GPUBindingResource { + Sampler(Ptr), + TextureView(Ptr), + BufferBinding(GPUBufferBinding), +} + +impl<'a> WebIdlConverter<'a> for GPUBindingResource { + type Options = (); + + fn convert<'b>( + scope: &mut HandleScope<'a>, + value: Local<'a, Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::Sampler) + .or_else(|_| { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::TextureView) + }) + .or_else(|_| { + GPUBufferBinding::convert(scope, value, prefix, context, options) + .map(Self::BufferBinding) + }) + } +} diff --git a/ext/webgpu/bind_group_layout.rs b/ext/webgpu/bind_group_layout.rs new file mode 100644 index 0000000000..3ebe594620 --- /dev/null +++ b/ext/webgpu/bind_group_layout.rs @@ -0,0 +1,180 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_core::op2; +use deno_core::GarbageCollected; +use deno_core::WebIDL; + +use crate::texture::GPUTextureViewDimension; +use crate::Instance; + +pub struct GPUBindGroupLayout { + pub instance: Instance, + pub id: wgpu_core::id::BindGroupLayoutId, + pub label: String, +} + +impl Drop for GPUBindGroupLayout { + fn drop(&mut self) { + self.instance.bind_group_layout_drop(self.id); + } +} + +impl deno_core::webidl::WebIdlInterfaceConverter for GPUBindGroupLayout { + const NAME: &'static str = "GPUBindGroupLayout"; +} + +impl GarbageCollected for GPUBindGroupLayout {} + +#[op2] +impl GPUBindGroupLayout { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBindGroupLayoutDescriptor { + #[webidl(default = String::new())] + pub label: String, + pub entries: Vec, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBindGroupLayoutEntry { + #[options(enforce_range = true)] + pub binding: u32, + #[options(enforce_range = true)] + pub visibility: u32, + pub buffer: Option, + pub sampler: Option, + pub texture: Option, + pub storage_texture: Option, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBufferBindingLayout { + #[webidl(default = GPUBufferBindingType::Uniform)] + pub r#type: GPUBufferBindingType, + #[webidl(default = false)] + pub has_dynamic_offset: bool, + #[webidl(default = 0)] + pub min_binding_size: u64, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUBufferBindingType { + Uniform, + Storage, + ReadOnlyStorage, +} + +impl From for wgpu_types::BufferBindingType { + fn from(value: GPUBufferBindingType) -> Self { + match value { + GPUBufferBindingType::Uniform => Self::Uniform, + GPUBufferBindingType::Storage => Self::Storage { read_only: false }, + GPUBufferBindingType::ReadOnlyStorage => { + Self::Storage { read_only: true } + } + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUSamplerBindingLayout { + #[webidl(default = GPUSamplerBindingType::Filtering)] + pub r#type: GPUSamplerBindingType, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUSamplerBindingType { + Filtering, + NonFiltering, + Comparison, +} + +impl From for wgpu_types::SamplerBindingType { + fn from(value: GPUSamplerBindingType) -> Self { + match value { + GPUSamplerBindingType::Filtering => Self::Filtering, + GPUSamplerBindingType::NonFiltering => Self::NonFiltering, + GPUSamplerBindingType::Comparison => Self::Comparison, + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUTextureBindingLayout { + #[webidl(default = GPUTextureSampleType::Float)] + pub sample_type: GPUTextureSampleType, + #[webidl(default = GPUTextureViewDimension::D2)] + pub view_dimension: GPUTextureViewDimension, + #[webidl(default = false)] + pub multisampled: bool, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUTextureSampleType { + Float, + UnfilterableFloat, + Depth, + Sint, + Uint, +} + +impl From for wgpu_types::TextureSampleType { + fn from(value: GPUTextureSampleType) -> Self { + match value { + GPUTextureSampleType::Float => Self::Float { filterable: true }, + GPUTextureSampleType::UnfilterableFloat => { + Self::Float { filterable: false } + } + GPUTextureSampleType::Depth => Self::Depth, + GPUTextureSampleType::Sint => Self::Sint, + GPUTextureSampleType::Uint => Self::Uint, + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUStorageTextureBindingLayout { + #[webidl(default = GPUStorageTextureAccess::WriteOnly)] + pub access: GPUStorageTextureAccess, + pub format: super::texture::GPUTextureFormat, + #[webidl(default = GPUTextureViewDimension::D2)] + pub view_dimension: GPUTextureViewDimension, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUStorageTextureAccess { + WriteOnly, + ReadOnly, + ReadWrite, +} + +impl From for wgpu_types::StorageTextureAccess { + fn from(value: GPUStorageTextureAccess) -> Self { + match value { + GPUStorageTextureAccess::WriteOnly => Self::WriteOnly, + GPUStorageTextureAccess::ReadOnly => Self::ReadOnly, + GPUStorageTextureAccess::ReadWrite => Self::ReadWrite, + } + } +} diff --git a/ext/webgpu/binding.rs b/ext/webgpu/binding.rs deleted file mode 100644 index c8441c64af..0000000000 --- a/ext/webgpu/binding.rs +++ /dev/null @@ -1,323 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use std::borrow::Cow; -use std::rc::Rc; - -use deno_core::error::ResourceError; -use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; - -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) { - gfx_select!(self.1 => self.0.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) { - gfx_select!(self.1 => self.0.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: wgpu_types::StorageTextureAccess, - format: wgpu_types::TextureFormat, - view_dimension: wgpu_types::TextureViewDimension, -} - -#[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, - 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, - None - ) => 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::, ResourceError>>()?; - - 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, - None - ) => 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::, ResourceError>>()?; - - 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, - None - ) => state, WebGpuBindGroup) -} diff --git a/ext/webgpu/buffer.rs b/ext/webgpu/buffer.rs index 25a5606e12..d111253773 100644 --- a/ext/webgpu/buffer.rs +++ b/ext/webgpu/buffer.rs @@ -1,209 +1,268 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; use std::cell::RefCell; use std::rc::Rc; -use std::sync::Arc; -use std::sync::Mutex; use std::time::Duration; +use deno_core::futures::channel::oneshot; use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; +use deno_core::v8; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use deno_error::JsErrorBox; +use wgpu_core::device::HostMap as MapMode; +use wgpu_core::resource::BufferMapCallback; -use super::error::WebGpuResult; +use crate::Instance; + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBufferDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub size: u64, + #[options(enforce_range = true)] + pub usage: u32, + #[webidl(default = false)] + pub mapped_at_creation: bool, +} #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BufferError { - #[class(inherit)] + #[class(generic)] #[error(transparent)] - Resource( - #[from] - #[inherit] - deno_core::error::ResourceError, - ), - #[class(type)] - #[error("usage is not valid")] - InvalidUsage, + Canceled(#[from] oneshot::Canceled), #[class("DOMExceptionOperationError")] #[error(transparent)] - Access(wgpu_core::resource::BufferAccessError), + Access(#[from] wgpu_core::resource::BufferAccessError), + #[class("DOMExceptionOperationError")] + #[error("{0}")] + Operation(&'static str), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), } -pub(crate) struct WebGpuBuffer( - pub(crate) super::Instance, - pub(crate) wgpu_core::id::BufferId, -); -impl Resource for WebGpuBuffer { - fn name(&self) -> Cow { - "webGPUBuffer".into() - } +pub struct GPUBuffer { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - fn close(self: Rc) { - gfx_select!(self.1 => self.0.buffer_drop(self.1, true)); + pub id: wgpu_core::id::BufferId, + pub device: wgpu_core::id::DeviceId, + + pub label: String, + + pub size: u64, + pub usage: u32, + + pub map_state: RefCell<&'static str>, + pub map_mode: RefCell>, + + pub mapped_js_buffers: RefCell>>, +} + +impl Drop for GPUBuffer { + fn drop(&mut self) { + self.instance.buffer_drop(self.id); } } -struct WebGpuBufferMapped(*mut u8, usize); -impl Resource for WebGpuBufferMapped { - fn name(&self) -> Cow { - "webGPUBufferMapped".into() - } +impl WebIdlInterfaceConverter for GPUBuffer { + const NAME: &'static str = "GPUBuffer"; } +impl GarbageCollected for GPUBuffer {} + #[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(BufferError::InvalidUsage)?, - mapped_at_creation, - }; - - gfx_put!(device => instance.device_create_buffer( - device, - &descriptor, - None - ) => 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 device; - let done = Arc::new(Mutex::new(None)); - { - 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 done_ = done.clone(); - let callback = Box::new(move |status| { - *done_.lock().unwrap() = Some(status); - }); - - let maybe_err = gfx_select!(buffer => instance.buffer_map_async( - buffer, - offset, - Some(size), - wgpu_core::resource::BufferMapOperation { - host: match mode { - 1 => wgpu_core::device::HostMap::Read, - 2 => wgpu_core::device::HostMap::Write, - _ => unreachable!(), - }, - callback: Some(wgpu_core::resource::BufferMapCallback::from_rust(callback)), - } - )) - .err(); - - if maybe_err.is_some() { - return Ok(WebGpuResult::maybe_err(maybe_err)); - } +impl GPUBuffer { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label } - loop { - let result = done.lock().unwrap().take(); - match result { - Some(Ok(())) => return Ok(WebGpuResult::empty()), - Some(Err(e)) => return Err(BufferError::Access(e)), - None => { + #[getter] + #[number] + fn size(&self) -> u64 { + self.size + } + #[getter] + fn usage(&self) -> u32 { + self.usage + } + + #[getter] + #[string] + fn map_state(&self) -> &'static str { + *self.map_state.borrow() + } + + #[async_method] + async fn map_async( + &self, + #[webidl(options(enforce_range = true))] mode: u32, + #[webidl(default = 0)] offset: u64, + #[webidl] size: Option, + ) -> Result<(), BufferError> { + let read_mode = (mode & 0x0001) == 0x0001; + let write_mode = (mode & 0x0002) == 0x0002; + if (read_mode && write_mode) || (!read_mode && !write_mode) { + return Err(BufferError::Operation( + "exactly one of READ or WRITE map mode must be set", + )); + } + + let mode = if read_mode { + MapMode::Read + } else { + assert!(write_mode); + MapMode::Write + }; + + { + *self.map_state.borrow_mut() = "pending"; + } + + let (sender, receiver) = + oneshot::channel::(); + + { + let callback = Box::new(move |status| { + sender.send(status).unwrap(); + }); + + let err = self + .instance + .buffer_map_async( + self.id, + offset, + size, + wgpu_core::resource::BufferMapOperation { + host: mode, + callback: Some(BufferMapCallback::from_rust(callback)), + }, + ) + .err(); + + if err.is_some() { + self.error_handler.push_error(err); + return Err(BufferError::Operation("validation error occurred")); + } + } + + 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::Poll)).unwrap(); + self + .instance + .device_poll(self.device, wgpu_types::Maintain::wait()) + .unwrap(); } tokio::time::sleep(Duration::from_millis(10)).await; } - } - } -} - -#[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(BufferError::Access)?; - - // 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) + Ok::<(), BufferError>(()) }; - slice.copy_from_slice(buf); + + let receiver_fut = async move { + receiver.await??; + let mut done = done_.borrow_mut(); + *done = true; + Ok::<(), BufferError>(()) + }; + + tokio::try_join!(device_poll_fut, receiver_fut)?; + + *self.map_state.borrow_mut() = "mapped"; + *self.map_mode.borrow_mut() = Some(mode); + + Ok(()) } - gfx_ok!(buffer => instance.buffer_unmap(buffer)) + fn get_mapped_range<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + #[webidl(default = 0)] offset: u64, + #[webidl] size: Option, + ) -> Result, BufferError> { + let size = size.unwrap_or_else(|| self.size.saturating_sub(offset)); + + let (slice_pointer, range_size) = self + .instance + .buffer_get_mapped_range(self.id, offset, Some(size)) + .map_err(BufferError::Access)?; + + let mode = self.map_mode.borrow(); + let mode = mode.as_ref().unwrap(); + + let bs = if mode == &MapMode::Write { + unsafe extern "C" fn noop_deleter_callback( + _data: *mut std::ffi::c_void, + _byte_length: usize, + _deleter_data: *mut std::ffi::c_void, + ) { + } + + // SAFETY: creating a backing store from the pointer and length provided by wgpu + unsafe { + v8::ArrayBuffer::new_backing_store_from_ptr( + slice_pointer.as_ptr() as _, + range_size as usize, + noop_deleter_callback, + std::ptr::null_mut(), + ) + } + } else { + // SAFETY: creating a vector from the pointer and length provided by wgpu + let slice = unsafe { + std::slice::from_raw_parts(slice_pointer.as_ptr(), range_size as usize) + }; + v8::ArrayBuffer::new_backing_store_from_vec(slice.to_vec()) + }; + + let shared_bs = bs.make_shared(); + let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs); + + if mode == &MapMode::Write { + self + .mapped_js_buffers + .borrow_mut() + .push(v8::Global::new(scope, ab)); + } + + Ok(v8::Uint8Array::new(scope, ab, 0, range_size as usize).unwrap()) + } + + #[nofast] + fn unmap(&self, scope: &mut v8::HandleScope) -> Result<(), BufferError> { + for ab in self.mapped_js_buffers.replace(vec![]) { + let ab = ab.open(scope); + ab.detach(None); + } + + self + .instance + .buffer_unmap(self.id) + .map_err(BufferError::Access)?; + + *self.map_state.borrow_mut() = "unmapped"; + + Ok(()) + } + + #[fast] + fn destroy(&self) -> Result<(), JsErrorBox> { + self + .instance + .buffer_destroy(self.id) + .map_err(|e| JsErrorBox::generic(e.to_string())) + } } diff --git a/ext/webgpu/bundle.rs b/ext/webgpu/bundle.rs deleted file mode 100644 index 73c3c9f221..0000000000 --- a/ext/webgpu/bundle.rs +++ /dev/null @@ -1,413 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use std::borrow::Cow; -use std::cell::RefCell; -use std::rc::Rc; - -use deno_core::error::ResourceError; -use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; - -use super::error::WebGpuResult; - -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum BundleError { - #[class(inherit)] - #[error(transparent)] - Resource( - #[from] - #[inherit] - ResourceError, - ), - #[class(type)] - #[error("size must be larger than 0")] - InvalidSize, -} - -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) { - gfx_select!(self.1 => self.0.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), - }, - None - ) => 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(BundleError::InvalidSize)?); - - 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(BundleError::InvalidSize)?) - } 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/byow.rs b/ext/webgpu/byow.rs index e911e1402b..84e97a1460 100644 --- a/ext/webgpu/byow.rs +++ b/ext/webgpu/byow.rs @@ -1,5 +1,4 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - +use std::cell::RefCell; use std::ffi::c_void; #[cfg(any( target_os = "linux", @@ -9,11 +8,17 @@ use std::ffi::c_void; ))] use std::ptr::NonNull; +use deno_core::cppgc::SameObject; use deno_core::op2; +use deno_core::v8; +use deno_core::v8::Local; +use deno_core::v8::Value; +use deno_core::FromV8; +use deno_core::GarbageCollected; use deno_core::OpState; -use deno_core::ResourceId; +use deno_error::JsErrorBox; -use crate::surface::WebGpuSurface; +use crate::surface::GPUCanvasContext; #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ByowError { @@ -65,46 +70,156 @@ pub enum ByowError { NSViewDisplay, } -#[op2(fast)] -#[smi] -pub fn op_webgpu_surface_create( - state: &mut OpState, - #[string] system: &str, - p1: *const c_void, - p2: *const c_void, -) -> Result { - let instance = state - .try_borrow::() - .ok_or(ByowError::WebGPUNotInitiated)?; - // Security note: - // - // The `p1` and `p2` parameters are pointers to platform-specific window - // handles. - // - // The code below works under the assumption that: - // - // - handles can only be created by the FFI interface which - // enforces --allow-ffi. - // - // - `*const c_void` deserizalizes null and v8::External. - // - // - Only FFI can export v8::External to user code. - if p1.is_null() { - return Err(ByowError::InvalidParameters); +// TODO(@littledivy): This will extend `OffscreenCanvas` when we add it. +pub struct UnsafeWindowSurface { + pub id: wgpu_core::id::SurfaceId, + pub width: RefCell, + pub height: RefCell, + + pub context: SameObject, +} + +impl GarbageCollected for UnsafeWindowSurface {} + +#[op2] +impl UnsafeWindowSurface { + #[constructor] + #[cppgc] + fn new( + state: &mut OpState, + #[from_v8] options: UnsafeWindowSurfaceOptions, + ) -> Result { + let instance = state + .try_borrow::() + .ok_or(ByowError::WebGPUNotInitiated)?; + + // Security note: + // + // The `window_handle` and `display_handle` options are pointers to + // platform-specific window handles. + // + // The code below works under the assumption that: + // + // - handles can only be created by the FFI interface which + // enforces --allow-ffi. + // + // - `*const c_void` deserizalizes null and v8::External. + // + // - Only FFI can export v8::External to user code. + if options.window_handle.is_null() { + return Err(ByowError::InvalidParameters); + } + + let (win_handle, display_handle) = raw_window( + options.system, + options.window_handle, + options.display_handle, + )?; + + // SAFETY: see above comment + let id = unsafe { + instance + .instance_create_surface(display_handle, win_handle, None) + .map_err(ByowError::CreateSurface)? + }; + + Ok(UnsafeWindowSurface { + id, + width: RefCell::new(options.width), + height: RefCell::new(options.height), + context: SameObject::new(), + }) } - let (win_handle, display_handle) = raw_window(system, p1, p2)?; - // SAFETY: see above comment - let surface = unsafe { - instance - .instance_create_surface(display_handle, win_handle, None) - .map_err(ByowError::CreateSurface)? - }; + #[global] + fn get_context(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.context.get(scope, |_| GPUCanvasContext { + surface_id: self.id, + width: self.width.clone(), + height: self.height.clone(), + config: RefCell::new(None), + texture: RefCell::new(None), + }) + } - let rid = state - .resource_table - .add(WebGpuSurface(instance.clone(), surface)); - Ok(rid) + #[nofast] + fn present(&self, scope: &mut v8::HandleScope) -> Result<(), JsErrorBox> { + let Some(context) = self.context.value(scope) else { + return Err(JsErrorBox::type_error("getContext was never called")); + }; + + context.present().map_err(JsErrorBox::from_err) + } +} + +struct UnsafeWindowSurfaceOptions { + system: UnsafeWindowSurfaceSystem, + window_handle: *const c_void, + display_handle: *const c_void, + width: u32, + height: u32, +} + +#[derive(Eq, PartialEq)] +enum UnsafeWindowSurfaceSystem { + Cocoa, + Win32, + X11, + Wayland, +} + +impl<'a> FromV8<'a> for UnsafeWindowSurfaceOptions { + type Error = JsErrorBox; + + fn from_v8( + scope: &mut v8::HandleScope<'a>, + value: Local<'a, Value>, + ) -> Result { + let obj = value.try_cast::().unwrap(); + + let key = v8::String::new(scope, "system").unwrap(); + let val = obj.get(scope, key.into()).unwrap(); + let s = String::from_v8(scope, val).unwrap(); + let system = match s.as_str() { + "cocoa" => UnsafeWindowSurfaceSystem::Cocoa, + "win32" => UnsafeWindowSurfaceSystem::Win32, + "x11" => UnsafeWindowSurfaceSystem::X11, + "wayland" => UnsafeWindowSurfaceSystem::Wayland, + _ => { + return Err(JsErrorBox::type_error(format!( + "Invalid system kind '{s}'" + ))) + } + }; + + let key = v8::String::new(scope, "windowHandle").unwrap(); + let val = obj.get(scope, key.into()).unwrap(); + let Some(window_handle) = deno_core::_ops::to_external_option(&val) else { + return Err(JsErrorBox::type_error("expected external")); + }; + + let key = v8::String::new(scope, "displayHandle").unwrap(); + let val = obj.get(scope, key.into()).unwrap(); + let Some(display_handle) = deno_core::_ops::to_external_option(&val) else { + return Err(JsErrorBox::type_error("expected external")); + }; + + let key = v8::String::new(scope, "width").unwrap(); + let val = obj.get(scope, key.into()).unwrap(); + let width = deno_core::convert::Number::::from_v8(scope, val)?.0; + + let key = v8::String::new(scope, "height").unwrap(); + let val = obj.get(scope, key.into()).unwrap(); + let height = deno_core::convert::Number::::from_v8(scope, val)?.0; + + Ok(Self { + system, + window_handle, + display_handle, + width, + height, + }) + } } type RawHandles = ( @@ -114,11 +229,11 @@ type RawHandles = ( #[cfg(target_os = "macos")] fn raw_window( - system: &str, + system: UnsafeWindowSurfaceSystem, _ns_window: *const c_void, ns_view: *const c_void, ) -> Result { - if system != "cocoa" { + if system != UnsafeWindowSurfaceSystem::Cocoa { return Err(ByowError::InvalidSystem); } @@ -136,12 +251,12 @@ fn raw_window( #[cfg(target_os = "windows")] fn raw_window( - system: &str, + system: UnsafeWindowSurfaceSystem, window: *const c_void, hinstance: *const c_void, ) -> Result { use raw_window_handle::WindowsDisplayHandle; - if system != "win32" { + if system != UnsafeWindowSurfaceSystem::Win32 { return Err(ByowError::InvalidSystem); } @@ -162,12 +277,12 @@ fn raw_window( #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] fn raw_window( - system: &str, + system: UnsafeWindowSurfaceSystem, window: *const c_void, display: *const c_void, ) -> Result { let (win_handle, display_handle); - if system == "x11" { + if system == UnsafeWindowSurfaceSystem::X11 { win_handle = raw_window_handle::RawWindowHandle::Xlib( raw_window_handle::XlibWindowHandle::new(window as *mut c_void as _), ); @@ -178,7 +293,7 @@ fn raw_window( 0, ), ); - } else if system == "wayland" { + } else if system == UnsafeWindowSurfaceSystem::Wayland { win_handle = raw_window_handle::RawWindowHandle::Wayland( raw_window_handle::WaylandWindowHandle::new( NonNull::new(window as *mut c_void).ok_or(ByowError::NullWindow)?, @@ -205,7 +320,7 @@ fn raw_window( target_os = "openbsd", )))] fn raw_window( - _system: &str, + _system: UnsafeWindowSurfaceSystem, _window: *const c_void, _display: *const c_void, ) -> Result { diff --git a/ext/webgpu/command_buffer.rs b/ext/webgpu/command_buffer.rs new file mode 100644 index 0000000000..d03311376c --- /dev/null +++ b/ext/webgpu/command_buffer.rs @@ -0,0 +1,46 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_core::op2; +use deno_core::GarbageCollected; +use deno_core::WebIDL; + +use crate::Instance; + +pub struct GPUCommandBuffer { + pub instance: Instance, + pub id: wgpu_core::id::CommandBufferId, + pub label: String, +} + +impl Drop for GPUCommandBuffer { + fn drop(&mut self) { + self.instance.command_buffer_drop(self.id); + } +} + +impl deno_core::webidl::WebIdlInterfaceConverter for GPUCommandBuffer { + const NAME: &'static str = "GPUCommandBuffer"; +} + +impl GarbageCollected for GPUCommandBuffer {} + +#[op2] +impl GPUCommandBuffer { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUCommandBufferDescriptor { + #[webidl(default = String::new())] + pub label: String, +} diff --git a/ext/webgpu/command_encoder.rs b/ext/webgpu/command_encoder.rs index 9b6bb44ae8..a468afbf9d 100644 --- a/ext/webgpu/command_encoder.rs +++ b/ext/webgpu/command_encoder.rs @@ -2,633 +2,410 @@ use std::borrow::Cow; use std::cell::RefCell; -use std::rc::Rc; -use deno_core::error::ResourceError; +use deno_core::cppgc::Ptr; use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use deno_error::JsErrorBox; +use wgpu_core::command::ImageCopyBuffer; +use wgpu_core::command::PassChannel; -use super::error::WebGpuResult; -use crate::WebGpuQuerySet; +use crate::buffer::GPUBuffer; +use crate::command_buffer::GPUCommandBuffer; +use crate::compute_pass::GPUComputePassEncoder; +use crate::queue::GPUTexelCopyTextureInfo; +use crate::render_pass::GPULoadOp; +use crate::render_pass::GPURenderPassEncoder; +use crate::render_pass::GPUStoreOp; +use crate::webidl::GPUExtent3D; +use crate::Instance; -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() - } +pub struct GPUCommandEncoder { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - fn close(self: Rc) { - gfx_select!(self.1 => self.0.command_encoder_drop(self.1)); + pub id: wgpu_core::id::CommandEncoderId, + pub label: String, +} + +impl Drop for GPUCommandEncoder { + fn drop(&mut self) { + self.instance.command_encoder_drop(self.id); } } -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() { - gfx_select!(id => self.0.command_buffer_drop(id)); - } - } -} +impl GarbageCollected for GPUCommandEncoder {} #[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; +impl GPUCommandEncoder { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } - let descriptor = wgpu_types::CommandEncoderDescriptor { label: Some(label) }; - - gfx_put!(device => instance.device_create_command_encoder( - device, - &descriptor, - None - ) => 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: Option, - 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) + #[required(1)] + #[cppgc] + fn begin_render_pass( + &self, + #[webidl] descriptor: crate::render_pass::GPURenderPassDescriptor, + ) -> Result { + let color_attachments = Cow::Owned( + descriptor + .color_attachments + .into_iter() + .map(|attachment| { + attachment.into_option().map(|attachment| { + wgpu_core::command::RenderPassColorAttachment { + view: attachment.view.id, + resolve_target: attachment.resolve_target.map(|target| target.id), + channel: PassChannel { + load_op: attachment.load_op.into(), + store_op: attachment.store_op.into(), + clear_value: attachment + .clear_value + .map(Into::into) + .unwrap_or_default(), + read_only: false, + }, + } }) - .transpose()? - .map(|texture| texture.1); + }) + .collect::>(), + ); - 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, + let depth_stencil_attachment = + descriptor.depth_stencil_attachment.map(|attachment| { + if attachment.depth_load_op.as_ref().is_some_and(|op| matches!(op, GPULoadOp::Clear)) && attachment.depth_clear_value.is_none() { + return Err(JsErrorBox::type_error(r#"'depthClearValue' must be specified when 'depthLoadOp' is "clear""#)); + } + + Ok(wgpu_core::command::RenderPassDepthStencilAttachment { + view: attachment.view.id, + depth: PassChannel { + load_op: attachment.depth_load_op.unwrap_or(GPULoadOp::Load).into(), + store_op: attachment + .depth_store_op + .unwrap_or(GPUStoreOp::Store) + .into(), + clear_value: attachment.depth_clear_value.unwrap_or_default(), + read_only: attachment.depth_read_only, + }, + stencil: PassChannel { + load_op: attachment + .stencil_load_op + .unwrap_or(GPULoadOp::Load) + .into(), + store_op: attachment + .stencil_store_op + .unwrap_or(GPUStoreOp::Store) + .into(), + clear_value: attachment.stencil_clear_value, + read_only: attachment.stencil_read_only, }, }) - } else { - None - }; - Ok(rp_at) - }) - .collect::, ResourceError>>()?; + }).transpose()?; - 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), - // In "01_webgpu.js", `depthLoadOp` is cheked to ensure its value is not "clear" - // when `depthClearValue` is undefined, so the default 0.0 doesn't matter. - clear_value: attachment.depth_clear_value.unwrap_or(0.0), - 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 = + descriptor.timestamp_writes.map(|timestamp_writes| { + wgpu_core::command::PassTimestampWrites { + query_set: timestamp_writes.query_set.id, + beginning_of_pass_write_index: timestamp_writes + .beginning_of_pass_write_index, + end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, + } }); + + let wgpu_descriptor = wgpu_core::command::RenderPassDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + color_attachments, + depth_stencil_attachment: depth_stencil_attachment.as_ref(), + timestamp_writes: timestamp_writes.as_ref(), + occlusion_query_set: descriptor + .occlusion_query_set + .map(|query_set| query_set.id), + }; + + let (render_pass, err) = self + .instance + .command_encoder_create_render_pass(self.id, &wgpu_descriptor); + + self.error_handler.push_error(err); + + Ok(GPURenderPassEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + render_pass: RefCell::new(render_pass), + label: descriptor.label, + }) } - 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; + #[cppgc] + fn begin_compute_pass( + &self, + #[webidl] descriptor: crate::compute_pass::GPUComputePassDescriptor, + ) -> GPUComputePassEncoder { + let timestamp_writes = + descriptor.timestamp_writes.map(|timestamp_writes| { + wgpu_core::command::PassTimestampWrites { + query_set: timestamp_writes.query_set.id, + beginning_of_pass_write_index: timestamp_writes + .beginning_of_pass_write_index, + end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, + } + }); - 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 wgpu_descriptor = wgpu_core::command::ComputePassDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + timestamp_writes: timestamp_writes.as_ref(), + }; - let occlusion_query_set_resource = occlusion_query_set - .map(|rid| state.resource_table.get::(rid)) - .transpose()? - .map(|query_set| query_set.1); + let (compute_pass, err) = self + .instance + .command_encoder_create_compute_pass(self.id, &wgpu_descriptor); - 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, - }; + self.error_handler.push_error(err); - let render_pass = wgpu_core::command::RenderPass::new( - command_encoder_resource.1, - &descriptor, - ); + GPUComputePassEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + compute_pass: RefCell::new(compute_pass), + label: descriptor.label, + } + } - let rid = state - .resource_table - .add(super::render_pass::WebGpuRenderPass(RefCell::new( - render_pass, - ))); + #[required(5)] + fn copy_buffer_to_buffer( + &self, + #[webidl] source: Ptr, + #[webidl(options(enforce_range = true))] source_offset: u64, + #[webidl] destination: Ptr, + #[webidl(options(enforce_range = true))] destination_offset: u64, + #[webidl(options(enforce_range = true))] size: u64, + ) { + let err = self + .instance + .command_encoder_copy_buffer_to_buffer( + self.id, + source.id, + source_offset, + destination.id, + destination_offset, + size, + ) + .err(); - Ok(WebGpuResult::rid(rid)) + self.error_handler.push_error(err); + } + + #[required(3)] + fn copy_buffer_to_texture( + &self, + #[webidl] source: GPUTexelCopyBufferInfo, + #[webidl] destination: GPUTexelCopyTextureInfo, + #[webidl] copy_size: GPUExtent3D, + ) { + let source = ImageCopyBuffer { + buffer: source.buffer.id, + layout: wgpu_types::ImageDataLayout { + offset: source.offset, + bytes_per_row: source.bytes_per_row, + rows_per_image: source.rows_per_image, + }, + }; + let destination = wgpu_types::ImageCopyTexture { + texture: destination.texture.id, + mip_level: destination.mip_level, + origin: destination.origin.into(), + aspect: destination.aspect.into(), + }; + + let err = self + .instance + .command_encoder_copy_buffer_to_texture( + self.id, + &source, + &destination, + ©_size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } + + #[required(3)] + fn copy_texture_to_buffer( + &self, + #[webidl] source: GPUTexelCopyTextureInfo, + #[webidl] destination: GPUTexelCopyBufferInfo, + #[webidl] copy_size: GPUExtent3D, + ) { + let source = wgpu_types::ImageCopyTexture { + texture: source.texture.id, + mip_level: source.mip_level, + origin: source.origin.into(), + aspect: source.aspect.into(), + }; + let destination = ImageCopyBuffer { + buffer: destination.buffer.id, + layout: wgpu_types::ImageDataLayout { + offset: destination.offset, + bytes_per_row: destination.bytes_per_row, + rows_per_image: destination.rows_per_image, + }, + }; + + let err = self + .instance + .command_encoder_copy_texture_to_buffer( + self.id, + &source, + &destination, + ©_size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } + + #[required(3)] + fn copy_texture_to_texture( + &self, + #[webidl] source: GPUTexelCopyTextureInfo, + #[webidl] destination: GPUTexelCopyTextureInfo, + #[webidl] copy_size: GPUExtent3D, + ) { + let source = wgpu_types::ImageCopyTexture { + texture: source.texture.id, + mip_level: source.mip_level, + origin: source.origin.into(), + aspect: source.aspect.into(), + }; + let destination = wgpu_types::ImageCopyTexture { + texture: destination.texture.id, + mip_level: destination.mip_level, + origin: destination.origin.into(), + aspect: destination.aspect.into(), + }; + + let err = self + .instance + .command_encoder_copy_texture_to_texture( + self.id, + &source, + &destination, + ©_size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } + + #[required(1)] + fn clear_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let err = self + .instance + .command_encoder_clear_buffer(self.id, buffer.id, offset, size) + .err(); + self.error_handler.push_error(err); + } + + #[required(5)] + fn resolve_query_set( + &self, + #[webidl] query_set: Ptr, + #[webidl(options(enforce_range = true))] first_query: u32, + #[webidl(options(enforce_range = true))] query_count: u32, + #[webidl] destination: Ptr, + #[webidl(options(enforce_range = true))] destination_offset: u64, + ) { + let err = self + .instance + .command_encoder_resolve_query_set( + self.id, + query_set.id, + first_query, + query_count, + destination.id, + destination_offset, + ) + .err(); + + self.error_handler.push_error(err); + } + + #[cppgc] + fn finish( + &self, + #[webidl] descriptor: crate::command_buffer::GPUCommandBufferDescriptor, + ) -> GPUCommandBuffer { + let wgpu_descriptor = wgpu_types::CommandBufferDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + }; + + let (id, err) = self + .instance + .command_encoder_finish(self.id, &wgpu_descriptor); + + self.error_handler.push_error(err); + + GPUCommandBuffer { + instance: self.instance.clone(), + id, + label: descriptor.label, + } + } + + fn push_debug_group(&self, #[webidl] group_label: String) { + let err = self + .instance + .command_encoder_push_debug_group(self.id, &group_label) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + fn pop_debug_group(&self) { + let err = self.instance.command_encoder_pop_debug_group(self.id).err(); + self.error_handler.push_error(err); + } + + fn insert_debug_marker(&self, #[webidl] marker_label: String) { + let err = self + .instance + .command_encoder_insert_debug_marker(self.id, &marker_label) + .err(); + self.error_handler.push_error(err); + } } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct GPUComputePassTimestampWrites { - query_set: ResourceId, - beginning_of_pass_write_index: Option, - end_of_pass_write_index: Option, +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUCommandEncoderDescriptor { + #[webidl(default = String::new())] + pub label: String, } -#[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, +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUTexelCopyBufferInfo { + pub buffer: Ptr, + #[webidl(default = 0)] + #[options(enforce_range = true)] offset: u64, + #[options(enforce_range = true)] bytes_per_row: Option, + #[options(enforce_range = true)] 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, - Some(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 index afa19b3fac..f9c837ed33 100644 --- a/ext/webgpu/compute_pass.rs +++ b/ext/webgpu/compute_pass.rs @@ -3,209 +3,231 @@ use std::borrow::Cow; use std::cell::RefCell; -use deno_core::error::ResourceError; +use deno_core::cppgc::Ptr; use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; +use deno_core::v8; +use deno_core::webidl::IntOptions; +use deno_core::webidl::Nullable; +use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; +use deno_core::GarbageCollected; +use deno_core::WebIDL; -use super::error::WebGpuResult; +use crate::Instance; -pub(crate) struct WebGpuComputePass( - pub(crate) RefCell, -); -impl Resource for WebGpuComputePass { - fn name(&self) -> Cow { - "webGPUComputePass".into() +pub struct GPUComputePassEncoder { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, + + pub compute_pass: RefCell, + pub label: String, +} + +impl GarbageCollected for GPUComputePassEncoder {} + +#[op2] +impl GPUComputePassEncoder { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + fn set_pipeline( + &self, + #[webidl] pipeline: Ptr, + ) { + let err = self + .instance + .compute_pass_set_pipeline( + &mut self.compute_pass.borrow_mut(), + pipeline.id, + ) + .err(); + self.error_handler.push_error(err); + } + + fn dispatch_workgroups( + &self, + #[webidl(options(enforce_range = true))] work_group_count_x: u32, + #[webidl(default = 1, options(enforce_range = true))] + work_group_count_y: u32, + #[webidl(default = 1, options(enforce_range = true))] + work_group_count_z: u32, + ) { + let err = self + .instance + .compute_pass_dispatch_workgroups( + &mut self.compute_pass.borrow_mut(), + work_group_count_x, + work_group_count_y, + work_group_count_z, + ) + .err(); + self.error_handler.push_error(err); + } + + fn dispatch_workgroups_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) { + let err = self + .instance + .compute_pass_dispatch_workgroups_indirect( + &mut self.compute_pass.borrow_mut(), + indirect_buffer.id, + indirect_offset, + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + fn end(&self) { + let err = self + .instance + .compute_pass_end(&mut self.compute_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + fn push_debug_group(&self, #[webidl] group_label: String) { + let err = self + .instance + .compute_pass_push_debug_group( + &mut self.compute_pass.borrow_mut(), + &group_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + fn pop_debug_group(&self) { + let err = self + .instance + .compute_pass_pop_debug_group(&mut self.compute_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + fn insert_debug_marker(&self, #[webidl] marker_label: String) { + let err = self + .instance + .compute_pass_insert_debug_marker( + &mut self.compute_pass.borrow_mut(), + &marker_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + fn set_bind_group<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + #[webidl(options(enforce_range = true))] index: u32, + #[webidl] bind_group: Nullable>, + dynamic_offsets: v8::Local<'a, v8::Value>, + dynamic_offsets_data_start: v8::Local<'a, v8::Value>, + dynamic_offsets_data_length: v8::Local<'a, v8::Value>, + ) -> Result<(), WebIdlError> { + const PREFIX: &str = + "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; + let err = if let Ok(uint_32) = dynamic_offsets.try_cast::() + { + let start = u64::convert( + scope, + dynamic_offsets_data_start, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 4")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + let len = u32::convert( + scope, + dynamic_offsets_data_length, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 5")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + + let ab = uint_32.buffer(scope).unwrap(); + let ptr = ab.data().unwrap(); + let ab_len = ab.byte_length() / 4; + + // SAFETY: compute_pass_set_bind_group internally calls extend_from_slice with this slice + let data = + unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; + + let offsets = &data[start..(start + len)]; + + self + .instance + .compute_pass_set_bind_group( + &mut self.compute_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets, + ) + .err() + } else { + let offsets = >>::convert( + scope, + dynamic_offsets, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 3")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? + .unwrap_or_default(); + + self + .instance + .compute_pass_set_bind_group( + &mut self.compute_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + &offsets, + ) + .err() + }; + + self.error_handler.push_error(err); + + Ok(()) } } -#[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)?; +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUComputePassDescriptor { + #[webidl(default = String::new())] + pub label: String, - wgpu_core::command::compute_commands::wgpu_compute_pass_set_pipeline( - &mut compute_pass_resource.0.borrow_mut(), - compute_pipeline_resource.1, - ); - - Ok(WebGpuResult::empty()) + pub timestamp_writes: Option, } -#[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_commands::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_commands::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]; - - wgpu_core::command::compute_commands::wgpu_compute_pass_set_bind_group( - &mut compute_pass_resource.0.borrow_mut(), - index, - bind_group_resource.1, - dynamic_offsets_data, - ); - - 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)?; - - wgpu_core::command::compute_commands::wgpu_compute_pass_push_debug_group( - &mut compute_pass_resource.0.borrow_mut(), - group_label, - 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_commands::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)?; - - wgpu_core::command::compute_commands::wgpu_compute_pass_insert_debug_marker( - &mut compute_pass_resource.0.borrow_mut(), - marker_label, - 0, // wgpu#975 - ); - - Ok(WebGpuResult::empty()) +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUComputePassTimestampWrites { + pub query_set: Ptr, + #[options(enforce_range = true)] + pub beginning_of_pass_write_index: Option, + #[options(enforce_range = true)] + pub end_of_pass_write_index: Option, } diff --git a/ext/webgpu/compute_pipeline.rs b/ext/webgpu/compute_pipeline.rs new file mode 100644 index 0000000000..c8c1d4a888 --- /dev/null +++ b/ext/webgpu/compute_pipeline.rs @@ -0,0 +1,82 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_core::cppgc::Ptr; +use deno_core::op2; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use indexmap::IndexMap; + +use crate::bind_group_layout::GPUBindGroupLayout; +use crate::shader::GPUShaderModule; +use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode; +use crate::Instance; + +pub struct GPUComputePipeline { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, + + pub id: wgpu_core::id::ComputePipelineId, + pub label: String, +} + +impl Drop for GPUComputePipeline { + fn drop(&mut self) { + self.instance.compute_pipeline_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPUComputePipeline { + const NAME: &'static str = "GPUComputePipeline"; +} + +impl GarbageCollected for GPUComputePipeline {} + +#[op2] +impl GPUComputePipeline { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[cppgc] + fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout { + let (id, err) = self + .instance + .compute_pipeline_get_bind_group_layout(self.id, index, None); + + self.error_handler.push_error(err); + + // TODO: wgpu needs to support retrieving the label + GPUBindGroupLayout { + instance: self.instance.clone(), + id, + label: "".to_string(), + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUComputePipelineDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub compute: GPUProgrammableStage, + pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUProgrammableStage { + pub module: Ptr, + pub entry_point: Option, + #[webidl(default = Default::default())] + pub constants: IndexMap, +} diff --git a/ext/webgpu/device.rs b/ext/webgpu/device.rs new file mode 100644 index 0000000000..70f35d8e2c --- /dev/null +++ b/ext/webgpu/device.rs @@ -0,0 +1,869 @@ +use std::borrow::Cow; +use std::cell::RefCell; +use std::future::Future; +use std::num::NonZeroU64; +use std::rc::Rc; + +use deno_core::cppgc::SameObject; +use deno_core::op2; +use deno_core::v8; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_error::JsErrorBox; +use wgpu_core::binding_model::BindingResource; +use wgpu_core::pipeline::ProgrammableStageDescriptor; +use wgpu_types::BindingType; + +use super::bind_group::GPUBindGroup; +use super::bind_group::GPUBindingResource; +use super::bind_group_layout::GPUBindGroupLayout; +use super::buffer::GPUBuffer; +use super::compute_pipeline::GPUComputePipeline; +use super::pipeline_layout::GPUPipelineLayout; +use super::queue::GPUQueue; +use super::sampler::GPUSampler; +use super::shader::GPUShaderModule; +use super::texture::GPUTexture; +use crate::adapter::GPUAdapterInfo; +use crate::adapter::GPUSupportedFeatures; +use crate::adapter::GPUSupportedLimits; +use crate::command_encoder::GPUCommandEncoder; +use crate::query_set::GPUQuerySet; +use crate::render_bundle::GPURenderBundleEncoder; +use crate::render_pipeline::GPURenderPipeline; +use crate::webidl::features_to_feature_names; +use crate::Instance; + +pub struct GPUDevice { + pub instance: Instance, + pub id: wgpu_core::id::DeviceId, + pub adapter: wgpu_core::id::AdapterId, + pub queue: wgpu_core::id::QueueId, + + pub label: String, + + pub features: SameObject, + pub limits: SameObject, + pub adapter_info: Rc>, + + pub queue_obj: SameObject, + + pub error_handler: super::error::ErrorHandler, + pub lost_receiver: + tokio::sync::Mutex>>, +} + +impl Drop for GPUDevice { + fn drop(&mut self) { + self.instance.device_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPUDevice { + const NAME: &'static str = "GPUDevice"; +} + +impl GarbageCollected for GPUDevice {} + +// TODO: extend EventTarget +// TODO: setEventTargetData on instance +#[op2] +impl GPUDevice { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[getter] + #[global] + fn features(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.features.get(scope, |scope| { + let features = self.instance.device_features(self.id); + let features = features_to_feature_names(features); + GPUSupportedFeatures::new(scope, features) + }) + } + + #[getter] + #[global] + fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.limits.get(scope, |_| { + let limits = self.instance.device_limits(self.id); + GPUSupportedLimits(limits) + }) + } + + #[getter] + #[global] + fn adapter_info( + &self, + scope: &mut v8::HandleScope, + ) -> v8::Global { + self.adapter_info.get(scope, |_| { + let info = self.instance.adapter_get_info(self.adapter); + GPUAdapterInfo(info) + }) + } + + #[getter] + #[global] + fn queue(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.queue_obj.get(scope, |_| GPUQueue { + id: self.queue, + error_handler: self.error_handler.clone(), + instance: self.instance.clone(), + label: self.label.clone(), + }) + } + + #[fast] + fn destroy(&self) { + self.instance.device_destroy(self.id); + } + + #[required(1)] + #[cppgc] + fn create_buffer( + &self, + #[webidl] descriptor: super::buffer::GPUBufferDescriptor, + ) -> Result { + let wgpu_descriptor = wgpu_core::resource::BufferDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + size: descriptor.size, + usage: wgpu_types::BufferUsages::from_bits(descriptor.usage) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, + mapped_at_creation: descriptor.mapped_at_creation, + }; + + let (id, err) = + self + .instance + .device_create_buffer(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + Ok(GPUBuffer { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + device: self.id, + label: descriptor.label, + size: descriptor.size, + usage: descriptor.usage, + map_state: RefCell::new(if descriptor.mapped_at_creation { + "mapped" + } else { + "unmapped" + }), + map_mode: RefCell::new(None), + mapped_js_buffers: RefCell::new(vec![]), + }) + } + + #[required(1)] + #[cppgc] + fn create_texture( + &self, + #[webidl] descriptor: super::texture::GPUTextureDescriptor, + ) -> Result { + let wgpu_descriptor = wgpu_core::resource::TextureDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + size: descriptor.size.into(), + mip_level_count: descriptor.mip_level_count, + sample_count: descriptor.sample_count, + dimension: descriptor.dimension.clone().into(), + format: descriptor.format.clone().into(), + usage: wgpu_types::TextureUsages::from_bits(descriptor.usage) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, + view_formats: descriptor + .view_formats + .into_iter() + .map(Into::into) + .collect(), + }; + + let (id, err) = + self + .instance + .device_create_texture(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + Ok(GPUTexture { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + label: descriptor.label, + size: wgpu_descriptor.size, + mip_level_count: wgpu_descriptor.mip_level_count, + sample_count: wgpu_descriptor.sample_count, + dimension: descriptor.dimension, + format: descriptor.format, + usage: descriptor.usage, + }) + } + + #[cppgc] + fn create_sampler( + &self, + #[webidl] descriptor: super::sampler::GPUSamplerDescriptor, + ) -> Result { + let wgpu_descriptor = wgpu_core::resource::SamplerDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + address_modes: [ + descriptor.address_mode_u.into(), + descriptor.address_mode_v.into(), + descriptor.address_mode_w.into(), + ], + mag_filter: descriptor.mag_filter.into(), + min_filter: descriptor.min_filter.into(), + mipmap_filter: descriptor.mipmap_filter.into(), + lod_min_clamp: descriptor.lod_min_clamp, + lod_max_clamp: descriptor.lod_max_clamp, + compare: descriptor.compare.map(Into::into), + anisotropy_clamp: descriptor.max_anisotropy, + border_color: None, + }; + + let (id, err) = + self + .instance + .device_create_sampler(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + Ok(GPUSampler { + instance: self.instance.clone(), + id, + label: descriptor.label, + }) + } + + #[required(1)] + #[cppgc] + fn create_bind_group_layout( + &self, + #[webidl] + descriptor: super::bind_group_layout::GPUBindGroupLayoutDescriptor, + ) -> Result { + let mut entries = Vec::with_capacity(descriptor.entries.len()); + + for entry in descriptor.entries { + let n_entries = [ + entry.buffer.is_some(), + entry.sampler.is_some(), + entry.texture.is_some(), + entry.storage_texture.is_some(), + ] + .into_iter() + .filter(|t| *t) + .count(); + + if n_entries != 1 { + return Err(JsErrorBox::type_error("Only one of 'buffer', 'sampler', 'texture' and 'storageTexture' may be specified")); + } + + let ty = if let Some(buffer) = entry.buffer { + BindingType::Buffer { + ty: buffer.r#type.into(), + has_dynamic_offset: buffer.has_dynamic_offset, + min_binding_size: NonZeroU64::new(buffer.min_binding_size), + } + } else if let Some(sampler) = entry.sampler { + BindingType::Sampler(sampler.r#type.into()) + } else if let Some(texture) = entry.texture { + BindingType::Texture { + sample_type: texture.sample_type.into(), + view_dimension: texture.view_dimension.into(), + multisampled: texture.multisampled, + } + } else if let Some(storage_texture) = entry.storage_texture { + BindingType::StorageTexture { + access: storage_texture.access.into(), + format: storage_texture.format.into(), + view_dimension: storage_texture.view_dimension.into(), + } + } else { + unreachable!() + }; + + entries.push(wgpu_types::BindGroupLayoutEntry { + binding: entry.binding, + visibility: wgpu_types::ShaderStages::from_bits(entry.visibility) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, + ty, + count: None, // native-only + }); + } + + let wgpu_descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + entries: Cow::Owned(entries), + }; + + let (id, err) = self.instance.device_create_bind_group_layout( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + Ok(GPUBindGroupLayout { + instance: self.instance.clone(), + id, + label: descriptor.label, + }) + } + + #[required(1)] + #[cppgc] + fn create_pipeline_layout( + &self, + #[webidl] descriptor: super::pipeline_layout::GPUPipelineLayoutDescriptor, + ) -> Result { + let bind_group_layouts = descriptor + .bind_group_layouts + .into_iter() + .map(|maybe_bind_group_layout| { + maybe_bind_group_layout + .into_option() + .map(|l| l.id) + .ok_or_else(|| { + JsErrorBox::type_error( + "Nullable GPUBindGroupLayouts are currently not supported", + ) + }) + }) + .collect::>()?; + + let wgpu_descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + bind_group_layouts: Cow::Owned(bind_group_layouts), + push_constant_ranges: Default::default(), + }; + + let (id, err) = self.instance.device_create_pipeline_layout( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + Ok(GPUPipelineLayout { + instance: self.instance.clone(), + id, + label: descriptor.label, + }) + } + + #[required(1)] + #[cppgc] + fn create_bind_group( + &self, + #[webidl] descriptor: super::bind_group::GPUBindGroupDescriptor, + ) -> GPUBindGroup { + let entries = descriptor + .entries + .into_iter() + .map(|entry| wgpu_core::binding_model::BindGroupEntry { + binding: entry.binding, + resource: match entry.resource { + GPUBindingResource::Sampler(sampler) => { + BindingResource::Sampler(sampler.id) + } + GPUBindingResource::TextureView(texture_view) => { + BindingResource::TextureView(texture_view.id) + } + GPUBindingResource::BufferBinding(buffer_binding) => { + BindingResource::Buffer(wgpu_core::binding_model::BufferBinding { + buffer_id: buffer_binding.buffer.id, + offset: buffer_binding.offset, + size: buffer_binding.size.and_then(NonZeroU64::new), + }) + } + }, + }) + .collect::>(); + + let wgpu_descriptor = wgpu_core::binding_model::BindGroupDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + layout: descriptor.layout.id, + entries: Cow::Owned(entries), + }; + + let (id, err) = + self + .instance + .device_create_bind_group(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + GPUBindGroup { + instance: self.instance.clone(), + id, + label: descriptor.label, + } + } + + #[required(1)] + #[cppgc] + fn create_shader_module( + &self, + #[webidl] descriptor: super::shader::GPUShaderModuleDescriptor, + ) -> GPUShaderModule { + let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + shader_bound_checks: wgpu_types::ShaderBoundChecks::default(), + }; + + let (id, err) = self.instance.device_create_shader_module( + self.id, + &wgpu_descriptor, + wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Owned( + descriptor.code, + )), + None, + ); + + self.error_handler.push_error(err); + + GPUShaderModule { + instance: self.instance.clone(), + id, + label: descriptor.label, + } + } + + #[required(1)] + #[cppgc] + fn create_compute_pipeline( + &self, + #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, + ) -> GPUComputePipeline { + self.new_compute_pipeline(descriptor) + } + + #[required(1)] + #[cppgc] + fn create_render_pipeline( + &self, + #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor, + ) -> Result { + self.new_render_pipeline(descriptor) + } + + #[async_method] + #[required(1)] + #[cppgc] + async fn create_compute_pipeline_async( + &self, + #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, + ) -> GPUComputePipeline { + self.new_compute_pipeline(descriptor) + } + + #[async_method] + #[required(1)] + #[cppgc] + async fn create_render_pipeline_async( + &self, + #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor, + ) -> Result { + self.new_render_pipeline(descriptor) + } + + #[required(1)] + #[cppgc] + fn create_command_encoder( + &self, + #[webidl] descriptor: super::command_encoder::GPUCommandEncoderDescriptor, + ) -> GPUCommandEncoder { + let wgpu_descriptor = wgpu_types::CommandEncoderDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + }; + + let (id, err) = self.instance.device_create_command_encoder( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + GPUCommandEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + label: descriptor.label, + } + } + + #[required(1)] + #[cppgc] + fn create_render_bundle_encoder( + &self, + #[webidl] + descriptor: super::render_bundle::GPURenderBundleEncoderDescriptor, + ) -> GPURenderBundleEncoder { + let wgpu_descriptor = wgpu_core::command::RenderBundleEncoderDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + color_formats: Cow::Owned( + descriptor + .color_formats + .into_iter() + .map(|format| format.into_option().map(Into::into)) + .collect::>(), + ), + depth_stencil: descriptor.depth_stencil_format.map(|format| { + wgpu_types::RenderBundleDepthStencil { + format: format.into(), + depth_read_only: descriptor.depth_read_only, + stencil_read_only: descriptor.stencil_read_only, + } + }), + sample_count: descriptor.sample_count, + multiview: None, + }; + + let res = wgpu_core::command::RenderBundleEncoder::new( + &wgpu_descriptor, + self.id, + None, + ); + let (encoder, err) = match res { + Ok(encoder) => (encoder, None), + Err(e) => ( + wgpu_core::command::RenderBundleEncoder::dummy(self.id), + Some(e), + ), + }; + + self.error_handler.push_error(err); + + GPURenderBundleEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + encoder: RefCell::new(Some(encoder)), + label: descriptor.label, + } + } + + #[required(1)] + #[cppgc] + fn create_query_set( + &self, + #[webidl] descriptor: crate::query_set::GPUQuerySetDescriptor, + ) -> GPUQuerySet { + let wgpu_descriptor = wgpu_core::resource::QuerySetDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + ty: descriptor.r#type.clone().into(), + count: descriptor.count, + }; + + let (id, err) = + self + .instance + .device_create_query_set(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + GPUQuerySet { + instance: self.instance.clone(), + id, + r#type: descriptor.r#type, + count: descriptor.count, + label: descriptor.label, + } + } + + // TODO: support returning same promise + #[async_method] + #[getter] + #[cppgc] + async fn lost(&self) -> GPUDeviceLostInfo { + if let Some(lost_receiver) = self.lost_receiver.lock().await.take() { + let _ = lost_receiver.await; + } + + GPUDeviceLostInfo + } + + #[required(1)] + fn push_error_scope(&self, #[webidl] filter: super::error::GPUErrorFilter) { + self + .error_handler + .scopes + .lock() + .unwrap() + .push((filter, vec![])); + } + + #[async_method] + fn pop_error_scope<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> impl Future, JsErrorBox>> { + if self.error_handler.is_lost.get().is_some() { + return std::future::ready(Ok(v8::null(scope).into())); + } + + let Some((_, errors)) = self.error_handler.scopes.lock().unwrap().pop() else { + return std::future::ready(Err(JsErrorBox::new( + "DOMExceptionOperationError", + "There are no error scopes on the error scope stack", + ))); + }; + + let val = if let Some(err) = errors.into_iter().next() { + Ok(deno_core::error::to_v8_error(scope, &err)) + } else { + Ok(v8::null(scope).into()) + }; + + std::future::ready(val) + } +} + +impl GPUDevice { + fn new_compute_pipeline( + &self, + descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, + ) -> GPUComputePipeline { + let wgpu_descriptor = wgpu_core::pipeline::ComputePipelineDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + layout: descriptor.layout.into(), + stage: ProgrammableStageDescriptor { + module: descriptor.compute.module.id, + entry_point: descriptor.compute.entry_point.map(Into::into), + constants: Cow::Owned( + descriptor.compute.constants.into_iter().collect(), + ), + zero_initialize_workgroup_memory: true, + }, + cache: None, + }; + + let (id, err) = self.instance.device_create_compute_pipeline( + self.id, + &wgpu_descriptor, + None, + None, + ); + + self.error_handler.push_error(err); + + GPUComputePipeline { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + label: descriptor.label.clone(), + } + } + + fn new_render_pipeline( + &self, + descriptor: super::render_pipeline::GPURenderPipelineDescriptor, + ) -> Result { + let vertex = wgpu_core::pipeline::VertexState { + stage: ProgrammableStageDescriptor { + module: descriptor.vertex.module.id, + entry_point: descriptor.vertex.entry_point.map(Into::into), + constants: Cow::Owned( + descriptor.vertex.constants.into_iter().collect(), + ), + zero_initialize_workgroup_memory: true, + }, + buffers: Cow::Owned( + descriptor + .vertex + .buffers + .into_iter() + .map(|b| { + let layout = b.into_option().ok_or_else(|| { + JsErrorBox::type_error( + "Nullable GPUVertexBufferLayouts are currently not supported", + ) + })?; + + Ok(wgpu_core::pipeline::VertexBufferLayout { + array_stride: layout.array_stride, + step_mode: layout.step_mode.into(), + attributes: Cow::Owned( + layout + .attributes + .into_iter() + .map(|attr| wgpu_types::VertexAttribute { + format: attr.format.into(), + offset: attr.offset, + shader_location: attr.shader_location, + }) + .collect(), + ), + }) + }) + .collect::>()?, + ), + }; + + let primitive = wgpu_types::PrimitiveState { + topology: descriptor.primitive.topology.into(), + strip_index_format: descriptor + .primitive + .strip_index_format + .map(Into::into), + front_face: descriptor.primitive.front_face.into(), + cull_mode: descriptor.primitive.cull_mode.into(), + unclipped_depth: descriptor.primitive.unclipped_depth, + polygon_mode: Default::default(), + conservative: false, + }; + + let depth_stencil = descriptor.depth_stencil.map(|depth_stencil| { + let front = wgpu_types::StencilFaceState { + compare: depth_stencil.stencil_front.compare.into(), + fail_op: depth_stencil.stencil_front.fail_op.into(), + depth_fail_op: depth_stencil.stencil_front.depth_fail_op.into(), + pass_op: depth_stencil.stencil_front.pass_op.into(), + }; + let back = wgpu_types::StencilFaceState { + compare: depth_stencil.stencil_back.compare.into(), + fail_op: depth_stencil.stencil_back.fail_op.into(), + depth_fail_op: depth_stencil.stencil_back.depth_fail_op.into(), + pass_op: depth_stencil.stencil_back.pass_op.into(), + }; + + wgpu_types::DepthStencilState { + format: depth_stencil.format.into(), + depth_write_enabled: depth_stencil + .depth_write_enabled + .unwrap_or_default(), + depth_compare: depth_stencil + .depth_compare + .map(Into::into) + .unwrap_or(wgpu_types::CompareFunction::Never), // TODO: wgpu should be optional here + stencil: wgpu_types::StencilState { + front, + back, + read_mask: depth_stencil.stencil_read_mask, + write_mask: depth_stencil.stencil_write_mask, + }, + bias: wgpu_types::DepthBiasState { + constant: depth_stencil.depth_bias, + slope_scale: depth_stencil.depth_bias_slope_scale, + clamp: depth_stencil.depth_bias_clamp, + }, + } + }); + + let multisample = wgpu_types::MultisampleState { + count: descriptor.multisample.count, + mask: descriptor.multisample.mask as u64, + alpha_to_coverage_enabled: descriptor + .multisample + .alpha_to_coverage_enabled, + }; + + let fragment = descriptor + .fragment + .map(|fragment| { + Ok::<_, JsErrorBox>(wgpu_core::pipeline::FragmentState { + stage: ProgrammableStageDescriptor { + module: fragment.module.id, + entry_point: fragment.entry_point.map(Into::into), + constants: Cow::Owned(fragment.constants.into_iter().collect()), + zero_initialize_workgroup_memory: true, + }, + targets: Cow::Owned( + fragment + .targets + .into_iter() + .map(|target| { + target + .into_option() + .map(|target| { + Ok(wgpu_types::ColorTargetState { + format: target.format.into(), + blend: target.blend.map(|blend| wgpu_types::BlendState { + color: wgpu_types::BlendComponent { + src_factor: blend.color.src_factor.into(), + dst_factor: blend.color.dst_factor.into(), + operation: blend.color.operation.into(), + }, + alpha: wgpu_types::BlendComponent { + src_factor: blend.alpha.src_factor.into(), + dst_factor: blend.alpha.dst_factor.into(), + operation: blend.alpha.operation.into(), + }, + }), + write_mask: wgpu_types::ColorWrites::from_bits( + target.write_mask, + ) + .ok_or_else(|| { + JsErrorBox::type_error("usage is not valid") + })?, + }) + }) + .transpose() + }) + .collect::>()?, + ), + }) + }) + .transpose()?; + + let wgpu_descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + layout: descriptor.layout.into(), + vertex, + primitive, + depth_stencil, + multisample, + fragment, + cache: None, + multiview: None, + }; + + let (id, err) = self.instance.device_create_render_pipeline( + self.id, + &wgpu_descriptor, + None, + None, + ); + + self.error_handler.push_error(err); + + Ok(GPURenderPipeline { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + label: descriptor.label, + }) + } +} + +pub struct GPUDeviceLostInfo; + +impl GarbageCollected for GPUDeviceLostInfo {} + +#[op2] +impl GPUDeviceLostInfo { + #[getter] + #[string] + fn reason(&self) -> &'static str { + "unknown" + } + + #[getter] + #[string] + fn message(&self) -> &'static str { + "device was lost" + } +} diff --git a/ext/webgpu/error.rs b/ext/webgpu/error.rs index f022a56916..7d658a7672 100644 --- a/ext/webgpu/error.rs +++ b/ext/webgpu/error.rs @@ -1,10 +1,10 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::convert::From; -use std::error::Error; +use std::fmt::Display; +use std::fmt::Formatter; +use std::sync::Mutex; +use std::sync::OnceLock; -use deno_core::ResourceId; -use serde::Serialize; use wgpu_core::binding_model::CreateBindGroupError; use wgpu_core::binding_model::CreateBindGroupLayoutError; use wgpu_core::binding_model::CreatePipelineLayoutError; @@ -31,7 +31,105 @@ use wgpu_core::resource::CreateSamplerError; use wgpu_core::resource::CreateTextureError; use wgpu_core::resource::CreateTextureViewError; -fn fmt_err(err: &(dyn Error + 'static)) -> String { +pub type ErrorHandler = std::sync::Arc; + +pub struct DeviceErrorHandler { + pub is_lost: OnceLock<()>, + lost_sender: Mutex>>, + + pub scopes: Mutex)>>, +} + +impl DeviceErrorHandler { + pub fn new(sender: tokio::sync::oneshot::Sender<()>) -> Self { + Self { + is_lost: Default::default(), + lost_sender: Mutex::new(Some(sender)), + scopes: Mutex::new(vec![]), + } + } + + pub fn push_error>(&self, err: Option) { + let Some(err) = err else { + return; + }; + + if self.is_lost.get().is_some() { + return; + } + + let err = err.into(); + + if matches!(err, GPUError::Lost) { + let _ = self.is_lost.set(()); + + if let Some(sender) = self.lost_sender.lock().unwrap().take() { + let _ = sender.send(()); + } + return; + } + + let error_filter = match err { + GPUError::Lost => unreachable!(), + GPUError::Validation(_) => GPUErrorFilter::Validation, + GPUError::OutOfMemory => GPUErrorFilter::OutOfMemory, + GPUError::Internal => GPUErrorFilter::Internal, + }; + + let mut scopes = self.scopes.lock().unwrap(); + let scope = scopes + .iter_mut() + .rfind(|(filter, _)| filter == &error_filter); + + if let Some(scope) = scope { + scope.1.push(err); + } else { + /* TODO: + this.device.dispatchEvent( + new GPUUncapturedErrorEvent("uncapturederror", { + error: constructedError, + }), + ); + */ + } + } +} + +#[derive(deno_core::WebIDL, Eq, PartialEq)] +#[webidl(enum)] +pub enum GPUErrorFilter { + Validation, + OutOfMemory, + Internal, +} + +#[derive(Debug, deno_error::JsError)] +pub enum GPUError { + // TODO(@crowlKats): consider adding an unreachable value that uses unreachable!() + #[class("UNREACHABLE")] + Lost, + #[class("GPUValidationError")] + Validation(String), + #[class("GPUOutOfMemoryError")] + OutOfMemory, + #[class("GPUInternalError")] + Internal, +} + +impl Display for GPUError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + GPUError::Lost => Ok(()), + GPUError::Validation(s) => f.write_str(s), + GPUError::OutOfMemory => f.write_str("not enough memory left"), + GPUError::Internal => Ok(()), + } + } +} + +impl std::error::Error for GPUError {} + +fn fmt_err(err: &(dyn std::error::Error + 'static)) -> String { let mut output = err.to_string(); let mut e = err.source(); @@ -40,248 +138,203 @@ fn fmt_err(err: &(dyn Error + 'static)) -> String { e = source.source(); } + if output.is_empty() { + output.push_str("validation error"); + } + 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), - Internal, -} - -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateBufferError) -> Self { match err { CreateBufferError::Device(err) => err.into(), CreateBufferError::AccessError(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: DeviceError) -> Self { match err { - DeviceError::Lost => WebGpuError::Lost, - DeviceError::OutOfMemory => WebGpuError::OutOfMemory, - _ => WebGpuError::Validation(fmt_err(&err)), + DeviceError::Lost => GPUError::Lost, + DeviceError::OutOfMemory => GPUError::OutOfMemory, + _ => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: BufferAccessError) -> Self { match err { BufferAccessError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateBindGroupLayoutError) -> Self { match err { CreateBindGroupLayoutError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreatePipelineLayoutError) -> Self { match err { CreatePipelineLayoutError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateBindGroupError) -> Self { match err { CreateBindGroupError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: RenderBundleError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateRenderBundleError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CopyError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CommandEncoderError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: QueryError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: ComputePassError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateComputePipelineError) -> Self { match err { CreateComputePipelineError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: GetBindGroupLayoutError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateRenderPipelineError) -> Self { match err { CreateRenderPipelineError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: RenderPassError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateSamplerError) -> Self { match err { CreateSamplerError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateShaderModuleError) -> Self { match err { CreateShaderModuleError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateTextureError) -> Self { match err { CreateTextureError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateTextureViewError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: CreateQuerySetError) -> Self { match err { CreateQuerySetError::Device(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: QueueSubmitError) -> Self { match err { QueueSubmitError::Queue(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: QueueWriteError) -> Self { match err { QueueWriteError::Queue(err) => err.into(), - err => WebGpuError::Validation(fmt_err(&err)), + err => GPUError::Validation(fmt_err(&err)), } } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: ClearError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } -impl From for WebGpuError { +impl From for GPUError { fn from(err: ConfigureSurfaceError) -> Self { - WebGpuError::Validation(fmt_err(&err)) + GPUError::Validation(fmt_err(&err)) } } diff --git a/ext/webgpu/lib.rs b/ext/webgpu/lib.rs index 0026f1c5a3..796a6282e8 100644 --- a/ext/webgpu/lib.rs +++ b/ext/webgpu/lib.rs @@ -2,20 +2,40 @@ #![cfg(not(target_arch = "wasm32"))] #![warn(unsafe_op_in_unsafe_fn)] -use std::borrow::Cow; use std::cell::RefCell; -use std::collections::HashSet; use std::rc::Rc; +use std::sync::Arc; +use deno_core::cppgc::SameObject; use deno_core::op2; +use deno_core::GarbageCollected; use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use error::WebGpuResult; -use serde::Deserialize; -use serde::Serialize; pub use wgpu_core; pub use wgpu_types; +use wgpu_types::PowerPreference; + +mod adapter; +mod bind_group; +mod bind_group_layout; +mod buffer; +mod byow; +mod command_buffer; +mod command_encoder; +mod compute_pass; +mod compute_pipeline; +mod device; +mod error; +mod pipeline_layout; +mod query_set; +mod queue; +mod render_bundle; +mod render_pass; +mod render_pipeline; +mod sampler; +mod shader; +mod surface; +mod texture; +mod webidl; pub const UNSTABLE_FEATURE_NAME: &str = "webgpu"; @@ -38,793 +58,116 @@ pub fn print_linker_flags(name: &str) { } } -#[macro_use] -mod macros { - macro_rules! gfx_select { - ($id:expr => $p0:ident.$p1:tt.$method:ident $params:tt) => { - gfx_select!($id => {$p0.$p1}, $method $params) - }; - - ($id:expr => $p0:ident.$method:ident $params:tt) => { - gfx_select!($id => {$p0}, $method $params) - }; - - ($id:expr => {$($c:tt)*}, $method:ident $params:tt) => { - 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 => $($c)*.$method:: $params, - #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))] - wgpu_types::Backend::Metal => $($c)*.$method:: $params, - #[cfg(all(not(target_arch = "wasm32"), windows))] - wgpu_types::Backend::Dx12 => $($c)*.$method:: $params, - #[cfg(any( - all(not(target_os = "macos"), not(target_os = "ios")), - feature = "angle", - target_arch = "wasm32" - ))] - wgpu_types::Backend::Gl => $($c)*.$method:: $params, - 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 byow; -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; -pub mod surface; -pub mod texture; - -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum InitError { - #[class(inherit)] - #[error(transparent)] - Resource( - #[from] - #[inherit] - deno_core::error::ResourceError, - ), - #[class(generic)] - #[error(transparent)] - InvalidAdapter(wgpu_core::instance::InvalidAdapter), - #[class("DOMExceptionOperationError")] - #[error(transparent)] - RequestDevice(wgpu_core::instance::RequestDeviceError), - #[class(generic)] - #[error(transparent)] - InvalidDevice(wgpu_core::device::InvalidDevice), -} - -pub type Instance = std::sync::Arc; - -struct WebGpuAdapter(Instance, wgpu_core::id::AdapterId); -impl Resource for WebGpuAdapter { - fn name(&self) -> Cow { - "webGPUAdapter".into() - } - - fn close(self: Rc) { - gfx_select!(self.1 => self.0.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) { - gfx_select!(self.1 => self.0.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) { - gfx_select!(self.1 => self.0.query_set_drop(self.1)); - } -} +pub type Instance = Arc; 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, - // surface - surface::op_webgpu_surface_configure, - surface::op_webgpu_surface_get_current_texture, - surface::op_webgpu_surface_present, - // byow - byow::op_webgpu_surface_create, + ops = [create_gpu], + objects = [ + GPU, + adapter::GPUAdapter, + adapter::GPUAdapterInfo, + bind_group::GPUBindGroup, + bind_group_layout::GPUBindGroupLayout, + buffer::GPUBuffer, + command_buffer::GPUCommandBuffer, + command_encoder::GPUCommandEncoder, + compute_pass::GPUComputePassEncoder, + compute_pipeline::GPUComputePipeline, + device::GPUDevice, + device::GPUDeviceLostInfo, + pipeline_layout::GPUPipelineLayout, + query_set::GPUQuerySet, + queue::GPUQueue, + render_bundle::GPURenderBundle, + render_bundle::GPURenderBundleEncoder, + render_pass::GPURenderPassEncoder, + render_pipeline::GPURenderPipeline, + sampler::GPUSampler, + shader::GPUShaderModule, + adapter::GPUSupportedFeatures, + adapter::GPUSupportedLimits, + texture::GPUTexture, + texture::GPUTextureView, + byow::UnsafeWindowSurface, + surface::GPUCanvasContext, ], esm = ["00_init.js", "02_surface.js"], 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"); - } - if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) { - return_features.push("float32-filterable"); - } - - // 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 +#[op2] +#[cppgc] +pub fn create_gpu() -> GPU { + GPU } -#[derive(Serialize)] -#[serde(untagged)] -pub enum GpuAdapterResOrErr { - Error { err: String }, - Features(GpuAdapterRes), -} +pub struct GPU; -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GpuAdapterRes { - rid: ResourceId, - limits: wgpu_types::Limits, - features: Vec<&'static str>, - is_fallback: bool, -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GpuDeviceRes { - rid: ResourceId, - queue_rid: ResourceId, - limits: wgpu_types::Limits, - features: Vec<&'static str>, -} +impl GarbageCollected for GPU {} #[op2] -#[serde] -pub fn op_webgpu_request_adapter( - state: Rc>, - #[serde] power_preference: Option, - force_fallback_adapter: bool, -) -> Result { - let mut state = state.borrow_mut(); +impl GPU { + #[async_method] + #[cppgc] + async fn request_adapter( + &self, + state: Rc>, + #[webidl] options: adapter::GPURequestAdapterOptions, + ) -> Option { + let mut state = state.borrow_mut(); - 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_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 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(Arc::new(wgpu_core::global::Global::new( + "webgpu", + 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, |_| None), - ); + let descriptor = wgpu_core::instance::RequestAdapterOptions { + power_preference: options + .power_preference + .map(|pp| match pp { + adapter::GPUPowerPreference::LowPower => PowerPreference::LowPower, + adapter::GPUPowerPreference::HighPerformance => { + PowerPreference::HighPerformance + } + }) + .unwrap_or_default(), + force_fallback_adapter: options.force_fallback_adapter, + compatible_surface: None, // windowless + }; + let id = instance.request_adapter(&descriptor, backends, None).ok()?; - let adapter = match res { - Ok(adapter) => adapter, - Err(err) => { - return Ok(GpuAdapterResOrErr::Error { - err: err.to_string(), - }) - } - }; - let adapter_features = - gfx_select!(adapter => instance.adapter_features(adapter)) - .map_err(InitError::InvalidAdapter)?; - let features = deserialize_features(&adapter_features); - let adapter_limits = gfx_select!(adapter => instance.adapter_limits(adapter)) - .map_err(InitError::InvalidAdapter)?; - - let instance = instance.clone(); - - let rid = state.resource_table.add(WebGpuAdapter(instance, adapter)); - - Ok(GpuAdapterResOrErr::Features(GpuAdapterRes { - rid, - features, - limits: adapter_limits, - // TODO(lucacasonato): report correctly from wgpu - is_fallback: 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"), - ); - features.set( - wgpu_types::Features::FLOAT32_FILTERABLE, - required_features.0.contains("float32-filterable"), - ); - - // 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] -#[serde] -pub 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.take::(adapter_rid)?; - let adapter = adapter_resource.1; - let instance = state.borrow::(); - - let descriptor = wgpu_types::DeviceDescriptor { - label: Some(Cow::Owned(label)), - required_features: required_features.into(), - required_limits: required_limits.unwrap_or_default(), - }; - - let (device, queue, 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), - None, - None - )); - adapter_resource.close(); - if let Some(err) = maybe_err { - return Err(InitError::RequestDevice(err)); + Some(adapter::GPUAdapter { + instance: instance.clone(), + features: SameObject::new(), + limits: SameObject::new(), + info: Rc::new(SameObject::new()), + id, + }) } - let device_features = gfx_select!(device => instance.device_features(device)) - .map_err(InitError::InvalidDevice)?; - let features = deserialize_features(&device_features); - let limits = gfx_select!(device => instance.device_limits(device)) - .map_err(InitError::InvalidDevice)?; - - let instance = instance.clone(); - let instance2 = instance.clone(); - let rid = state.resource_table.add(WebGpuDevice(instance, device)); - let queue_rid = state - .resource_table - .add(queue::WebGpuQueue(instance2, queue)); - - Ok(GpuDeviceRes { - rid, - queue_rid, - features, - limits, - }) -} - -#[derive(Serialize)] -#[serde(rename_all = "camelCase")] -pub struct GPUAdapterInfo { - vendor: String, - architecture: String, - device: String, - description: String, -} - -#[op2] -#[serde] -pub 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)) - .map_err(InitError::InvalidAdapter)?; - - 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, + #[string] + fn getPreferredCanvasFormat(&self) -> &'static str { + // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47 + if cfg!(target_os = "android") { + texture::GPUTextureFormat::Rgba8unorm.as_str() + } else { + texture::GPUTextureFormat::Bgra8unorm.as_str() } } } - -#[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, - None - ) => state, WebGpuQuerySet) -} diff --git a/ext/webgpu/pipeline.rs b/ext/webgpu/pipeline.rs deleted file mode 100644 index 07452a1caf..0000000000 --- a/ext/webgpu/pipeline.rs +++ /dev/null @@ -1,460 +0,0 @@ -// Copyright 2018-2025 the Deno authors. MIT license. - -use std::borrow::Cow; -use std::collections::HashMap; -use std::rc::Rc; - -use deno_core::error::ResourceError; -use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; -use serde::Serialize; - -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) { - gfx_select!(self.1 => self.0.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) { - gfx_select!(self.1 => self.0.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) { - gfx_select!(self.1 => self.0.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: Option, - constants: Option>, -} - -#[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: compute.entry_point.map(Cow::from), - constants: Cow::Owned(compute.constants.unwrap_or_default()), - zero_initialize_workgroup_memory: true, - }, - }; - let implicit_pipelines = match layout { - GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(_) => None, - GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => { - Some(wgpu_core::device::ImplicitPipelineIds { - root_id: None, - group_ids: &[None; MAX_BIND_GROUPS], - }) - } - }; - - let (compute_pipeline, maybe_err) = gfx_select!(device => instance.device_create_compute_pipeline( - device, - &descriptor, - None, - 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, None)); - - 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: Option, - constants: Option>, - 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: Option, - constants: Option>, -} - -#[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: fragment.entry_point.map(Cow::from), - constants: Cow::Owned(fragment.constants.unwrap_or_default()), - // Required to be true for WebGPU - zero_initialize_workgroup_memory: true, - }, - targets: Cow::Owned(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: args.vertex.entry_point.map(Cow::Owned), - constants: Cow::Owned(args.vertex.constants.unwrap_or_default()), - // Required to be true for WebGPU - zero_initialize_workgroup_memory: true, - }, - 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: None, - group_ids: &[None; MAX_BIND_GROUPS], - }) - } - }; - - let (render_pipeline, maybe_err) = gfx_select!(device => instance.device_create_render_pipeline( - device, - &descriptor, - None, - 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, None)); - - 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/pipeline_layout.rs b/ext/webgpu/pipeline_layout.rs new file mode 100644 index 0000000000..4559942ea2 --- /dev/null +++ b/ext/webgpu/pipeline_layout.rs @@ -0,0 +1,52 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_core::cppgc::Ptr; +use deno_core::op2; +use deno_core::webidl::Nullable; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; + +use crate::Instance; + +pub struct GPUPipelineLayout { + pub instance: Instance, + pub id: wgpu_core::id::PipelineLayoutId, + pub label: String, +} + +impl Drop for GPUPipelineLayout { + fn drop(&mut self) { + self.instance.pipeline_layout_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPUPipelineLayout { + const NAME: &'static str = "GPUPipelineLayout"; +} + +impl GarbageCollected for GPUPipelineLayout {} + +#[op2] +impl GPUPipelineLayout { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUPipelineLayoutDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub bind_group_layouts: + Vec>>, +} diff --git a/ext/webgpu/query_set.rs b/ext/webgpu/query_set.rs new file mode 100644 index 0000000000..eada34052c --- /dev/null +++ b/ext/webgpu/query_set.rs @@ -0,0 +1,87 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_core::op2; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use deno_error::JsErrorBox; + +use crate::Instance; + +pub struct GPUQuerySet { + pub instance: Instance, + pub id: wgpu_core::id::QuerySetId, + pub r#type: GPUQueryType, + pub count: u32, + pub label: String, +} + +impl Drop for GPUQuerySet { + fn drop(&mut self) { + self.instance.query_set_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPUQuerySet { + const NAME: &'static str = "GPUQuerySet"; +} + +impl GarbageCollected for GPUQuerySet {} + +#[op2] +impl GPUQuerySet { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[fast] + fn destroy(&self) -> Result<(), JsErrorBox> { + Err(JsErrorBox::generic( + "This operation is currently not supported", + )) + } + + #[getter] + #[string] + fn r#type(&self) -> &'static str { + self.r#type.as_str() + } + + #[getter] + fn count(&self) -> u32 { + self.count + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUQuerySetDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub r#type: GPUQueryType, + #[options(enforce_range = true)] + pub count: u32, +} + +#[derive(WebIDL, Clone)] +#[webidl(enum)] +pub(crate) enum GPUQueryType { + Occlusion, + Timestamp, +} +impl From for wgpu_types::QueryType { + fn from(value: GPUQueryType) -> Self { + match value { + GPUQueryType::Occlusion => Self::Occlusion, + GPUQueryType::Timestamp => Self::Timestamp, + } + } +} diff --git a/ext/webgpu/queue.rs b/ext/webgpu/queue.rs index 51f4c4e009..a97affe70a 100644 --- a/ext/webgpu/queue.rs +++ b/ext/webgpu/queue.rs @@ -1,144 +1,150 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; -use std::rc::Rc; - -use deno_core::error::ResourceError; +use deno_core::cppgc::Ptr; use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use deno_error::JsErrorBox; -use super::error::WebGpuResult; -use crate::command_encoder::WebGpuCommandBuffer; +use crate::buffer::GPUBuffer; +use crate::command_buffer::GPUCommandBuffer; +use crate::texture::GPUTexture; +use crate::texture::GPUTextureAspect; +use crate::webidl::GPUExtent3D; +use crate::webidl::GPUOrigin3D; use crate::Instance; -pub struct WebGpuQueue(pub Instance, pub wgpu_core::id::QueueId); -impl Resource for WebGpuQueue { - fn name(&self) -> Cow { - "webGPUQueue".into() - } +pub struct GPUQueue { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - fn close(self: Rc) { - gfx_select!(self.1 => self.0.queue_drop(self.1)); + pub label: String, + + pub id: wgpu_core::id::QueueId, +} + +impl Drop for GPUQueue { + fn drop(&mut self) { + self.instance.queue_drop(self.id); } } +impl GarbageCollected for GPUQueue {} + #[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::, ResourceError>>()?; - - 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(); +impl GPUQueue { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label } - Ok(WebGpuResult::maybe_err(maybe_err)) -} + #[required(1)] + fn submit(&self, #[webidl] command_buffers: Vec>) { + let ids = command_buffers + .into_iter() + .map(|cb| cb.id) + .collect::>(); -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct GpuImageDataLayout { - offset: u64, - bytes_per_row: Option, - rows_per_image: Option, -} + let err = self.instance.queue_submit(self.id, &ids).err(); -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, + if let Some((_, err)) = err { + self.error_handler.push_error(Some(err)); } } + + #[async_method] + async fn on_submitted_work_done(&self) -> Result<(), JsErrorBox> { + Err(JsErrorBox::generic( + "This operation is currently not supported", + )) + } + + #[required(3)] + fn write_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl(options(enforce_range = true))] buffer_offset: u64, + #[anybuffer] buf: &[u8], + #[webidl(default = 0, options(enforce_range = true))] data_offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let data = match size { + Some(size) => { + &buf[(data_offset as usize)..((data_offset + size) as usize)] + } + None => &buf[(data_offset as usize)..], + }; + + let err = self + .instance + .queue_write_buffer(self.id, buffer.id, buffer_offset, data) + .err(); + + self.error_handler.push_error(err); + } + + #[required(4)] + fn write_texture( + &self, + #[webidl] destination: GPUTexelCopyTextureInfo, + #[anybuffer] buf: &[u8], + #[webidl] data_layout: GPUTexelCopyBufferLayout, + #[webidl] size: GPUExtent3D, + ) { + let destination = wgpu_core::command::ImageCopyTexture { + texture: destination.texture.id, + mip_level: destination.mip_level, + origin: destination.origin.into(), + aspect: destination.aspect.into(), + }; + + let data_layout = wgpu_types::ImageDataLayout { + offset: data_layout.offset, + bytes_per_row: data_layout.bytes_per_row, + rows_per_image: data_layout.rows_per_image, + }; + + let err = self + .instance + .queue_write_texture( + self.id, + &destination, + buf, + &data_layout, + &size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } } -#[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)) +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUTexelCopyTextureInfo { + pub texture: Ptr, + #[options(enforce_range = true)] + pub mip_level: u32, + #[webidl(default = Default::default())] + pub origin: GPUOrigin3D, + #[webidl(default = GPUTextureAspect::All)] + pub aspect: GPUTextureAspect, } -#[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 - )) +#[derive(WebIDL)] +#[webidl(dictionary)] +struct GPUTexelCopyBufferLayout { + #[webidl(default = 0)] + #[options(enforce_range = true)] + offset: u64, + #[options(enforce_range = true)] + bytes_per_row: Option, + #[options(enforce_range = true)] + rows_per_image: Option, } diff --git a/ext/webgpu/render_bundle.rs b/ext/webgpu/render_bundle.rs new file mode 100644 index 0000000000..4fef111736 --- /dev/null +++ b/ext/webgpu/render_bundle.rs @@ -0,0 +1,416 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::cell::RefCell; +use std::num::NonZeroU64; + +use deno_core::cppgc::Ptr; +use deno_core::op2; +use deno_core::v8; +use deno_core::webidl::IntOptions; +use deno_core::webidl::Nullable; +use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use deno_error::JsErrorBox; + +use crate::buffer::GPUBuffer; +use crate::texture::GPUTextureFormat; +use crate::Instance; + +pub struct GPURenderBundleEncoder { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, + + pub encoder: RefCell>, + pub label: String, +} + +impl GarbageCollected for GPURenderBundleEncoder {} + +#[op2] +impl GPURenderBundleEncoder { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[cppgc] + fn finish( + &self, + #[webidl] descriptor: GPURenderBundleDescriptor, + ) -> GPURenderBundle { + let wgpu_descriptor = wgpu_core::command::RenderBundleDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + }; + + let (id, err) = self.instance.render_bundle_encoder_finish( + self.encoder.borrow_mut().take().unwrap(), + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + GPURenderBundle { + instance: self.instance.clone(), + id, + label: descriptor.label.clone(), + } + } + + fn push_debug_group( + &self, + #[webidl] group_label: String, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + let label = std::ffi::CString::new(group_label).unwrap(); + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group( + encoder, + label.as_ptr(), + ); + } + + Ok(()) + } + + #[fast] + fn pop_debug_group(&self) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(encoder); + Ok(()) + } + + fn insert_debug_marker( + &self, + #[webidl] marker_label: String, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + let label = std::ffi::CString::new(marker_label).unwrap(); + + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker( + encoder, + label.as_ptr(), + ); + } + Ok(()) + } + + fn set_bind_group<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + #[webidl(options(enforce_range = true))] index: u32, + #[webidl] bind_group: Nullable>, + dynamic_offsets: v8::Local<'a, v8::Value>, + dynamic_offsets_data_start: v8::Local<'a, v8::Value>, + dynamic_offsets_data_length: v8::Local<'a, v8::Value>, + ) -> Result<(), SetBindGroupError> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + const PREFIX: &str = + "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; + if let Ok(uint_32) = dynamic_offsets.try_cast::() { + let start = u64::convert( + scope, + dynamic_offsets_data_start, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 4")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + let len = u32::convert( + scope, + dynamic_offsets_data_length, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 5")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + + let ab = uint_32.buffer(scope).unwrap(); + let ptr = ab.data().unwrap(); + let ab_len = ab.byte_length() / 4; + + let data = + unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; + + let offsets = &data[start..(start + len)]; + + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group( + encoder, + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets.as_ptr(), + offsets.len(), + ); + } + } else { + let offsets = >>::convert( + scope, + dynamic_offsets, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 3")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? + .unwrap_or_default(); + + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group( + encoder, + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets.as_ptr(), + offsets.len(), + ); + } + } + + Ok(()) + } + + fn set_pipeline( + &self, + #[webidl] pipeline: Ptr, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline( + encoder, + pipeline.id, + ); + Ok(()) + } + + #[required(2)] + fn set_index_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl] index_format: crate::render_pipeline::GPUIndexFormat, + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + encoder.set_index_buffer( + buffer.id, + index_format.into(), + offset, + size.and_then(NonZeroU64::new), + ); + Ok(()) + } + + #[required(2)] + fn set_vertex_buffer( + &self, + #[webidl(options(enforce_range = true))] slot: u32, + #[webidl] buffer: Ptr, // TODO: support nullable buffer + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer( + encoder, + slot, + buffer.id, + offset, + size.and_then(NonZeroU64::new), + ); + Ok(()) + } + + #[required(1)] + fn draw( + &self, + #[webidl(options(enforce_range = true))] vertex_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw( + encoder, + vertex_count, + instance_count, + first_vertex, + first_instance, + ); + Ok(()) + } + + #[required(1)] + fn draw_indexed( + &self, + #[webidl(options(enforce_range = true))] index_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_index: u32, + #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed( + encoder, + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ); + Ok(()) + } + + #[required(2)] + fn draw_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect( + encoder, + indirect_buffer.id, + indirect_offset, + ); + Ok(()) + } + + #[required(2)] + fn draw_indexed_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed_indirect( + encoder, + indirect_buffer.id, + indirect_offset, + ); + Ok(()) + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURenderBundleEncoderDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub color_formats: Vec>, + pub depth_stencil_format: Option, + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub sample_count: u32, + + #[webidl(default = false)] + pub depth_read_only: bool, + #[webidl(default = false)] + pub stencil_read_only: bool, +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +enum SetBindGroupError { + #[class(inherit)] + #[error(transparent)] + WebIDL(#[from] WebIdlError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), +} + +pub struct GPURenderBundle { + pub instance: Instance, + pub id: wgpu_core::id::RenderBundleId, + pub label: String, +} + +impl Drop for GPURenderBundle { + fn drop(&mut self) { + self.instance.render_bundle_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPURenderBundle { + const NAME: &'static str = "GPURenderBundle"; +} + +impl GarbageCollected for GPURenderBundle {} + +#[op2] +impl GPURenderBundle { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURenderBundleDescriptor { + #[webidl(default = String::new())] + pub label: String, +} diff --git a/ext/webgpu/render_pass.rs b/ext/webgpu/render_pass.rs index 43c4cae846..9b84ecb3ff 100644 --- a/ext/webgpu/render_pass.rs +++ b/ext/webgpu/render_pass.rs @@ -2,506 +2,496 @@ use std::borrow::Cow; use std::cell::RefCell; +use std::num::NonZeroU64; -use deno_core::error::ResourceError; +use deno_core::cppgc::Ptr; use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; +use deno_core::v8; +use deno_core::webidl::IntOptions; +use deno_core::webidl::Nullable; +use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; +use deno_core::GarbageCollected; +use deno_core::WebIDL; -use super::error::WebGpuResult; +use crate::buffer::GPUBuffer; +use crate::render_bundle::GPURenderBundle; +use crate::texture::GPUTextureView; +use crate::webidl::GPUColor; +use crate::Instance; -#[derive(Debug, thiserror::Error, deno_error::JsError)] -pub enum RenderPassError { - #[class(inherit)] - #[error(transparent)] - Resource( - #[from] - #[inherit] - ResourceError, - ), - #[class(type)] - #[error("size must be larger than 0")] - InvalidSize, +pub struct GPURenderPassEncoder { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, + + pub render_pass: RefCell, + pub label: String, } -pub(crate) struct WebGpuRenderPass( - pub(crate) RefCell, -); -impl Resource for WebGpuRenderPass { - fn name(&self) -> Cow { - "webGPURenderPass".into() +impl GarbageCollected for GPURenderPassEncoder {} + +#[op2] +impl GPURenderPassEncoder { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[required(6)] + fn set_viewport( + &self, + #[webidl] x: f32, + #[webidl] y: f32, + #[webidl] width: f32, + #[webidl] height: f32, + #[webidl] min_depth: f32, + #[webidl] max_depth: f32, + ) { + let err = self + .instance + .render_pass_set_viewport( + &mut self.render_pass.borrow_mut(), + x, + y, + width, + height, + min_depth, + max_depth, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(4)] + fn set_scissor_rect( + &self, + #[webidl(options(enforce_range = true))] x: u32, + #[webidl(options(enforce_range = true))] y: u32, + #[webidl(options(enforce_range = true))] width: u32, + #[webidl(options(enforce_range = true))] height: u32, + ) { + let err = self + .instance + .render_pass_set_scissor_rect( + &mut self.render_pass.borrow_mut(), + x, + y, + width, + height, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + fn set_blend_constant(&self, #[webidl] color: GPUColor) { + let err = self + .instance + .render_pass_set_blend_constant( + &mut self.render_pass.borrow_mut(), + color.into(), + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + fn set_stencil_reference( + &self, + #[webidl(options(enforce_range = true))] reference: u32, + ) { + let err = self + .instance + .render_pass_set_stencil_reference( + &mut self.render_pass.borrow_mut(), + reference, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + fn begin_occlusion_query( + &self, + #[webidl(options(enforce_range = true))] query_index: u32, + ) { + let err = self + .instance + .render_pass_begin_occlusion_query( + &mut self.render_pass.borrow_mut(), + query_index, + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + fn end_occlusion_query(&self) { + let err = self + .instance + .render_pass_end_occlusion_query(&mut self.render_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + fn execute_bundles(&self, #[webidl] bundles: Vec>) { + let err = self + .instance + .render_pass_execute_bundles( + &mut self.render_pass.borrow_mut(), + &bundles + .into_iter() + .map(|bundle| bundle.id) + .collect::>(), + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + fn end(&self) { + let err = self + .instance + .render_pass_end(&mut self.render_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + fn push_debug_group(&self, #[webidl] group_label: String) { + let err = self + .instance + .render_pass_push_debug_group( + &mut self.render_pass.borrow_mut(), + &group_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + fn pop_debug_group(&self) { + let err = self + .instance + .render_pass_pop_debug_group(&mut self.render_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + fn insert_debug_marker(&self, #[webidl] marker_label: String) { + let err = self + .instance + .render_pass_insert_debug_marker( + &mut self.render_pass.borrow_mut(), + &marker_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + fn set_bind_group<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + #[webidl(options(enforce_range = true))] index: u32, + #[webidl] bind_group: Nullable>, + dynamic_offsets: v8::Local<'a, v8::Value>, + dynamic_offsets_data_start: v8::Local<'a, v8::Value>, + dynamic_offsets_data_length: v8::Local<'a, v8::Value>, + ) -> Result<(), WebIdlError> { + const PREFIX: &str = + "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; + + let err = if let Ok(uint_32) = dynamic_offsets.try_cast::() + { + let start = u64::convert( + scope, + dynamic_offsets_data_start, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 4")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + let len = u32::convert( + scope, + dynamic_offsets_data_length, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 5")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + + let ab = uint_32.buffer(scope).unwrap(); + let ptr = ab.data().unwrap(); + let ab_len = ab.byte_length() / 4; + + let data = + unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; + + let offsets = &data[start..(start + len)]; + + self + .instance + .render_pass_set_bind_group( + &mut self.render_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets, + ) + .err() + } else { + let offsets = >>::convert( + scope, + dynamic_offsets, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 3")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? + .unwrap_or_default(); + + self + .instance + .render_pass_set_bind_group( + &mut self.render_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + &offsets, + ) + .err() + }; + + self.error_handler.push_error(err); + + Ok(()) + } + + fn set_pipeline( + &self, + #[webidl] pipeline: Ptr, + ) { + let err = self + .instance + .render_pass_set_pipeline(&mut self.render_pass.borrow_mut(), pipeline.id) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + fn set_index_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl] index_format: crate::render_pipeline::GPUIndexFormat, + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let err = self + .instance + .render_pass_set_index_buffer( + &mut self.render_pass.borrow_mut(), + buffer.id, + index_format.into(), + offset, + size.and_then(NonZeroU64::new), + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + fn set_vertex_buffer( + &self, + #[webidl(options(enforce_range = true))] slot: u32, + #[webidl] buffer: Ptr, // TODO: support nullable buffer + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let err = self + .instance + .render_pass_set_vertex_buffer( + &mut self.render_pass.borrow_mut(), + slot, + buffer.id, + offset, + size.and_then(NonZeroU64::new), + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + fn draw( + &self, + #[webidl(options(enforce_range = true))] vertex_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) { + let err = self + .instance + .render_pass_draw( + &mut self.render_pass.borrow_mut(), + vertex_count, + instance_count, + first_vertex, + first_instance, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + fn draw_indexed( + &self, + #[webidl(options(enforce_range = true))] index_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_index: u32, + #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) { + let err = self + .instance + .render_pass_draw_indexed( + &mut self.render_pass.borrow_mut(), + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + fn draw_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) { + let err = self + .instance + .render_pass_draw_indirect( + &mut self.render_pass.borrow_mut(), + indirect_buffer.id, + indirect_offset, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + fn draw_indexed_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) { + let err = self + .instance + .render_pass_draw_indexed_indirect( + &mut self.render_pass.borrow_mut(), + indirect_buffer.id, + indirect_offset, + ) + .err(); + self.error_handler.push_error(err); } } -#[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, +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURenderPassDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub color_attachments: Vec>, + pub depth_stencil_attachment: Option, + pub occlusion_query_set: Option>, + pub timestamp_writes: Option, + #[webidl(default = 50000000)] + #[options(enforce_range = true)] + pub max_draw_count: u64, } -#[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_commands::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()) +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURenderPassColorAttachment { + pub view: Ptr, + #[options(enforce_range = true)] + pub depth_slice: Option, + pub resolve_target: Option>, + pub clear_value: Option, + pub load_op: GPULoadOp, + pub store_op: GPUStoreOp, } -#[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_commands::wgpu_render_pass_set_scissor_rect( - &mut render_pass_resource.0.borrow_mut(), - x, - y, - width, - height, - ); - - Ok(WebGpuResult::empty()) +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPULoadOp { + Load, + Clear, +} +impl From for wgpu_core::command::LoadOp { + fn from(value: GPULoadOp) -> Self { + match value { + GPULoadOp::Load => Self::Load, + GPULoadOp::Clear => Self::Clear, + } + } } -#[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_commands::wgpu_render_pass_set_blend_constant( - &mut render_pass_resource.0.borrow_mut(), - &color, - ); - - Ok(WebGpuResult::empty()) +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUStoreOp { + Store, + Discard, +} +impl From for wgpu_core::command::StoreOp { + fn from(value: GPUStoreOp) -> Self { + match value { + GPUStoreOp::Store => Self::Store, + GPUStoreOp::Discard => Self::Discard, + } + } } -#[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_commands::wgpu_render_pass_set_stencil_reference( - &mut render_pass_resource.0.borrow_mut(), - reference, - ); - - Ok(WebGpuResult::empty()) +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURenderPassDepthStencilAttachment { + pub view: Ptr, + pub depth_clear_value: Option, + pub depth_load_op: Option, + pub depth_store_op: Option, + #[webidl(default = false)] + pub depth_read_only: bool, + #[webidl(default = 0)] + #[options(enforce_range = true)] + pub stencil_clear_value: u32, + pub stencil_load_op: Option, + pub stencil_store_op: Option, + #[webidl(default = false)] + pub stencil_read_only: bool, } -#[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_commands::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_commands::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::, ResourceError>>()?; - - let render_pass_resource = state - .resource_table - .get::(render_pass_rid)?; - - wgpu_core::command::render_commands::wgpu_render_pass_execute_bundles( - &mut render_pass_resource.0.borrow_mut(), - &bundles, - ); - - 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]; - - wgpu_core::command::render_commands::wgpu_render_pass_set_bind_group( - &mut render_pass_resource.0.borrow_mut(), - index, - bind_group_resource.1, - dynamic_offsets_data, - ); - - 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)?; - - wgpu_core::command::render_commands::wgpu_render_pass_push_debug_group( - &mut render_pass_resource.0.borrow_mut(), - group_label, - 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_commands::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)?; - - wgpu_core::command::render_commands::wgpu_render_pass_insert_debug_marker( - &mut render_pass_resource.0.borrow_mut(), - marker_label, - 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_commands::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(RenderPassError::InvalidSize)?) - } 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(RenderPassError::InvalidSize)?) - } else { - None - }; - - wgpu_core::command::render_commands::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_commands::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_commands::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_commands::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_commands::wgpu_render_pass_draw_indexed_indirect( - &mut render_pass_resource.0.borrow_mut(), - buffer_resource.1, - indirect_offset, - ); - - Ok(WebGpuResult::empty()) +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURenderPassTimestampWrites { + pub query_set: Ptr, + #[options(enforce_range = true)] + pub beginning_of_pass_write_index: Option, + #[options(enforce_range = true)] + pub end_of_pass_write_index: Option, } diff --git a/ext/webgpu/render_pipeline.rs b/ext/webgpu/render_pipeline.rs new file mode 100644 index 0000000000..27dfd7f590 --- /dev/null +++ b/ext/webgpu/render_pipeline.rs @@ -0,0 +1,550 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use deno_core::cppgc::Ptr; +use deno_core::op2; +use deno_core::webidl::Nullable; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use indexmap::IndexMap; + +use crate::bind_group_layout::GPUBindGroupLayout; +use crate::sampler::GPUCompareFunction; +use crate::shader::GPUShaderModule; +use crate::texture::GPUTextureFormat; +use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode; +use crate::Instance; + +pub struct GPURenderPipeline { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, + + pub id: wgpu_core::id::RenderPipelineId, + pub label: String, +} + +impl Drop for GPURenderPipeline { + fn drop(&mut self) { + self.instance.render_pipeline_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPURenderPipeline { + const NAME: &'static str = "GPURenderPipeline"; +} + +impl GarbageCollected for GPURenderPipeline {} + +#[op2] +impl GPURenderPipeline { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[cppgc] + fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout { + let (id, err) = self + .instance + .render_pipeline_get_bind_group_layout(self.id, index, None); + + self.error_handler.push_error(err); + + // TODO: wgpu needs to add a way to retrieve the label + GPUBindGroupLayout { + instance: self.instance.clone(), + id, + label: "".to_string(), + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPURenderPipelineDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode, + pub vertex: GPUVertexState, + pub primitive: GPUPrimitiveState, // TODO: default {} + pub depth_stencil: Option, + pub multisample: GPUMultisampleState, // TODO: default {} + pub fragment: Option, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUMultisampleState { + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub count: u32, + #[webidl(default = 0xFFFFFFFF)] + #[options(enforce_range = true)] + pub mask: u32, + #[webidl(default = false)] + pub alpha_to_coverage_enabled: bool, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUDepthStencilState { + pub format: GPUTextureFormat, + pub depth_write_enabled: Option, + pub depth_compare: Option, + pub stencil_front: GPUStencilFaceState, // TODO: default {} + pub stencil_back: GPUStencilFaceState, // TODO: default {} + #[webidl(default = 0xFFFFFFFF)] + #[options(enforce_range = true)] + pub stencil_read_mask: u32, + #[webidl(default = 0xFFFFFFFF)] + #[options(enforce_range = true)] + pub stencil_write_mask: u32, + #[webidl(default = 0)] + #[options(enforce_range = true)] + pub depth_bias: i32, + #[webidl(default = 0.0)] + pub depth_bias_slope_scale: f32, + #[webidl(default = 0.0)] + pub depth_bias_clamp: f32, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUStencilFaceState { + #[webidl(default = GPUCompareFunction::Always)] + pub compare: GPUCompareFunction, + #[webidl(default = GPUStencilOperation::Keep)] + pub fail_op: GPUStencilOperation, + #[webidl(default = GPUStencilOperation::Keep)] + pub depth_fail_op: GPUStencilOperation, + #[webidl(default = GPUStencilOperation::Keep)] + pub pass_op: GPUStencilOperation, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUStencilOperation { + Keep, + Zero, + Replace, + Invert, + IncrementClamp, + DecrementClamp, + IncrementWrap, + DecrementWrap, +} + +impl From for wgpu_types::StencilOperation { + fn from(value: GPUStencilOperation) -> Self { + match value { + GPUStencilOperation::Keep => Self::Keep, + GPUStencilOperation::Zero => Self::Zero, + GPUStencilOperation::Replace => Self::Replace, + GPUStencilOperation::Invert => Self::Invert, + GPUStencilOperation::IncrementClamp => Self::IncrementClamp, + GPUStencilOperation::DecrementClamp => Self::DecrementClamp, + GPUStencilOperation::IncrementWrap => Self::IncrementWrap, + GPUStencilOperation::DecrementWrap => Self::DecrementWrap, + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUVertexState { + pub module: Ptr, + pub entry_point: Option, + #[webidl(default = Default::default())] + pub constants: IndexMap, + #[webidl(default = vec![])] + pub buffers: Vec>, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUFragmentState { + pub module: Ptr, + pub entry_point: Option, + #[webidl(default = Default::default())] + pub constants: IndexMap, + pub targets: Vec>, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUColorTargetState { + pub format: GPUTextureFormat, + pub blend: Option, + #[webidl(default = 0xF)] + #[options(enforce_range = true)] + pub write_mask: u32, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBlendState { + pub color: GPUBlendComponent, + pub alpha: GPUBlendComponent, +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUBlendComponent { + #[webidl(default = GPUBlendOperation::Add)] + pub operation: GPUBlendOperation, + #[webidl(default = GPUBlendFactor::One)] + pub src_factor: GPUBlendFactor, + #[webidl(default = GPUBlendFactor::Zero)] + pub dst_factor: GPUBlendFactor, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUBlendOperation { + Add, + Subtract, + ReverseSubtract, + Min, + Max, +} + +impl From for wgpu_types::BlendOperation { + fn from(value: GPUBlendOperation) -> Self { + match value { + GPUBlendOperation::Add => Self::Add, + GPUBlendOperation::Subtract => Self::Subtract, + GPUBlendOperation::ReverseSubtract => Self::ReverseSubtract, + GPUBlendOperation::Min => Self::Min, + GPUBlendOperation::Max => Self::Max, + } + } +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUBlendFactor { + #[webidl(rename = "zero")] + Zero, + #[webidl(rename = "one")] + One, + #[webidl(rename = "src")] + Src, + #[webidl(rename = "one-minus-src")] + OneMinusSrc, + #[webidl(rename = "src-alpha")] + SrcAlpha, + #[webidl(rename = "one-minus-src-alpha")] + OneMinusSrcAlpha, + #[webidl(rename = "dst")] + Dst, + #[webidl(rename = "one-minus-dst")] + OneMinusDst, + #[webidl(rename = "dst-alpha")] + DstAlpha, + #[webidl(rename = "one-minus-dst-alpha")] + OneMinusDstAlpha, + #[webidl(rename = "src-alpha-saturated")] + SrcAlphaSaturated, + #[webidl(rename = "constant")] + Constant, + #[webidl(rename = "one-minus-constant")] + OneMinusConstant, + #[webidl(rename = "src1")] + Src1, + #[webidl(rename = "one-minus-src1")] + OneMinusSrc1, + #[webidl(rename = "src1-alpha")] + Src1Alpha, + #[webidl(rename = "one-minus-src1-alpha")] + OneMinusSrc1Alpha, +} + +impl From for wgpu_types::BlendFactor { + fn from(value: GPUBlendFactor) -> Self { + match value { + GPUBlendFactor::Zero => Self::Zero, + GPUBlendFactor::One => Self::One, + GPUBlendFactor::Src => Self::Src, + GPUBlendFactor::OneMinusSrc => Self::OneMinusSrc, + GPUBlendFactor::SrcAlpha => Self::SrcAlpha, + GPUBlendFactor::OneMinusSrcAlpha => Self::OneMinusSrcAlpha, + GPUBlendFactor::Dst => Self::Dst, + GPUBlendFactor::OneMinusDst => Self::OneMinusDst, + GPUBlendFactor::DstAlpha => Self::DstAlpha, + GPUBlendFactor::OneMinusDstAlpha => Self::OneMinusDstAlpha, + GPUBlendFactor::SrcAlphaSaturated => Self::SrcAlphaSaturated, + GPUBlendFactor::Constant => Self::Constant, + GPUBlendFactor::OneMinusConstant => Self::OneMinusConstant, + GPUBlendFactor::Src1 => Self::Src1, + GPUBlendFactor::OneMinusSrc1 => Self::OneMinusSrc1, + GPUBlendFactor::Src1Alpha => Self::Src1Alpha, + GPUBlendFactor::OneMinusSrc1Alpha => Self::OneMinusSrc1Alpha, + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUPrimitiveState { + #[webidl(default = GPUPrimitiveTopology::TriangleList)] + pub topology: GPUPrimitiveTopology, + pub strip_index_format: Option, + #[webidl(default = GPUFrontFace::Ccw)] + pub front_face: GPUFrontFace, + #[webidl(default = GPUCullMode::None)] + pub cull_mode: GPUCullMode, + #[webidl(default = false)] + pub unclipped_depth: bool, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUPrimitiveTopology { + PointList, + LineList, + LineStrip, + TriangleList, + TriangleStrip, +} + +impl From for wgpu_types::PrimitiveTopology { + fn from(value: GPUPrimitiveTopology) -> Self { + match value { + GPUPrimitiveTopology::PointList => Self::PointList, + GPUPrimitiveTopology::LineList => Self::LineList, + GPUPrimitiveTopology::LineStrip => Self::LineStrip, + GPUPrimitiveTopology::TriangleList => Self::TriangleList, + GPUPrimitiveTopology::TriangleStrip => Self::TriangleStrip, + } + } +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUIndexFormat { + #[webidl(rename = "uint16")] + Uint16, + #[webidl(rename = "uint32")] + Uint32, +} + +impl From for wgpu_types::IndexFormat { + fn from(value: GPUIndexFormat) -> Self { + match value { + GPUIndexFormat::Uint16 => Self::Uint16, + GPUIndexFormat::Uint32 => Self::Uint32, + } + } +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUFrontFace { + Ccw, + Cw, +} + +impl From for wgpu_types::FrontFace { + fn from(value: GPUFrontFace) -> Self { + match value { + GPUFrontFace::Ccw => Self::Ccw, + GPUFrontFace::Cw => Self::Cw, + } + } +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUCullMode { + None, + Front, + Back, +} + +impl From for Option { + fn from(value: GPUCullMode) -> Self { + match value { + GPUCullMode::None => None, + GPUCullMode::Front => Some(wgpu_types::Face::Front), + GPUCullMode::Back => Some(wgpu_types::Face::Back), + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUVertexBufferLayout { + #[options(enforce_range = true)] + pub array_stride: u64, + #[webidl(default = GPUVertexStepMode::Vertex)] + pub step_mode: GPUVertexStepMode, + pub attributes: Vec, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUVertexStepMode { + Vertex, + Instance, +} + +impl From for wgpu_types::VertexStepMode { + fn from(value: GPUVertexStepMode) -> Self { + match value { + GPUVertexStepMode::Vertex => Self::Vertex, + GPUVertexStepMode::Instance => Self::Instance, + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUVertexAttribute { + pub format: GPUVertexFormat, + #[options(enforce_range = true)] + pub offset: u64, + #[options(enforce_range = true)] + pub shader_location: u32, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUVertexFormat { + // #[webidl(rename = "uint8")] + // Uint8, + #[webidl(rename = "uint8x2")] + Uint8x2, + #[webidl(rename = "uint8x4")] + Uint8x4, + // #[webidl(rename = "sint8")] + // Sint8, + #[webidl(rename = "sint8x2")] + Sint8x2, + #[webidl(rename = "sint8x4")] + Sint8x4, + // #[webidl(rename = "unorm8")] + // Unorm8, + #[webidl(rename = "unorm8x2")] + Unorm8x2, + #[webidl(rename = "unorm8x4")] + Unorm8x4, + // #[webidl(rename = "snorm8")] + // Snorm8, + #[webidl(rename = "snorm8x2")] + Snorm8x2, + #[webidl(rename = "snorm8x4")] + Snorm8x4, + // #[webidl(rename = "uint16")] + // Uint16, + #[webidl(rename = "uint16x2")] + Uint16x2, + #[webidl(rename = "uint16x4")] + Uint16x4, + // #[webidl(rename = "sint16")] + // Sint16, + #[webidl(rename = "sint16x2")] + Sint16x2, + #[webidl(rename = "sint16x4")] + Sint16x4, + // #[webidl(rename = "unorm16")] + // Unorm16, + #[webidl(rename = "unorm16x2")] + Unorm16x2, + #[webidl(rename = "unorm16x4")] + Unorm16x4, + // #[webidl(rename = "snorm16")] + // Snorm16, + #[webidl(rename = "snorm16x2")] + Snorm16x2, + #[webidl(rename = "snorm16x4")] + Snorm16x4, + // #[webidl(rename = "float16")] + // Float16, + #[webidl(rename = "float16x2")] + Float16x2, + #[webidl(rename = "float16x4")] + Float16x4, + #[webidl(rename = "float32")] + Float32, + #[webidl(rename = "float32x2")] + Float32x2, + #[webidl(rename = "float32x3")] + Float32x3, + #[webidl(rename = "float32x4")] + Float32x4, + #[webidl(rename = "uint32")] + Uint32, + #[webidl(rename = "uint32x2")] + Uint32x2, + #[webidl(rename = "uint32x3")] + Uint32x3, + #[webidl(rename = "uint32x4")] + Uint32x4, + #[webidl(rename = "sint32")] + Sint32, + #[webidl(rename = "sint32x2")] + Sint32x2, + #[webidl(rename = "sint32x3")] + Sint32x3, + #[webidl(rename = "sint32x4")] + Sint32x4, + #[webidl(rename = "unorm10-10-10-2")] + Unorm1010102, + // #[webidl(rename = "unorm8x4-bgra")] + // Unorm8x4Bgra, +} + +impl From for wgpu_types::VertexFormat { + fn from(value: GPUVertexFormat) -> Self { + match value { + //GPUVertexFormat::Uint8 => Self::Uint8, + GPUVertexFormat::Uint8x2 => Self::Uint8x2, + GPUVertexFormat::Uint8x4 => Self::Uint8x4, + //GPUVertexFormat::Sint8 => Self::Sint8, + GPUVertexFormat::Sint8x2 => Self::Sint8x2, + GPUVertexFormat::Sint8x4 => Self::Sint8x4, + //GPUVertexFormat::Unorm8 => Self::Unorm8, + GPUVertexFormat::Unorm8x2 => Self::Unorm8x2, + GPUVertexFormat::Unorm8x4 => Self::Unorm8x4, + //GPUVertexFormat::Snorm8 => Self::Snorm8, + GPUVertexFormat::Snorm8x2 => Self::Snorm8x2, + GPUVertexFormat::Snorm8x4 => Self::Snorm8x4, + //GPUVertexFormat::Uint16 => Self::Uint16, + GPUVertexFormat::Uint16x2 => Self::Uint16x2, + GPUVertexFormat::Uint16x4 => Self::Uint16x4, + //GPUVertexFormat::Sint16 => Self::Sint16, + GPUVertexFormat::Sint16x2 => Self::Sint16x2, + GPUVertexFormat::Sint16x4 => Self::Sint16x4, + //GPUVertexFormat::Unorm16 => Self::Unorm16, + GPUVertexFormat::Unorm16x2 => Self::Unorm16x2, + GPUVertexFormat::Unorm16x4 => Self::Unorm16x4, + //GPUVertexFormat::Snorm16 => Self::Snorm16, + GPUVertexFormat::Snorm16x2 => Self::Snorm16x2, + GPUVertexFormat::Snorm16x4 => Self::Snorm16x4, + //GPUVertexFormat::Float16 => Self::Float16, + GPUVertexFormat::Float16x2 => Self::Float16x2, + GPUVertexFormat::Float16x4 => Self::Float16x4, + GPUVertexFormat::Float32 => Self::Float32, + GPUVertexFormat::Float32x2 => Self::Float32x2, + GPUVertexFormat::Float32x3 => Self::Float32x3, + GPUVertexFormat::Float32x4 => Self::Float32x4, + GPUVertexFormat::Uint32 => Self::Uint32, + GPUVertexFormat::Uint32x2 => Self::Uint32x2, + GPUVertexFormat::Uint32x3 => Self::Uint32x3, + GPUVertexFormat::Uint32x4 => Self::Uint32x4, + GPUVertexFormat::Sint32 => Self::Sint32, + GPUVertexFormat::Sint32x2 => Self::Sint32x2, + GPUVertexFormat::Sint32x3 => Self::Sint32x3, + GPUVertexFormat::Sint32x4 => Self::Sint32x4, + GPUVertexFormat::Unorm1010102 => Self::Unorm10_10_10_2, + //GPUVertexFormat::Unorm8x4Bgra => Self::Unorm8x4Bgra, + } + } +} diff --git a/ext/webgpu/sampler.rs b/ext/webgpu/sampler.rs index 88c1947e63..997d2acde2 100644 --- a/ext/webgpu/sampler.rs +++ b/ext/webgpu/sampler.rs @@ -1,79 +1,134 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; -use std::rc::Rc; - use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; -use super::error::WebGpuResult; +use crate::Instance; -pub(crate) struct WebGpuSampler( - pub(crate) crate::Instance, - pub(crate) wgpu_core::id::SamplerId, -); -impl Resource for WebGpuSampler { - fn name(&self) -> Cow { - "webGPUSampler".into() - } +pub struct GPUSampler { + pub instance: Instance, + pub id: wgpu_core::id::SamplerId, + pub label: String, +} - fn close(self: Rc) { - gfx_select!(self.1 => self.0.sampler_drop(self.1)); +impl Drop for GPUSampler { + fn drop(&mut self) { + self.instance.sampler_drop(self.id); } } -#[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, +impl WebIdlInterfaceConverter for GPUSampler { + const NAME: &'static str = "GPUSampler"; } +impl GarbageCollected for GPUSampler {} + #[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, - None - ) => state, WebGpuSampler) +impl GPUSampler { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(super) struct GPUSamplerDescriptor { + #[webidl(default = String::new())] + pub label: String, + + #[webidl(default = GPUAddressMode::ClampToEdge)] + pub address_mode_u: GPUAddressMode, + #[webidl(default = GPUAddressMode::ClampToEdge)] + pub address_mode_v: GPUAddressMode, + #[webidl(default = GPUAddressMode::ClampToEdge)] + pub address_mode_w: GPUAddressMode, + #[webidl(default = GPUFilterMode::Nearest)] + pub mag_filter: GPUFilterMode, + #[webidl(default = GPUFilterMode::Nearest)] + pub min_filter: GPUFilterMode, + #[webidl(default = GPUFilterMode::Nearest)] + pub mipmap_filter: GPUFilterMode, + + #[webidl(default = 0.0)] + pub lod_min_clamp: f32, + #[webidl(default = 32.0)] + pub lod_max_clamp: f32, + + pub compare: Option, + + #[webidl(default = 1)] + #[options(clamp = true)] + pub max_anisotropy: u16, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUAddressMode { + ClampToEdge, + Repeat, + MirrorRepeat, +} + +impl From for wgpu_types::AddressMode { + fn from(value: GPUAddressMode) -> Self { + match value { + GPUAddressMode::ClampToEdge => Self::ClampToEdge, + GPUAddressMode::Repeat => Self::Repeat, + GPUAddressMode::MirrorRepeat => Self::MirrorRepeat, + } + } +} + +// Same as GPUMipmapFilterMode +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUFilterMode { + Nearest, + Linear, +} + +impl From for wgpu_types::FilterMode { + fn from(value: GPUFilterMode) -> Self { + match value { + GPUFilterMode::Nearest => Self::Nearest, + GPUFilterMode::Linear => Self::Linear, + } + } +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUCompareFunction { + Never, + Less, + Equal, + LessEqual, + Greater, + NotEqual, + GreaterEqual, + Always, +} + +impl From for wgpu_types::CompareFunction { + fn from(value: GPUCompareFunction) -> Self { + match value { + GPUCompareFunction::Never => Self::Never, + GPUCompareFunction::Less => Self::Less, + GPUCompareFunction::Equal => Self::Equal, + GPUCompareFunction::LessEqual => Self::LessEqual, + GPUCompareFunction::Greater => Self::Greater, + GPUCompareFunction::NotEqual => Self::NotEqual, + GPUCompareFunction::GreaterEqual => Self::GreaterEqual, + GPUCompareFunction::Always => Self::Always, + } + } } diff --git a/ext/webgpu/shader.rs b/ext/webgpu/shader.rs index 84615ea6f6..a49dd57152 100644 --- a/ext/webgpu/shader.rs +++ b/ext/webgpu/shader.rs @@ -1,54 +1,49 @@ // Copyright 2018-2025 the Deno authors. MIT license. -use std::borrow::Cow; -use std::rc::Rc; - use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; -use super::error::WebGpuResult; +use crate::Instance; -pub(crate) struct WebGpuShaderModule( - pub(crate) super::Instance, - pub(crate) wgpu_core::id::ShaderModuleId, -); -impl Resource for WebGpuShaderModule { - fn name(&self) -> Cow { - "webGPUShaderModule".into() - } +pub struct GPUShaderModule { + pub instance: Instance, + pub id: wgpu_core::id::ShaderModuleId, + pub label: String, +} - fn close(self: Rc) { - gfx_select!(self.1 => self.0.shader_module_drop(self.1)); +impl Drop for GPUShaderModule { + fn drop(&mut self) { + self.instance.shader_module_drop(self.id); } } +impl WebIdlInterfaceConverter for GPUShaderModule { + const NAME: &'static str = "GPUShaderModule"; +} + +impl GarbageCollected for GPUShaderModule {} + #[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, - None - ) => state, WebGpuShaderModule) +impl GPUShaderModule { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUShaderModuleDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub code: String, } diff --git a/ext/webgpu/surface.rs b/ext/webgpu/surface.rs index 23e617c7de..934a914047 100644 --- a/ext/webgpu/surface.rs +++ b/ext/webgpu/surface.rs @@ -1,144 +1,229 @@ -// Copyright 2018-2025 the Deno authors. MIT license. +use std::cell::RefCell; -use std::borrow::Cow; -use std::rc::Rc; - -use deno_core::error::ResourceError; use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; +use deno_core::v8; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use deno_core::_ops::make_cppgc_object; +use deno_core::cppgc::Ptr; +use deno_error::JsErrorBox; use wgpu_types::SurfaceStatus; -use crate::error::WebGpuResult; +use crate::device::GPUDevice; +use crate::texture::GPUTexture; +use crate::texture::GPUTextureFormat; #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SurfaceError { - #[class(inherit)] - #[error(transparent)] - Resource( - #[from] - #[inherit] - ResourceError, - ), + #[class("DOMExceptionInvalidStateError")] + #[error("Context is not configured")] + UnconfiguredContext, #[class(generic)] #[error("Invalid Surface Status")] InvalidStatus, #[class(generic)] #[error(transparent)] - Surface(wgpu_core::present::SurfaceError), + Surface(#[from] wgpu_core::present::SurfaceError), } -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); - } +pub struct Configuration { + pub device: Ptr, + pub usage: u32, + pub format: GPUTextureFormat, } -#[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, +pub struct GPUCanvasContext { + pub surface_id: wgpu_core::id::SurfaceId, + pub width: RefCell, + pub height: RefCell, + + pub config: RefCell>, + pub texture: RefCell>>, } +impl GarbageCollected for GPUCanvasContext {} + #[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; +impl GPUCanvasContext { + #[getter] + #[global] + fn canvas(&self) -> v8::Global { + todo!() + } - 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, - desired_maximum_frame_latency: 2, - }; + fn configure( + &self, + #[webidl] configuration: GPUCanvasConfiguration, + ) -> Result<(), JsErrorBox> { + let usage = wgpu_types::TextureUsages::from_bits(configuration.usage) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?; + let format = configuration.format.clone().into(); + let conf = wgpu_types::SurfaceConfiguration { + usage, + format, + width: *self.width.borrow(), + height: *self.height.borrow(), + present_mode: configuration + .present_mode + .map(Into::into) + .unwrap_or_default(), + alpha_mode: configuration.alpha_mode.into(), + view_formats: configuration + .view_formats + .into_iter() + .map(Into::into) + .collect(), + desired_maximum_frame_latency: 2, + }; - let err = - gfx_select!(device => instance.surface_configure(surface, device, &conf)); + let device = configuration.device; - Ok(WebGpuResult::maybe_err(err)) -} + let err = + device + .instance + .surface_configure(self.surface_id, device.id, &conf); -#[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; + device.error_handler.push_error(err); - let output = - gfx_select!(device => instance.surface_get_current_texture(surface, None)) - .map_err(SurfaceError::Surface)?; + self.config.borrow_mut().replace(Configuration { + device, + usage: configuration.usage, + format: configuration.format, + }); - 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)) + Ok(()) + } + + #[fast] + fn unconfigure(&self) { + *self.config.borrow_mut() = None; + } + + #[global] + fn get_current_texture( + &self, + scope: &mut v8::HandleScope, + ) -> Result, SurfaceError> { + let config = self.config.borrow(); + let Some(config) = config.as_ref() else { + return Err(SurfaceError::UnconfiguredContext); + }; + + { + if let Some(obj) = self.texture.borrow().as_ref() { + return Ok(obj.clone()); + } + } + + let output = config + .device + .instance + .surface_get_current_texture(self.surface_id, None)?; + + match output.status { + SurfaceStatus::Good | SurfaceStatus::Suboptimal => { + let id = output.texture_id.unwrap(); + + let texture = GPUTexture { + instance: config.device.instance.clone(), + error_handler: config.device.error_handler.clone(), + id, + label: "".to_string(), + size: wgpu_types::Extent3d { + width: *self.width.borrow(), + height: *self.height.borrow(), + depth_or_array_layers: 1, + }, + mip_level_count: 0, + sample_count: 0, + dimension: crate::texture::GPUTextureDimension::D2, + format: config.format.clone(), + usage: config.usage, + }; + let obj = make_cppgc_object(scope, texture); + let obj = v8::Global::new(scope, obj); + *self.texture.borrow_mut() = Some(obj.clone()); + + Ok(obj) + } + _ => Err(SurfaceError::InvalidStatus), } - _ => Err(SurfaceError::InvalidStatus), } } -#[op2(fast)] -pub fn op_webgpu_surface_present( - state: &mut OpState, - #[smi] device_rid: ResourceId, - #[smi] surface_rid: ResourceId, -) -> Result<(), SurfaceError> { - 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; +impl GPUCanvasContext { + pub fn present(&self) -> Result<(), SurfaceError> { + let config = self.config.borrow(); + let Some(config) = config.as_ref() else { + return Err(SurfaceError::UnconfiguredContext); + }; - let _ = gfx_select!(device => instance.surface_present(surface)) - .map_err(SurfaceError::Surface)?; + config.device.instance.surface_present(self.surface_id)?; - Ok(()) + Ok(()) + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +struct GPUCanvasConfiguration { + device: Ptr, + format: GPUTextureFormat, + #[webidl(default = wgpu_types::TextureUsages::RENDER_ATTACHMENT.bits())] + #[options(enforce_range = true)] + usage: u32, + #[webidl(default = GPUCanvasAlphaMode::Opaque)] + alpha_mode: GPUCanvasAlphaMode, + + // Extended from spec + present_mode: Option, + #[webidl(default = vec![])] + view_formats: Vec, +} + +#[derive(WebIDL)] +#[webidl(enum)] +enum GPUCanvasAlphaMode { + Opaque, + Premultiplied, +} + +impl From for wgpu_types::CompositeAlphaMode { + fn from(value: GPUCanvasAlphaMode) -> Self { + match value { + GPUCanvasAlphaMode::Opaque => Self::Opaque, + GPUCanvasAlphaMode::Premultiplied => Self::PreMultiplied, + } + } +} + +// Extended from spec +#[derive(WebIDL)] +#[webidl(enum)] +enum GPUPresentMode { + #[webidl(rename = "autoVsync")] + AutoVsync, + #[webidl(rename = "autoNoVsync")] + AutoNoVsync, + #[webidl(rename = "fifo")] + Fifo, + #[webidl(rename = "fifoRelaxed")] + FifoRelaxed, + #[webidl(rename = "immediate")] + Immediate, + #[webidl(rename = "mailbox")] + Mailbox, +} + +impl From for wgpu_types::PresentMode { + fn from(value: GPUPresentMode) -> Self { + match value { + GPUPresentMode::AutoVsync => Self::AutoVsync, + GPUPresentMode::AutoNoVsync => Self::AutoNoVsync, + GPUPresentMode::Fifo => Self::Fifo, + GPUPresentMode::FifoRelaxed => Self::FifoRelaxed, + GPUPresentMode::Immediate => Self::Immediate, + GPUPresentMode::Mailbox => Self::Mailbox, + } + } } diff --git a/ext/webgpu/texture.rs b/ext/webgpu/texture.rs index 10c5d902ee..3cab5bcfd6 100644 --- a/ext/webgpu/texture.rs +++ b/ext/webgpu/texture.rs @@ -1,133 +1,661 @@ // Copyright 2018-2025 the Deno authors. MIT license. use std::borrow::Cow; -use std::rc::Rc; use deno_core::op2; -use deno_core::OpState; -use deno_core::Resource; -use deno_core::ResourceId; -use serde::Deserialize; +use deno_core::webidl::WebIdlInterfaceConverter; +use deno_core::GarbageCollected; +use deno_core::WebIDL; +use deno_error::JsErrorBox; +use wgpu_types::AstcBlock; +use wgpu_types::AstcChannel; +use wgpu_types::Extent3d; +use wgpu_types::TextureAspect; +use wgpu_types::TextureDimension; +use wgpu_types::TextureFormat; +use wgpu_types::TextureViewDimension; -use super::error::WebGpuResult; -pub(crate) struct WebGpuTexture { - pub(crate) instance: crate::Instance, - pub(crate) id: wgpu_core::id::TextureId, - pub(crate) owned: bool, +use crate::Instance; + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUTextureDescriptor { + #[webidl(default = String::new())] + pub label: String, + + pub size: super::webidl::GPUExtent3D, + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub mip_level_count: u32, + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub sample_count: u32, + #[webidl(default = GPUTextureDimension::D2)] + pub dimension: GPUTextureDimension, + pub format: GPUTextureFormat, + #[options(enforce_range = true)] + pub usage: u32, + #[webidl(default = vec![])] + pub view_formats: Vec, } -impl Resource for WebGpuTexture { - fn name(&self) -> Cow { - "webGPUTexture".into() +pub struct GPUTexture { + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, + + pub id: wgpu_core::id::TextureId, + + pub label: String, + + pub size: Extent3d, + pub mip_level_count: u32, + pub sample_count: u32, + pub dimension: GPUTextureDimension, + pub format: GPUTextureFormat, + pub usage: u32, +} + +impl Drop for GPUTexture { + fn drop(&mut self) { + self.instance.texture_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPUTexture { + const NAME: &'static str = "GPUTexture"; +} + +impl GarbageCollected for GPUTexture {} + +#[op2] +impl GPUTexture { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label } - fn close(self: Rc) { - if self.owned { - let instance = &self.instance; - gfx_select!(self.id => instance.texture_drop(self.id, true)); + #[getter] + fn width(&self) -> u32 { + self.size.width + } + #[getter] + fn height(&self) -> u32 { + self.size.height + } + #[getter] + fn depth_or_array_layers(&self) -> u32 { + self.size.depth_or_array_layers + } + #[getter] + fn mip_level_count(&self) -> u32 { + self.mip_level_count + } + #[getter] + fn sample_count(&self) -> u32 { + self.sample_count + } + #[getter] + #[string] + fn dimension(&self) -> &'static str { + self.dimension.as_str() + } + #[getter] + #[string] + fn format(&self) -> &'static str { + self.format.as_str() + } + #[getter] + fn usage(&self) -> u32 { + self.usage + } + #[fast] + fn destroy(&self) -> Result<(), JsErrorBox> { + self + .instance + .texture_destroy(self.id) + .map_err(|e| JsErrorBox::generic(e.to_string())) + } + + #[cppgc] + fn create_view( + &self, + #[webidl] descriptor: GPUTextureViewDescriptor, + ) -> Result { + let wgpu_descriptor = wgpu_core::resource::TextureViewDescriptor { + label: Some(Cow::Owned(descriptor.label.clone())), + format: descriptor.format.map(Into::into), + dimension: descriptor.dimension.map(Into::into), + range: wgpu_types::ImageSubresourceRange { + aspect: descriptor.aspect.into(), + base_mip_level: descriptor.base_mip_level, + mip_level_count: descriptor.mip_level_count, + base_array_layer: descriptor.base_array_layer, + array_layer_count: descriptor.array_layer_count, + }, + }; + + let (id, err) = + self + .instance + .texture_create_view(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + Ok(GPUTextureView { + instance: self.instance.clone(), + id, + label: descriptor.label, + }) + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +struct GPUTextureViewDescriptor { + #[webidl(default = String::new())] + label: String, + + format: Option, + dimension: Option, + #[webidl(default = GPUTextureAspect::All)] + aspect: GPUTextureAspect, + #[webidl(default = 0)] + #[options(enforce_range = true)] + base_mip_level: u32, + #[options(enforce_range = true)] + mip_level_count: Option, + #[webidl(default = 0)] + #[options(enforce_range = true)] + base_array_layer: u32, + #[options(enforce_range = true)] + array_layer_count: Option, +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUTextureViewDimension { + #[webidl(rename = "1d")] + D1, + #[webidl(rename = "2d")] + D2, + #[webidl(rename = "2d-array")] + D2Array, + #[webidl(rename = "cube")] + Cube, + #[webidl(rename = "cube-array")] + CubeArray, + #[webidl(rename = "3d")] + D3, +} + +impl From for TextureViewDimension { + fn from(value: GPUTextureViewDimension) -> Self { + match value { + GPUTextureViewDimension::D1 => Self::D1, + GPUTextureViewDimension::D2 => Self::D2, + GPUTextureViewDimension::D3 => Self::D3, + GPUTextureViewDimension::D2Array => Self::D2Array, + GPUTextureViewDimension::Cube => Self::Cube, + GPUTextureViewDimension::CubeArray => Self::CubeArray, } } } -pub(crate) struct WebGpuTextureView( - pub(crate) crate::Instance, - pub(crate) wgpu_core::id::TextureViewId, -); -impl Resource for WebGpuTextureView { - fn name(&self) -> Cow { - "webGPUTextureView".into() - } +#[derive(WebIDL)] +#[webidl(enum)] +pub enum GPUTextureAspect { + All, + StencilOnly, + DepthOnly, +} - fn close(self: Rc) { - gfx_select!(self.1 => self.0.texture_view_drop(self.1, true)).unwrap(); +impl From for TextureAspect { + fn from(value: GPUTextureAspect) -> Self { + match value { + GPUTextureAspect::All => Self::All, + GPUTextureAspect::StencilOnly => Self::StencilOnly, + GPUTextureAspect::DepthOnly => Self::DepthOnly, + } } } -#[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, +pub struct GPUTextureView { + pub instance: Instance, + pub id: wgpu_core::id::TextureViewId, + pub label: String, } +impl Drop for GPUTextureView { + fn drop(&mut self) { + let _ = self.instance.texture_view_drop(self.id); + } +} + +impl WebIdlInterfaceConverter for GPUTextureView { + const NAME: &'static str = "GPUTextureView"; +} + +impl GarbageCollected for GPUTextureView {} +// TODO: weakref in texture for view + #[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, - None - )); - - let rid = state.resource_table.add(WebGpuTexture { - instance: instance.clone(), - id: val, - owned: true, - }); - - Ok(WebGpuResult::rid_err(rid, maybe_err)) +impl GPUTextureView { + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct CreateTextureViewArgs { - texture_rid: ResourceId, - label: String, - format: Option, - dimension: Option, - #[serde(flatten)] - range: wgpu_types::ImageSubresourceRange, +#[derive(WebIDL, Clone)] +#[webidl(enum)] +pub enum GPUTextureDimension { + #[webidl(rename = "1d")] + D1, + #[webidl(rename = "2d")] + D2, + #[webidl(rename = "3d")] + D3, } -#[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, - None - ) => state, WebGpuTextureView) +impl From for TextureDimension { + fn from(value: GPUTextureDimension) -> Self { + match value { + GPUTextureDimension::D1 => Self::D1, + GPUTextureDimension::D2 => Self::D2, + GPUTextureDimension::D3 => Self::D3, + } + } +} + +#[derive(WebIDL, Clone)] +#[webidl(enum)] +pub(crate) enum GPUTextureFormat { + #[webidl(rename = "r8unorm")] + R8unorm, + #[webidl(rename = "r8snorm")] + R8snorm, + #[webidl(rename = "r8uint")] + R8uint, + #[webidl(rename = "r8sint")] + R8sint, + #[webidl(rename = "r16uint")] + R16uint, + #[webidl(rename = "r16sint")] + R16sint, + #[webidl(rename = "r16float")] + R16float, + #[webidl(rename = "rg8unorm")] + Rg8unorm, + #[webidl(rename = "rg8snorm")] + Rg8snorm, + #[webidl(rename = "rg8uint")] + Rg8uint, + #[webidl(rename = "rg8sint")] + Rg8sint, + #[webidl(rename = "r32uint")] + R32uint, + #[webidl(rename = "r32sint")] + R32sint, + #[webidl(rename = "r32float")] + R32float, + #[webidl(rename = "rg16uint")] + Rg16uint, + #[webidl(rename = "rg16sint")] + Rg16sint, + #[webidl(rename = "rg16float")] + Rg16float, + #[webidl(rename = "rgba8unorm")] + Rgba8unorm, + #[webidl(rename = "rgba8unorm-srgb")] + Rgba8unormSrgb, + #[webidl(rename = "rgba8snorm")] + Rgba8snorm, + #[webidl(rename = "rgba8uint")] + Rgba8uint, + #[webidl(rename = "rgba8sint")] + Rgba8sint, + #[webidl(rename = "bgra8unorm")] + Bgra8unorm, + #[webidl(rename = "bgra8unorm-srgb")] + Bgra8unormSrgb, + #[webidl(rename = "rgb9e5ufloat")] + Rgb9e5ufloat, + #[webidl(rename = "rgb10a2uint")] + Rgb10a2uint, + #[webidl(rename = "rgb10a2unorm")] + Rgb10a2unorm, + #[webidl(rename = "rg11b10ufloat")] + Rg11b10ufloat, + #[webidl(rename = "rg32uint")] + Rg32uint, + #[webidl(rename = "rg32sint")] + Rg32sint, + #[webidl(rename = "rg32float")] + Rg32float, + #[webidl(rename = "rgba16uint")] + Rgba16uint, + #[webidl(rename = "rgba16sint")] + Rgba16sint, + #[webidl(rename = "rgba16float")] + Rgba16float, + #[webidl(rename = "rgba32uint")] + Rgba32uint, + #[webidl(rename = "rgba32sint")] + Rgba32sint, + #[webidl(rename = "rgba32float")] + Rgba32float, + #[webidl(rename = "stencil8")] + Stencil8, + #[webidl(rename = "depth16unorm")] + Depth16unorm, + #[webidl(rename = "depth24plus")] + Depth24plus, + #[webidl(rename = "depth24plus-stencil8")] + Depth24plusStencil8, + #[webidl(rename = "depth32float")] + Depth32float, + #[webidl(rename = "depth32float-stencil8")] + Depth32floatStencil8, + #[webidl(rename = "bc1-rgba-unorm")] + Bc1RgbaUnorm, + #[webidl(rename = "bc1-rgba-unorm-srgb")] + Bc1RgbaUnormSrgb, + #[webidl(rename = "bc2-rgba-unorm")] + Bc2RgbaUnorm, + #[webidl(rename = "bc2-rgba-unorm-srgb")] + Bc2RgbaUnormSrgb, + #[webidl(rename = "bc3-rgba-unorm")] + Bc3RgbaUnorm, + #[webidl(rename = "bc3-rgba-unorm-srgb")] + Bc3RgbaUnormSrgb, + #[webidl(rename = "bc4-r-unorm")] + Bc4RUnorm, + #[webidl(rename = "bc4-r-snorm")] + Bc4RSnorm, + #[webidl(rename = "bc5-rg-unorm")] + Bc5RgUnorm, + #[webidl(rename = "bc5-rg-snorm")] + Bc5RgSnorm, + #[webidl(rename = "bc6h-rgb-ufloat")] + Bc6hRgbUfloat, + #[webidl(rename = "bc6h-rgb-float")] + Bc6hRgbFloat, + #[webidl(rename = "bc7-rgba-unorm")] + Bc7RgbaUnorm, + #[webidl(rename = "bc7-rgba-unorm-srgb")] + Bc7RgbaUnormSrgb, + #[webidl(rename = "etc2-rgb8unorm")] + Etc2Rgb8unorm, + #[webidl(rename = "etc2-rgb8unorm-srgb")] + Etc2Rgb8unormSrgb, + #[webidl(rename = "etc2-rgb8a1unorm")] + Etc2Rgb8a1unorm, + #[webidl(rename = "etc2-rgb8a1unorm-srgb")] + Etc2Rgb8a1unormSrgb, + #[webidl(rename = "etc2-rgba8unorm")] + Etc2Rgba8unorm, + #[webidl(rename = "etc2-rgba8unorm-srgb")] + Etc2Rgba8unormSrgb, + #[webidl(rename = "eac-r11unorm")] + EacR11unorm, + #[webidl(rename = "eac-r11snorm")] + EacR11snorm, + #[webidl(rename = "eac-rg11unorm")] + EacRg11unorm, + #[webidl(rename = "eac-rg11snorm")] + EacRg11snorm, + #[webidl(rename = "astc-4x4-unorm")] + Astc4x4Unorm, + #[webidl(rename = "astc-4x4-unorm-srgb")] + Astc4x4UnormSrgb, + #[webidl(rename = "astc-5x4-unorm")] + Astc5x4Unorm, + #[webidl(rename = "astc-5x4-unorm-srgb")] + Astc5x4UnormSrgb, + #[webidl(rename = "astc-5x5-unorm")] + Astc5x5Unorm, + #[webidl(rename = "astc-5x5-unorm-srgb")] + Astc5x5UnormSrgb, + #[webidl(rename = "astc-6x5-unorm")] + Astc6x5Unorm, + #[webidl(rename = "astc-6x5-unorm-srgb")] + Astc6x5UnormSrgb, + #[webidl(rename = "astc-6x6-unorm")] + Astc6x6Unorm, + #[webidl(rename = "astc-6x6-unorm-srgb")] + Astc6x6UnormSrgb, + #[webidl(rename = "astc-8x5-unorm")] + Astc8x5Unorm, + #[webidl(rename = "astc-8x5-unorm-srgb")] + Astc8x5UnormSrgb, + #[webidl(rename = "astc-8x6-unorm")] + Astc8x6Unorm, + #[webidl(rename = "astc-8x6-unorm-srgb")] + Astc8x6UnormSrgb, + #[webidl(rename = "astc-8x8-unorm")] + Astc8x8Unorm, + #[webidl(rename = "astc-8x8-unorm-srgb")] + Astc8x8UnormSrgb, + #[webidl(rename = "astc-10x5-unorm")] + Astc10x5Unorm, + #[webidl(rename = "astc-10x5-unorm-srgb")] + Astc10x5UnormSrgb, + #[webidl(rename = "astc-10x6-unorm")] + Astc10x6Unorm, + #[webidl(rename = "astc-10x6-unorm-srgb")] + Astc10x6UnormSrgb, + #[webidl(rename = "astc-10x8-unorm")] + Astc10x8Unorm, + #[webidl(rename = "astc-10x8-unorm-srgb")] + Astc10x8UnormSrgb, + #[webidl(rename = "astc-10x10-unorm")] + Astc10x10Unorm, + #[webidl(rename = "astc-10x10-unorm-srgb")] + Astc10x10UnormSrgb, + #[webidl(rename = "astc-12x10-unorm")] + Astc12x10Unorm, + #[webidl(rename = "astc-12x10-unorm-srgb")] + Astc12x10UnormSrgb, + #[webidl(rename = "astc-12x12-unorm")] + Astc12x12Unorm, + #[webidl(rename = "astc-12x12-unorm-srgb")] + Astc12x12UnormSrgb, +} + +impl From for TextureFormat { + fn from(value: GPUTextureFormat) -> Self { + match value { + GPUTextureFormat::R8unorm => Self::R8Unorm, + GPUTextureFormat::R8snorm => Self::R8Snorm, + GPUTextureFormat::R8uint => Self::R8Uint, + GPUTextureFormat::R8sint => Self::R8Sint, + GPUTextureFormat::R16uint => Self::R16Uint, + GPUTextureFormat::R16sint => Self::R16Sint, + GPUTextureFormat::R16float => Self::R16Float, + GPUTextureFormat::Rg8unorm => Self::Rg8Unorm, + GPUTextureFormat::Rg8snorm => Self::Rg8Snorm, + GPUTextureFormat::Rg8uint => Self::Rg8Uint, + GPUTextureFormat::Rg8sint => Self::Rg8Sint, + GPUTextureFormat::R32uint => Self::R32Uint, + GPUTextureFormat::R32sint => Self::R32Sint, + GPUTextureFormat::R32float => Self::R32Float, + GPUTextureFormat::Rg16uint => Self::Rg16Uint, + GPUTextureFormat::Rg16sint => Self::Rg16Sint, + GPUTextureFormat::Rg16float => Self::Rg16Float, + GPUTextureFormat::Rgba8unorm => Self::Rgba8Unorm, + GPUTextureFormat::Rgba8unormSrgb => Self::Rgba8UnormSrgb, + GPUTextureFormat::Rgba8snorm => Self::Rgba8Snorm, + GPUTextureFormat::Rgba8uint => Self::Rgba8Uint, + GPUTextureFormat::Rgba8sint => Self::Rgba8Sint, + GPUTextureFormat::Bgra8unorm => Self::Bgra8Unorm, + GPUTextureFormat::Bgra8unormSrgb => Self::Bgra8UnormSrgb, + GPUTextureFormat::Rgb9e5ufloat => Self::Rgb9e5Ufloat, + GPUTextureFormat::Rgb10a2uint => Self::Rgb10a2Uint, + GPUTextureFormat::Rgb10a2unorm => Self::Rgb10a2Unorm, + GPUTextureFormat::Rg11b10ufloat => Self::Rg11b10Ufloat, + GPUTextureFormat::Rg32uint => Self::Rg32Uint, + GPUTextureFormat::Rg32sint => Self::Rg32Sint, + GPUTextureFormat::Rg32float => Self::Rg32Float, + GPUTextureFormat::Rgba16uint => Self::Rgba16Uint, + GPUTextureFormat::Rgba16sint => Self::Rgba16Sint, + GPUTextureFormat::Rgba16float => Self::Rgba16Float, + GPUTextureFormat::Rgba32uint => Self::Rgba32Uint, + GPUTextureFormat::Rgba32sint => Self::Rgba32Sint, + GPUTextureFormat::Rgba32float => Self::Rgba32Float, + GPUTextureFormat::Stencil8 => Self::Stencil8, + GPUTextureFormat::Depth16unorm => Self::Depth16Unorm, + GPUTextureFormat::Depth24plus => Self::Depth24Plus, + GPUTextureFormat::Depth24plusStencil8 => Self::Depth24PlusStencil8, + GPUTextureFormat::Depth32float => Self::Depth32Float, + GPUTextureFormat::Depth32floatStencil8 => Self::Depth32FloatStencil8, + GPUTextureFormat::Bc1RgbaUnorm => Self::Bc1RgbaUnorm, + GPUTextureFormat::Bc1RgbaUnormSrgb => Self::Bc1RgbaUnormSrgb, + GPUTextureFormat::Bc2RgbaUnorm => Self::Bc2RgbaUnorm, + GPUTextureFormat::Bc2RgbaUnormSrgb => Self::Bc2RgbaUnormSrgb, + GPUTextureFormat::Bc3RgbaUnorm => Self::Bc3RgbaUnorm, + GPUTextureFormat::Bc3RgbaUnormSrgb => Self::Bc3RgbaUnormSrgb, + GPUTextureFormat::Bc4RUnorm => Self::Bc4RUnorm, + GPUTextureFormat::Bc4RSnorm => Self::Bc4RSnorm, + GPUTextureFormat::Bc5RgUnorm => Self::Bc5RgUnorm, + GPUTextureFormat::Bc5RgSnorm => Self::Bc5RgSnorm, + GPUTextureFormat::Bc6hRgbUfloat => Self::Bc6hRgbUfloat, + GPUTextureFormat::Bc6hRgbFloat => Self::Bc6hRgbFloat, + GPUTextureFormat::Bc7RgbaUnorm => Self::Bc7RgbaUnorm, + GPUTextureFormat::Bc7RgbaUnormSrgb => Self::Bc7RgbaUnormSrgb, + GPUTextureFormat::Etc2Rgb8unorm => Self::Etc2Rgb8Unorm, + GPUTextureFormat::Etc2Rgb8unormSrgb => Self::Etc2Rgb8UnormSrgb, + GPUTextureFormat::Etc2Rgb8a1unorm => Self::Etc2Rgb8A1Unorm, + GPUTextureFormat::Etc2Rgb8a1unormSrgb => Self::Etc2Rgb8A1UnormSrgb, + GPUTextureFormat::Etc2Rgba8unorm => Self::Etc2Rgba8Unorm, + GPUTextureFormat::Etc2Rgba8unormSrgb => Self::Etc2Rgba8UnormSrgb, + GPUTextureFormat::EacR11unorm => Self::EacR11Unorm, + GPUTextureFormat::EacR11snorm => Self::EacR11Snorm, + GPUTextureFormat::EacRg11unorm => Self::EacRg11Unorm, + GPUTextureFormat::EacRg11snorm => Self::EacRg11Snorm, + GPUTextureFormat::Astc4x4Unorm => Self::Astc { + block: AstcBlock::B4x4, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc4x4UnormSrgb => Self::Astc { + block: AstcBlock::B5x4, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc5x4Unorm => Self::Astc { + block: AstcBlock::B5x4, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc5x4UnormSrgb => Self::Astc { + block: AstcBlock::B5x4, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc5x5Unorm => Self::Astc { + block: AstcBlock::B5x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc5x5UnormSrgb => Self::Astc { + block: AstcBlock::B5x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc6x5Unorm => Self::Astc { + block: AstcBlock::B6x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc6x5UnormSrgb => Self::Astc { + block: AstcBlock::B6x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc6x6Unorm => Self::Astc { + block: AstcBlock::B6x6, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc6x6UnormSrgb => Self::Astc { + block: AstcBlock::B6x6, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc8x5Unorm => Self::Astc { + block: AstcBlock::B8x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc8x5UnormSrgb => Self::Astc { + block: AstcBlock::B8x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc8x6Unorm => Self::Astc { + block: AstcBlock::B8x6, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc8x6UnormSrgb => Self::Astc { + block: AstcBlock::B8x6, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc8x8Unorm => Self::Astc { + block: AstcBlock::B8x8, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc8x8UnormSrgb => Self::Astc { + block: AstcBlock::B8x8, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x5Unorm => Self::Astc { + block: AstcBlock::B10x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x5UnormSrgb => Self::Astc { + block: AstcBlock::B10x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x6Unorm => Self::Astc { + block: AstcBlock::B10x6, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x6UnormSrgb => Self::Astc { + block: AstcBlock::B10x6, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x8Unorm => Self::Astc { + block: AstcBlock::B10x8, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x8UnormSrgb => Self::Astc { + block: AstcBlock::B10x8, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x10Unorm => Self::Astc { + block: AstcBlock::B10x10, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x10UnormSrgb => Self::Astc { + block: AstcBlock::B10x10, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc12x10Unorm => Self::Astc { + block: AstcBlock::B12x10, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc12x10UnormSrgb => Self::Astc { + block: AstcBlock::B12x10, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc12x12Unorm => Self::Astc { + block: AstcBlock::B12x12, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc12x12UnormSrgb => Self::Astc { + block: AstcBlock::B12x12, + channel: AstcChannel::UnormSrgb, + }, + } + } } diff --git a/ext/webgpu/webidl.rs b/ext/webgpu/webidl.rs new file mode 100644 index 0000000000..d7c51f6f8b --- /dev/null +++ b/ext/webgpu/webidl.rs @@ -0,0 +1,655 @@ +// Copyright 2018-2025 the Deno authors. MIT license. + +use std::borrow::Cow; +use std::collections::HashSet; + +use deno_core::cppgc::Ptr; +use deno_core::v8; +use deno_core::webidl::ContextFn; +use deno_core::webidl::IntOptions; +use deno_core::webidl::WebIdlConverter; +use deno_core::webidl::WebIdlError; +use deno_core::webidl::WebIdlErrorKind; +use deno_core::WebIDL; +use deno_error::JsErrorBox; + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUExtent3DDict { + #[options(enforce_range = true)] + width: u32, + #[webidl(default = 1)] + #[options(enforce_range = true)] + height: u32, + #[webidl(default = 1)] + #[options(enforce_range = true)] + depth_or_array_layers: u32, +} + +pub(crate) enum GPUExtent3D { + Dict(GPUExtent3DDict), + Sequence((u32, u32, u32)), +} + +impl<'a> WebIdlConverter<'a> for GPUExtent3D { + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_null_or_undefined() { + return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert( + scope, + value, + prefix, + context.borrowed(), + &options, + )?)); + } + if let Ok(obj) = value.try_cast::() { + let iter = v8::Symbol::get_iterator(scope); + if let Some(iter) = obj.get(scope, iter.into()) { + if !iter.is_undefined() { + let conv = >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )?; + if !(conv.len() > 1 && conv.len() < 3) { + return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUExtent3D must have between 1 and 3 elements, received {} elements", conv.len())))); + } + + let mut iter = conv.into_iter(); + return Ok(GPUExtent3D::Sequence(( + iter.next().unwrap(), + iter.next().unwrap_or(1), + iter.next().unwrap_or(1), + ))); + } + } + + return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert( + scope, value, prefix, context, &options, + )?)); + } + + Err(WebIdlError::new( + prefix, + context, + WebIdlErrorKind::ConvertToConverterType( + "sequence or GPUExtent3DDict", + ), + )) + } +} + +impl From for wgpu_types::Extent3d { + fn from(value: GPUExtent3D) -> Self { + match value { + GPUExtent3D::Dict(dict) => Self { + width: dict.width, + height: dict.height, + depth_or_array_layers: dict.depth_or_array_layers, + }, + GPUExtent3D::Sequence((width, height, depth)) => Self { + width, + height, + depth_or_array_layers: depth, + }, + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUOrigin3DDict { + #[webidl(default = 0)] + #[options(enforce_range = true)] + x: u32, + #[webidl(default = 0)] + #[options(enforce_range = true)] + y: u32, + #[webidl(default = 0)] + #[options(enforce_range = true)] + z: u32, +} + +pub(crate) enum GPUOrigin3D { + Dict(GPUOrigin3DDict), + Sequence((u32, u32, u32)), +} + +impl Default for GPUOrigin3D { + fn default() -> Self { + GPUOrigin3D::Sequence((0, 0, 0)) + } +} + +impl<'a> WebIdlConverter<'a> for GPUOrigin3D { + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_null_or_undefined() { + return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert( + scope, + value, + prefix, + context.borrowed(), + &options, + )?)); + } + if let Ok(obj) = value.try_cast::() { + let iter = v8::Symbol::get_iterator(scope); + if let Some(iter) = obj.get(scope, iter.into()) { + if !iter.is_undefined() { + let conv = >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )?; + if !(conv.len() > 1 && conv.len() < 3) { + return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUOrigin3D must have between 1 and 3 elements, received {} elements", conv.len())))); + } + + let mut iter = conv.into_iter(); + return Ok(GPUOrigin3D::Sequence(( + iter.next().unwrap(), + iter.next().unwrap_or(0), + iter.next().unwrap_or(0), + ))); + } + } + + return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert( + scope, value, prefix, context, &options, + )?)); + } + + Err(WebIdlError::new( + prefix, + context, + WebIdlErrorKind::ConvertToConverterType( + "sequence or GPUOrigin3DDict", + ), + )) + } +} + +impl From for wgpu_types::Origin3d { + fn from(value: GPUOrigin3D) -> Self { + match value { + GPUOrigin3D::Dict(dict) => Self { + x: dict.x, + y: dict.y, + z: dict.z, + }, + GPUOrigin3D::Sequence((x, y, z)) => Self { x, y, z }, + } + } +} + +#[derive(WebIDL)] +#[webidl(dictionary)] +pub(crate) struct GPUColorDict { + r: f64, + g: f64, + b: f64, + a: f64, +} + +pub(crate) enum GPUColor { + Dict(GPUColorDict), + Sequence((f64, f64, f64, f64)), +} + +impl<'a> WebIdlConverter<'a> for GPUColor { + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_null_or_undefined() { + return Ok(GPUColor::Dict(GPUColorDict::convert( + scope, + value, + prefix, + context.borrowed(), + options, + )?)); + } + if let Ok(obj) = value.try_cast::() { + let iter = v8::Symbol::get_iterator(scope); + if let Some(iter) = obj.get(scope, iter.into()) { + if !iter.is_undefined() { + let conv = >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + )?; + if conv.len() != 4 { + return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUColor must have 4 elements, received {} elements", conv.len())))); + } + + let mut iter = conv.into_iter(); + return Ok(GPUColor::Sequence(( + iter.next().unwrap(), + iter.next().unwrap(), + iter.next().unwrap(), + iter.next().unwrap(), + ))); + } + } + + return Ok(GPUColor::Dict(GPUColorDict::convert( + scope, value, prefix, context, options, + )?)); + } + + Err(WebIdlError::new( + prefix, + context, + WebIdlErrorKind::ConvertToConverterType( + "sequence or GPUOrigin3DDict", + ), + )) + } +} + +impl From for wgpu_types::Color { + fn from(value: GPUColor) -> Self { + match value { + GPUColor::Dict(dict) => Self { + r: dict.r, + g: dict.g, + b: dict.b, + a: dict.a, + }, + GPUColor::Sequence((r, g, b, a)) => Self { r, g, b, a }, + } + } +} + +#[derive(WebIDL)] +#[webidl(enum)] +pub(crate) enum GPUAutoLayoutMode { + Auto, +} + +pub(crate) enum GPUPipelineLayoutOrGPUAutoLayoutMode { + PipelineLayout(Ptr), + AutoLayoutMode(GPUAutoLayoutMode), +} + +impl From + for Option +{ + fn from(value: GPUPipelineLayoutOrGPUAutoLayoutMode) -> Self { + match value { + GPUPipelineLayoutOrGPUAutoLayoutMode::PipelineLayout(layout) => { + Some(layout.id) + } + GPUPipelineLayoutOrGPUAutoLayoutMode::AutoLayoutMode( + GPUAutoLayoutMode::Auto, + ) => None, + } + } +} + +impl<'a> WebIdlConverter<'a> for GPUPipelineLayoutOrGPUAutoLayoutMode { + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_object() { + Ok(Self::PipelineLayout(WebIdlConverter::convert( + scope, value, prefix, context, options, + )?)) + } else { + Ok(Self::AutoLayoutMode(WebIdlConverter::convert( + scope, value, prefix, context, options, + )?)) + } + } +} + +#[derive(WebIDL, Clone, Hash, Eq, PartialEq)] +#[webidl(enum)] +pub enum GPUFeatureName { + // api + #[webidl(rename = "depth-clip-control")] + DepthClipControl, + #[webidl(rename = "timestamp-query")] + TimestampQuery, + #[webidl(rename = "indirect-first-instance")] + IndirectFirstInstance, + // shader + #[webidl(rename = "shader-f16")] + ShaderF16, + // texture formats + #[webidl(rename = "depth32float-stencil8")] + Depth32floatStencil8, + #[webidl(rename = "texture-compression-bc")] + TextureCompressionBc, + #[webidl(rename = "texture-compression-bc-sliced-3d")] + TextureCompressionBcSliced3d, + #[webidl(rename = "texture-compression-etc2")] + TextureCompressionEtc2, + #[webidl(rename = "texture-compression-astc")] + TextureCompressionAstc, + #[webidl(rename = "rg11b10ufloat-renderable")] + Rg11b10ufloatRenderable, + #[webidl(rename = "bgra8unorm-storage")] + Bgra8unormStorage, + #[webidl(rename = "float32-filterable")] + Float32Filterable, + #[webidl(rename = "dual-source-blending")] + DualSourceBlending, + + // extended from spec + + // texture formats + #[webidl(rename = "texture-format-16-bit-norm")] + TextureFormat16BitNorm, + #[webidl(rename = "texture-compression-astc-hdr")] + TextureCompressionAstcHdr, + #[webidl(rename = "texture-adapter-specific-format-features")] + TextureAdapterSpecificFormatFeatures, + // api + #[webidl(rename = "pipeline-statistics-query")] + PipelineStatisticsQuery, + #[webidl(rename = "timestamp-query-inside-passes")] + TimestampQueryInsidePasses, + #[webidl(rename = "mappable-primary-buffers")] + MappablePrimaryBuffers, + #[webidl(rename = "texture-binding-array")] + TextureBindingArray, + #[webidl(rename = "buffer-binding-array")] + BufferBindingArray, + #[webidl(rename = "storage-resource-binding-array")] + StorageResourceBindingArray, + #[webidl( + rename = "sampled-texture-and-storage-buffer-array-non-uniform-indexing" + )] + SampledTextureAndStorageBufferArrayNonUniformIndexing, + #[webidl( + rename = "uniform-buffer-and-storage-texture-array-non-uniform-indexing" + )] + UniformBufferAndStorageTextureArrayNonUniformIndexing, + #[webidl(rename = "partially-bound-binding-array")] + PartiallyBoundBindingArray, + #[webidl(rename = "multi-draw-indirect")] + MultiDrawIndirect, + #[webidl(rename = "multi-draw-indirect-count")] + MultiDrawIndirectCount, + #[webidl(rename = "push-constants")] + PushConstants, + #[webidl(rename = "address-mode-clamp-to-zero")] + AddressModeClampToZero, + #[webidl(rename = "address-mode-clamp-to-border")] + AddressModeClampToBorder, + #[webidl(rename = "polygon-mode-line")] + PolygonModeLine, + #[webidl(rename = "polygon-mode-point")] + PolygonModePoint, + #[webidl(rename = "conservative-rasterization")] + ConservativeRasterization, + #[webidl(rename = "vertex-writable-storage")] + VertexWritableStorage, + #[webidl(rename = "clear-texture")] + ClearTexture, + #[webidl(rename = "spirv-shader-passthrough")] + SpirvShaderPassthrough, + #[webidl(rename = "multiview")] + Multiview, + #[webidl(rename = "vertex-attribute-64-bit")] + VertexAttribute64Bit, + // shader + #[webidl(rename = "shader-f64")] + ShaderF64, + #[webidl(rename = "shader-i16")] + ShaderI16, + #[webidl(rename = "shader-primitive-index")] + ShaderPrimitiveIndex, + #[webidl(rename = "shader-early-depth-test")] + ShaderEarlyDepthTest, +} + +pub fn feature_names_to_features( + names: Vec, +) -> wgpu_types::Features { + use wgpu_types::Features; + let mut features = Features::empty(); + + for name in names { + #[rustfmt::skip] + let feature = match name { + GPUFeatureName::DepthClipControl => Features::DEPTH_CLIP_CONTROL, + GPUFeatureName::TimestampQuery => Features::TIMESTAMP_QUERY, + GPUFeatureName::IndirectFirstInstance => Features::INDIRECT_FIRST_INSTANCE, + GPUFeatureName::ShaderF16 => Features::SHADER_F16, + GPUFeatureName::Depth32floatStencil8 => Features::DEPTH32FLOAT_STENCIL8, + GPUFeatureName::TextureCompressionBc => Features::TEXTURE_COMPRESSION_BC, + GPUFeatureName::TextureCompressionBcSliced3d => Features::TEXTURE_COMPRESSION_BC_SLICED_3D, + GPUFeatureName::TextureCompressionEtc2 => Features::TEXTURE_COMPRESSION_ETC2, + GPUFeatureName::TextureCompressionAstc => Features::TEXTURE_COMPRESSION_ASTC, + GPUFeatureName::Rg11b10ufloatRenderable => Features::RG11B10UFLOAT_RENDERABLE, + GPUFeatureName::Bgra8unormStorage => Features::BGRA8UNORM_STORAGE, + GPUFeatureName::Float32Filterable => Features::FLOAT32_FILTERABLE, + GPUFeatureName::DualSourceBlending => Features::DUAL_SOURCE_BLENDING, + GPUFeatureName::TextureFormat16BitNorm => Features::TEXTURE_FORMAT_16BIT_NORM, + GPUFeatureName::TextureCompressionAstcHdr => Features::TEXTURE_COMPRESSION_ASTC_HDR, + GPUFeatureName::TextureAdapterSpecificFormatFeatures => Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, + GPUFeatureName::PipelineStatisticsQuery => Features::PIPELINE_STATISTICS_QUERY, + GPUFeatureName::TimestampQueryInsidePasses => Features::TIMESTAMP_QUERY_INSIDE_PASSES, + GPUFeatureName::MappablePrimaryBuffers => Features::MAPPABLE_PRIMARY_BUFFERS, + GPUFeatureName::TextureBindingArray => Features::TEXTURE_BINDING_ARRAY, + GPUFeatureName::BufferBindingArray => Features::BUFFER_BINDING_ARRAY, + GPUFeatureName::StorageResourceBindingArray => Features::STORAGE_RESOURCE_BINDING_ARRAY, + GPUFeatureName::SampledTextureAndStorageBufferArrayNonUniformIndexing => Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + GPUFeatureName::UniformBufferAndStorageTextureArrayNonUniformIndexing => Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + GPUFeatureName::PartiallyBoundBindingArray => Features::PARTIALLY_BOUND_BINDING_ARRAY, + GPUFeatureName::MultiDrawIndirect => Features::MULTI_DRAW_INDIRECT, + GPUFeatureName::MultiDrawIndirectCount => Features::MULTI_DRAW_INDIRECT_COUNT, + GPUFeatureName::PushConstants => Features::PUSH_CONSTANTS, + GPUFeatureName::AddressModeClampToZero => Features::ADDRESS_MODE_CLAMP_TO_ZERO, + GPUFeatureName::AddressModeClampToBorder => Features::ADDRESS_MODE_CLAMP_TO_BORDER, + GPUFeatureName::PolygonModeLine => Features::POLYGON_MODE_LINE, + GPUFeatureName::PolygonModePoint => Features::POLYGON_MODE_POINT, + GPUFeatureName::ConservativeRasterization => Features::CONSERVATIVE_RASTERIZATION, + GPUFeatureName::VertexWritableStorage => Features::VERTEX_WRITABLE_STORAGE, + GPUFeatureName::ClearTexture => Features::CLEAR_TEXTURE, + GPUFeatureName::SpirvShaderPassthrough => Features::SPIRV_SHADER_PASSTHROUGH, + GPUFeatureName::Multiview => Features::MULTIVIEW, + GPUFeatureName::VertexAttribute64Bit => Features::VERTEX_ATTRIBUTE_64BIT, + GPUFeatureName::ShaderF64 => Features::SHADER_F64, + GPUFeatureName::ShaderI16 => Features::SHADER_F16, + GPUFeatureName::ShaderPrimitiveIndex => Features::SHADER_PRIMITIVE_INDEX, + GPUFeatureName::ShaderEarlyDepthTest => Features::SHADER_EARLY_DEPTH_TEST, + }; + features.set(feature, true); + } + + features +} + +pub fn features_to_feature_names( + features: wgpu_types::Features, +) -> HashSet { + use GPUFeatureName::*; + let mut return_features = HashSet::new(); + + // api + if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) { + return_features.insert(DepthClipControl); + } + if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) { + return_features.insert(TimestampQuery); + } + if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) { + return_features.insert(IndirectFirstInstance); + } + // shader + if features.contains(wgpu_types::Features::SHADER_F16) { + return_features.insert(ShaderF16); + } + // texture formats + if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) { + return_features.insert(Depth32floatStencil8); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) { + return_features.insert(TextureCompressionBc); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC_SLICED_3D) { + return_features.insert(TextureCompressionBcSliced3d); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) { + return_features.insert(TextureCompressionEtc2); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC) { + return_features.insert(TextureCompressionAstc); + } + if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) { + return_features.insert(Rg11b10ufloatRenderable); + } + if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) { + return_features.insert(Bgra8unormStorage); + } + if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) { + return_features.insert(Float32Filterable); + } + if features.contains(wgpu_types::Features::DUAL_SOURCE_BLENDING) { + return_features.insert(DualSourceBlending); + } + + // extended from spec + + // texture formats + if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) { + return_features.insert(TextureFormat16BitNorm); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) { + return_features.insert(TextureCompressionAstcHdr); + } + if features + .contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) + { + return_features.insert(TextureAdapterSpecificFormatFeatures); + } + // api + if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) { + return_features.insert(PipelineStatisticsQuery); + } + if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) { + return_features.insert(TimestampQueryInsidePasses); + } + if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) { + return_features.insert(MappablePrimaryBuffers); + } + if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) { + return_features.insert(TextureBindingArray); + } + if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) { + return_features.insert(BufferBindingArray); + } + if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) { + return_features.insert(StorageResourceBindingArray); + } + if features.contains( + wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + ) { + return_features.insert(SampledTextureAndStorageBufferArrayNonUniformIndexing); + } + if features.contains( + wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + ) { + return_features.insert(UniformBufferAndStorageTextureArrayNonUniformIndexing); + } + if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) { + return_features.insert(PartiallyBoundBindingArray); + } + if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) { + return_features.insert(MultiDrawIndirect); + } + if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) { + return_features.insert(MultiDrawIndirectCount); + } + if features.contains(wgpu_types::Features::PUSH_CONSTANTS) { + return_features.insert(PushConstants); + } + if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) { + return_features.insert(AddressModeClampToZero); + } + if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) { + return_features.insert(AddressModeClampToBorder); + } + if features.contains(wgpu_types::Features::POLYGON_MODE_LINE) { + return_features.insert(PolygonModeLine); + } + if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) { + return_features.insert(PolygonModePoint); + } + if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) { + return_features.insert(ConservativeRasterization); + } + if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) { + return_features.insert(VertexWritableStorage); + } + if features.contains(wgpu_types::Features::CLEAR_TEXTURE) { + return_features.insert(ClearTexture); + } + if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) { + return_features.insert(SpirvShaderPassthrough); + } + if features.contains(wgpu_types::Features::MULTIVIEW) { + return_features.insert(Multiview); + } + if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) { + return_features.insert(VertexAttribute64Bit); + } + // shader + if features.contains(wgpu_types::Features::SHADER_F64) { + return_features.insert(ShaderF64); + } + if features.contains(wgpu_types::Features::SHADER_I16) { + return_features.insert(ShaderI16); + } + if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) { + return_features.insert(ShaderPrimitiveIndex); + } + if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) { + return_features.insert(ShaderEarlyDepthTest); + } + + return_features +} diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js index b3c3b299f0..d460f8c863 100644 --- a/ext/webidl/00_webidl.js +++ b/ext/webidl/00_webidl.js @@ -38,6 +38,7 @@ const { MathRound, MathTrunc, Number, + SymbolFor, NumberIsFinite, NumberIsNaN, NumberMAX_SAFE_INTEGER, @@ -1311,18 +1312,17 @@ function configureProperties(obj) { } } -const setlikeInner = Symbol("[[set]]"); +const setlikeInner = SymbolFor("setlike_set"); // Ref: https://webidl.spec.whatwg.org/#es-setlike function setlike(obj, objPrototype, readonly) { - ObjectDefineProperties(obj, { + ObjectDefineProperties(objPrototype, { size: { __proto__: null, configurable: true, enumerable: true, get() { - assertBranded(this, objPrototype); - return obj[setlikeInner].size; + return this[setlikeInner].size; }, }, [SymbolIterator]: { @@ -1331,8 +1331,7 @@ function setlike(obj, objPrototype, readonly) { enumerable: false, writable: true, value() { - assertBranded(this, objPrototype); - return obj[setlikeInner][SymbolIterator](); + return this[setlikeInner][SymbolIterator](); }, }, entries: { @@ -1341,8 +1340,7 @@ function setlike(obj, objPrototype, readonly) { enumerable: true, writable: true, value() { - assertBranded(this, objPrototype); - return SetPrototypeEntries(obj[setlikeInner]); + return SetPrototypeEntries(this[setlikeInner]); }, }, keys: { @@ -1351,8 +1349,7 @@ function setlike(obj, objPrototype, readonly) { enumerable: true, writable: true, value() { - assertBranded(this, objPrototype); - return SetPrototypeKeys(obj[setlikeInner]); + return SetPrototypeKeys(this[setlikeInner]); }, }, values: { @@ -1361,8 +1358,7 @@ function setlike(obj, objPrototype, readonly) { enumerable: true, writable: true, value() { - assertBranded(this, objPrototype); - return SetPrototypeValues(obj[setlikeInner]); + return SetPrototypeValues(this[setlikeInner]); }, }, forEach: { @@ -1371,8 +1367,7 @@ function setlike(obj, objPrototype, readonly) { enumerable: true, writable: true, value(callbackfn, thisArg) { - assertBranded(this, objPrototype); - return SetPrototypeForEach(obj[setlikeInner], callbackfn, thisArg); + return SetPrototypeForEach(this[setlikeInner], callbackfn, thisArg); }, }, has: { @@ -1381,22 +1376,20 @@ function setlike(obj, objPrototype, readonly) { enumerable: true, writable: true, value(value) { - assertBranded(this, objPrototype); - return SetPrototypeHas(obj[setlikeInner], value); + return SetPrototypeHas(this[setlikeInner], value); }, }, }); if (!readonly) { - ObjectDefineProperties(obj, { + ObjectDefineProperties(objPrototype, { add: { __proto__: null, configurable: true, enumerable: true, writable: true, value(value) { - assertBranded(this, objPrototype); - return SetPrototypeAdd(obj[setlikeInner], value); + return SetPrototypeAdd(this[setlikeInner], value); }, }, delete: { @@ -1405,8 +1398,7 @@ function setlike(obj, objPrototype, readonly) { enumerable: true, writable: true, value(value) { - assertBranded(this, objPrototype); - return SetPrototypeDelete(obj[setlikeInner], value); + return SetPrototypeDelete(this[setlikeInner], value); }, }, clear: { @@ -1415,8 +1407,7 @@ function setlike(obj, objPrototype, readonly) { enumerable: true, writable: true, value() { - assertBranded(this, objPrototype); - return SetPrototypeClear(obj[setlikeInner]); + return SetPrototypeClear(this[setlikeInner]); }, }, }); diff --git a/runtime/js/98_global_scope_window.js b/runtime/js/98_global_scope_window.js index c42a940a82..a1d4361ffe 100644 --- a/runtime/js/98_global_scope_window.js +++ b/runtime/js/98_global_scope_window.js @@ -66,6 +66,7 @@ ObjectDefineProperties(Navigator.prototype, { get() { webidl.assertBranded(this, NavigatorPrototype); const webgpu = loadWebGPU(); + webgpu.initGPU(); return webgpu.gpu; }, }, diff --git a/runtime/js/98_global_scope_worker.js b/runtime/js/98_global_scope_worker.js index f10bb2830d..dc53dc8046 100644 --- a/runtime/js/98_global_scope_worker.js +++ b/runtime/js/98_global_scope_worker.js @@ -64,6 +64,7 @@ ObjectDefineProperties(WorkerNavigator.prototype, { get() { webidl.assertBranded(this, WorkerNavigatorPrototype); const webgpu = loadWebGPU(); + webgpu.initGPU(); return webgpu.gpu; }, },