summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock557
-rw-r--r--cli/build.rs6
-rw-r--r--cli/dts/lib.deno.window.d.ts7
-rw-r--r--cli/dts/lib.deno.worker.d.ts8
-rw-r--r--cli/flags.rs1
-rw-r--r--cli/main.rs5
-rw-r--r--cli/tests/unit/unit_tests.ts1
-rw-r--r--cli/tests/unit/webgpu_test.ts225
-rw-r--r--cli/tests/webgpu_computepass_shader.wgsl39
-rw-r--r--cli/tests/webgpu_hellotriangle.outbin0 -> 204800 bytes
-rw-r--r--cli/tests/webgpu_hellotriangle_shader.wgsl19
-rw-r--r--cli/tsc.rs1
-rw-r--r--op_crates/web/02_event.js1
-rw-r--r--op_crates/web/internal.d.ts4
-rw-r--r--op_crates/webgpu/01_webgpu.js5048
-rw-r--r--op_crates/webgpu/02_idl_types.js1800
-rw-r--r--op_crates/webgpu/Cargo.toml21
-rw-r--r--op_crates/webgpu/README.md35
-rw-r--r--op_crates/webgpu/binding.rs362
-rw-r--r--op_crates/webgpu/buffer.rs243
-rw-r--r--op_crates/webgpu/bundle.rs465
-rw-r--r--op_crates/webgpu/command_encoder.rs734
-rw-r--r--op_crates/webgpu/compute_pass.rs365
-rw-r--r--op_crates/webgpu/error.rs252
-rw-r--r--op_crates/webgpu/lib.deno_webgpu.d.ts1126
-rw-r--r--op_crates/webgpu/lib.rs541
-rw-r--r--op_crates/webgpu/pipeline.rs643
-rw-r--r--op_crates/webgpu/queue.rs157
-rw-r--r--op_crates/webgpu/render_pass.rs676
-rw-r--r--op_crates/webgpu/sampler.rs129
-rw-r--r--op_crates/webgpu/shader.rs77
-rw-r--r--op_crates/webgpu/texture.rs256
-rw-r--r--op_crates/webgpu/webgpu.idl1023
-rw-r--r--runtime/Cargo.toml2
-rw-r--r--runtime/build.rs1
-rw-r--r--runtime/errors.rs1
-rw-r--r--runtime/js/99_main.js47
-rw-r--r--runtime/lib.rs1
-rw-r--r--runtime/ops/mod.rs1
-rw-r--r--runtime/ops/webgpu.rs421
-rw-r--r--runtime/web_worker.rs1
-rw-r--r--runtime/worker.rs1
42 files changed, 15302 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f824c5079..b30c86e98 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -17,6 +17,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
[[package]]
+name = "ahash"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
+
+[[package]]
name = "aho-corasick"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -74,6 +80,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
+name = "ash"
+version = "0.31.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c69a8137596e84c22d57f3da1b5de1d4230b1742a710091c85f4d7ce50f00f38"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
name = "ast_node"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -181,6 +196,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
+name = "bit-set"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de"
+dependencies = [
+ "bit-vec",
+]
+
+[[package]]
+name = "bit-vec"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb"
+
+[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -199,6 +229,12 @@ dependencies = [
]
[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -257,6 +293,9 @@ name = "cc"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
+dependencies = [
+ "jobserver",
+]
[[package]]
name = "cfg-if"
@@ -271,6 +310,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "cfg_aliases"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
+
+[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -299,6 +344,55 @@ dependencies = [
]
[[package]]
+name = "cocoa-foundation"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
+dependencies = [
+ "bitflags",
+ "block",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+ "objc",
+]
+
+[[package]]
+name = "copyless"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536"
+
+[[package]]
+name = "core-foundation"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
+dependencies = [
+ "bitflags",
+ "core-foundation",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
name = "cpuid-bool"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -344,6 +438,17 @@ dependencies = [
]
[[package]]
+name = "d3d12"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a60cceb22c7c53035f8980524fdc7f17cf49681a3c154e6757d30afbec6ec4"
+dependencies = [
+ "bitflags",
+ "libloading",
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "darling"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -538,6 +643,7 @@ dependencies = [
"deno_crypto",
"deno_fetch",
"deno_web",
+ "deno_webgpu",
"deno_websocket",
"dlopen",
"encoding_rs",
@@ -580,6 +686,17 @@ dependencies = [
]
[[package]]
+name = "deno_webgpu"
+version = "0.1.0"
+dependencies = [
+ "deno_core",
+ "serde",
+ "tokio",
+ "wgpu-core",
+ "wgpu-types",
+]
+
+[[package]]
name = "deno_websocket"
version = "0.5.0"
dependencies = [
@@ -841,6 +958,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
+[[package]]
name = "form_urlencoded"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1060,6 +1192,205 @@ dependencies = [
]
[[package]]
+name = "gfx-auxil"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7b33ecf067f2117668d91c9b0f2e5f223ebd1ffec314caa2f3de27bb580186d"
+dependencies = [
+ "fxhash",
+ "gfx-hal",
+ "spirv_cross",
+]
+
+[[package]]
+name = "gfx-backend-dx11"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f851d03c2e8f117e3702bf41201a4fafa447d5cb1276d5375870ae7573d069dd"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "gfx-auxil",
+ "gfx-hal",
+ "libloading",
+ "log",
+ "parking_lot",
+ "range-alloc",
+ "raw-window-handle",
+ "smallvec",
+ "spirv_cross",
+ "thunderdome",
+ "winapi 0.3.9",
+ "wio",
+]
+
+[[package]]
+name = "gfx-backend-dx12"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36dc6ba2b7647e2c2b27b8f74ff5ccdd53c703776588eee5b1de515fdcbd6bc9"
+dependencies = [
+ "arrayvec",
+ "bit-set",
+ "bitflags",
+ "d3d12",
+ "gfx-auxil",
+ "gfx-hal",
+ "log",
+ "parking_lot",
+ "range-alloc",
+ "raw-window-handle",
+ "smallvec",
+ "spirv_cross",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "gfx-backend-empty"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f07ef26a65954cfdd7b4c587f485100d1bb3b0bd6a51b02d817d6c87cca7a91"
+dependencies = [
+ "gfx-hal",
+ "log",
+ "raw-window-handle",
+]
+
+[[package]]
+name = "gfx-backend-gl"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17fd85420547bceb851fadb90f196f168abfc252d57528bd2d749db0d18b75f"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "gfx-auxil",
+ "gfx-hal",
+ "glow",
+ "js-sys",
+ "khronos-egl",
+ "libloading",
+ "log",
+ "naga",
+ "parking_lot",
+ "raw-window-handle",
+ "spirv_cross",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gfx-backend-metal"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8dc54b456ece69ef49f8893269ebf24ac70969ed34ba2719c3f3abcc8fbff14e"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "copyless",
+ "foreign-types",
+ "gfx-auxil",
+ "gfx-hal",
+ "log",
+ "metal",
+ "naga",
+ "objc",
+ "parking_lot",
+ "range-alloc",
+ "raw-window-handle",
+ "spirv_cross",
+ "storage-map",
+]
+
+[[package]]
+name = "gfx-backend-vulkan"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dabe88b1a5c91e0f969b441cc57e70364858066e4ba937deeb62065654ef9bd9"
+dependencies = [
+ "arrayvec",
+ "ash",
+ "byteorder",
+ "core-graphics-types",
+ "gfx-hal",
+ "inplace_it",
+ "log",
+ "naga",
+ "objc",
+ "parking_lot",
+ "raw-window-handle",
+ "smallvec",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "gfx-hal"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1d9cc8d3b573dda62d0baca4f02e0209786e22c562caff001d77c389008781d"
+dependencies = [
+ "bitflags",
+ "naga",
+ "raw-window-handle",
+ "thiserror",
+]
+
+[[package]]
+name = "glow"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "072136d2c3783f3a92f131acb227bc806d3886278e2a4dc1e9990ec89ef9e70b"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "gpu-alloc"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e7724b9aef57ea36d70faf54e0ee6265f86e41de16bed8333efdeab5b00e16b"
+dependencies = [
+ "bitflags",
+ "gpu-alloc-types",
+ "tracing",
+]
+
+[[package]]
+name = "gpu-alloc-types"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54804d0d6bc9d7f26db4eaec1ad10def69b599315f487d32c334a80d1efe67a5"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "gpu-descriptor"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8a70f1e87a3840ed6a3e99e02c2b861e4dbdf26f0d07e38f42ea5aff46cfce2"
+dependencies = [
+ "bitflags",
+ "gpu-descriptor-types",
+ "hashbrown",
+ "tracing",
+]
+
+[[package]]
+name = "gpu-descriptor-types"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "363e3677e55ad168fef68cf9de3a4a310b53124c5e784c53a1d70e92d23f2126"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
name = "h2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1084,6 +1415,9 @@ name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+dependencies = [
+ "ahash",
+]
[[package]]
name = "heck"
@@ -1247,6 +1581,12 @@ dependencies = [
]
[[package]]
+name = "inplace_it"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90953f308a79fe6d62a4643e51f848fbfddcd05975a38e69fdf4ab86a7baf7ca"
+
+[[package]]
name = "input_buffer"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1302,6 +1642,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
+name = "jobserver"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "js-sys"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1327,6 +1676,16 @@ dependencies = [
]
[[package]]
+name = "khronos-egl"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8020ff3b84f9ac87461216ad0501bc09b33c1cbe17404d8ea405160fd164bab"
+dependencies = [
+ "libc",
+ "libloading",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1352,6 +1711,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c"
[[package]]
+name = "libloading"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
+dependencies = [
+ "cfg-if 1.0.0",
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "linked-hash-map"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1436,6 +1805,15 @@ dependencies = [
]
[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "match_cfg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1454,6 +1832,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
+name = "metal"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c"
+dependencies = [
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "foreign-types",
+ "log",
+ "objc",
+]
+
+[[package]]
name = "mime"
version = "0.3.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1493,6 +1885,23 @@ dependencies = [
]
[[package]]
+name = "naga"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8f30d7036f137a2f64fd7d53b70a91545d3f09e030b77b3816ff7bd4cf3f789"
+dependencies = [
+ "bit-set",
+ "bitflags",
+ "fxhash",
+ "log",
+ "num-traits",
+ "petgraph",
+ "serde",
+ "spirv_headers",
+ "thiserror",
+]
+
+[[package]]
name = "new_debug_unreachable"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1601,6 +2010,25 @@ dependencies = [
]
[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+ "objc_exception",
+]
+
+[[package]]
+name = "objc_exception"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "once_cell"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2013,6 +2441,21 @@ dependencies = [
]
[[package]]
+name = "range-alloc"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a441a7a6c80ad6473bd4b74ec1c9a4c951794285bf941c2126f607c72e48211"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "redox_syscall"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2129,6 +2572,17 @@ dependencies = [
]
[[package]]
+name = "ron"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "064ea8613fb712a19faf920022ec8ddf134984f100090764a4e1d768f3827f1f"
+dependencies = [
+ "base64 0.13.0",
+ "bitflags",
+ "serde",
+]
+
+[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2350,6 +2804,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
[[package]]
+name = "slotmap"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd"
+
+[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2389,6 +2849,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
+name = "spirv_cross"
+version = "0.23.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06db6bd7b6518f761593783e2896eefe55e90455efc5f44511078ce0426ed418"
+dependencies = [
+ "cc",
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "spirv_headers"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f5b132530b1ac069df335577e3581765995cba5a13995cdbbdbc8fb057c532c"
+dependencies = [
+ "bitflags",
+ "num-traits",
+]
+
+[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2401,6 +2882,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
+name = "storage-map"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "418bb14643aa55a7841d5303f72cf512cfb323b8cc221d51580500a1ca75206c"
+dependencies = [
+ "lock_api",
+]
+
+[[package]]
name = "string_cache"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2926,6 +3416,12 @@ dependencies = [
]
[[package]]
+name = "thunderdome"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7572415bd688d401c52f6e36f4c8e805b9ae1622619303b9fa835d531db0acae"
+
+[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3087,10 +3583,22 @@ checksum = "f7d40a22fd029e33300d8d89a5cc8ffce18bb7c587662f54629e94c9de5487f3"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
+ "tracing-attributes",
"tracing-core",
]
[[package]]
+name = "tracing-attributes"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43f080ea7e4107844ef4766459426fa2d5c1ada2e47edba05dc7fa99d9629f47"
+dependencies = [
+ "proc-macro2 1.0.24",
+ "quote 1.0.8",
+ "syn 1.0.60",
+]
+
+[[package]]
name = "tracing-core"
version = "0.1.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3469,6 +3977,46 @@ dependencies = [
]
[[package]]
+name = "wgpu-core"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c89fa2cc5d72236461ac09c5be967012663e29cb62f1a972654cbf35e49dffa8"
+dependencies = [
+ "arrayvec",
+ "bitflags",
+ "cfg_aliases",
+ "copyless",
+ "fxhash",
+ "gfx-backend-dx11",
+ "gfx-backend-dx12",
+ "gfx-backend-empty",
+ "gfx-backend-gl",
+ "gfx-backend-metal",
+ "gfx-backend-vulkan",
+ "gfx-hal",
+ "gpu-alloc",
+ "gpu-descriptor",
+ "naga",
+ "parking_lot",
+ "ron",
+ "serde",
+ "smallvec",
+ "thiserror",
+ "tracing",
+ "wgpu-types",
+]
+
+[[package]]
+name = "wgpu-types"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72fa9ba80626278fd87351555c363378d08122d7601e58319be3d6fa85a87747"
+dependencies = [
+ "bitflags",
+ "serde",
+]
+
+[[package]]
name = "which"
version = "4.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3555,6 +4103,15 @@ dependencies = [
]
[[package]]
+name = "wio"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
+dependencies = [
+ "winapi 0.3.9",
+]
+
+[[package]]
name = "wyz"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/cli/build.rs b/cli/build.rs
index a54bf962c..236097cbf 100644
--- a/cli/build.rs
+++ b/cli/build.rs
@@ -11,6 +11,7 @@ use deno_core::RuntimeOptions;
use deno_runtime::deno_crypto;
use deno_runtime::deno_fetch;
use deno_runtime::deno_web;
+use deno_runtime::deno_webgpu;
use deno_runtime::deno_websocket;
use regex::Regex;
use std::collections::HashMap;
@@ -62,6 +63,7 @@ fn create_compiler_snapshot(
let mut op_crate_libs = HashMap::new();
op_crate_libs.insert("deno.web", deno_web::get_declaration());
op_crate_libs.insert("deno.fetch", deno_fetch::get_declaration());
+ op_crate_libs.insert("deno.webgpu", deno_webgpu::get_declaration());
op_crate_libs.insert("deno.websocket", deno_websocket::get_declaration());
op_crate_libs.insert("deno.crypto", deno_crypto::get_declaration());
@@ -261,6 +263,10 @@ fn main() {
deno_fetch::get_declaration().display()
);
println!(
+ "cargo:rustc-env=DENO_WEBGPU_LIB_PATH={}",
+ deno_webgpu::get_declaration().display()
+ );
+ println!(
"cargo:rustc-env=DENO_WEBSOCKET_LIB_PATH={}",
deno_websocket::get_declaration().display()
);
diff --git a/cli/dts/lib.deno.window.d.ts b/cli/dts/lib.deno.window.d.ts
index 721c89af2..2d46e5fe0 100644
--- a/cli/dts/lib.deno.window.d.ts
+++ b/cli/dts/lib.deno.window.d.ts
@@ -3,6 +3,7 @@
/// <reference no-default-lib="true" />
/// <reference lib="deno.ns" />
/// <reference lib="deno.shared_globals" />
+/// <reference lib="deno.webgpu" />
/// <reference lib="esnext" />
declare class Window extends EventTarget {
@@ -17,12 +18,18 @@ declare class Window extends EventTarget {
confirm: (message?: string) => boolean;
prompt: (message?: string, defaultValue?: string) => string | null;
Deno: typeof Deno;
+ navigator: Navigator;
}
declare var window: Window & typeof globalThis;
declare var self: Window & typeof globalThis;
declare var onload: ((this: Window, ev: Event) => any) | null;
declare var onunload: ((this: Window, ev: Event) => any) | null;
+declare var navigator: Navigator;
+
+declare interface Navigator {
+ readonly gpu: GPU;
+}
/**
* Shows the given message and waits for the enter key pressed.
diff --git a/cli/dts/lib.deno.worker.d.ts b/cli/dts/lib.deno.worker.d.ts
index a8f42f336..93549ecd5 100644
--- a/cli/dts/lib.deno.worker.d.ts
+++ b/cli/dts/lib.deno.worker.d.ts
@@ -3,6 +3,7 @@
/// <reference no-default-lib="true" />
/// <reference lib="deno.ns" />
/// <reference lib="deno.shared_globals" />
+/// <reference lib="deno.webgpu" />
/// <reference lib="esnext" />
declare class WorkerGlobalScope {
@@ -29,6 +30,13 @@ declare class WorkerGlobalScope {
close: () => void;
postMessage: (message: any) => void;
Deno: typeof Deno;
+ navigator: WorkerNavigator;
+}
+
+declare var navigator: WorkerNavigator;
+
+declare interface WorkerNavigator {
+ readonly gpu: GPU;
}
declare class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
diff --git a/cli/flags.rs b/cli/flags.rs
index a803ff63f..6add2df87 100644
--- a/cli/flags.rs
+++ b/cli/flags.rs
@@ -234,6 +234,7 @@ static ENV_VARIABLES_HELP: &str = r#"ENVIRONMENT VARIABLES:
DENO_DIR Set the cache directory
DENO_INSTALL_ROOT Set deno install's output directory
(defaults to $HOME/.deno/bin)
+ DENO_WEBGPU_TRACE Directory to use for wgpu traces
HTTP_PROXY Proxy address for HTTP requests
(module downloads, fetch)
HTTPS_PROXY Proxy address for HTTPS requests
diff --git a/cli/main.rs b/cli/main.rs
index 7871fe882..0fa63a138 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -278,10 +278,11 @@ fn print_cache_info(
pub fn get_types(unstable: bool) -> String {
let mut types = format!(
- "{}\n{}\n{}\n{}\n{}\n{}",
+ "{}\n{}\n{}\n{}\n{}\n{}\n{}",
crate::tsc::DENO_NS_LIB,
crate::tsc::DENO_WEB_LIB,
crate::tsc::DENO_FETCH_LIB,
+ crate::tsc::DENO_WEBGPU_LIB,
crate::tsc::DENO_WEBSOCKET_LIB,
crate::tsc::SHARED_GLOBALS_LIB,
crate::tsc::WINDOW_LIB,
@@ -1022,6 +1023,8 @@ fn init_logger(maybe_level: Option<Level>) {
)
// https://github.com/denoland/deno/issues/6641
.filter_module("rustyline", LevelFilter::Off)
+ // wgpu backend crates (gfx_backend), have a lot of useless INFO and WARN logs
+ .filter_module("gfx", LevelFilter::Error)
.format(|buf, record| {
let mut target = record.target().to_string();
if let Some(line_no) = record.line() {
diff --git a/cli/tests/unit/unit_tests.ts b/cli/tests/unit/unit_tests.ts
index 8af28c1bb..2664a9ab0 100644
--- a/cli/tests/unit/unit_tests.ts
+++ b/cli/tests/unit/unit_tests.ts
@@ -76,3 +76,4 @@ import "./write_text_file_test.ts";
import "./performance_test.ts";
import "./version_test.ts";
import "./websocket_test.ts";
+import "./webgpu_test.ts";
diff --git a/cli/tests/unit/webgpu_test.ts b/cli/tests/unit/webgpu_test.ts
new file mode 100644
index 000000000..628a3c8db
--- /dev/null
+++ b/cli/tests/unit/webgpu_test.ts
@@ -0,0 +1,225 @@
+// TODO(lucacasonato): remove when GPUBufferUsage and friends are added to dlint
+// deno-lint-ignore-file no-undef
+
+import { assert, assertEquals, unitTest } from "./test_util.ts";
+
+let isCI: boolean;
+try {
+ isCI = (Deno.env.get("CI")?.length ?? 0) > 0;
+} catch {
+ isCI = true;
+}
+
+// Skip this test on linux CI, because the vulkan emulator is not good enough
+// yet, and skip on macOS because these do not have virtual GPUs.
+unitTest({
+ perms: { read: true, env: true },
+ ignore: (Deno.build.os === "linux" || Deno.build.os === "darwin") && isCI,
+}, async function webgpuComputePass() {
+ const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter);
+
+ const numbers = [1, 4, 3, 295];
+
+ const device = await adapter.requestDevice();
+ assert(device);
+
+ const shaderCode = await Deno.readTextFile(
+ "cli/tests/webgpu_computepass_shader.wgsl",
+ );
+
+ const shaderModule = device.createShaderModule({
+ code: shaderCode,
+ });
+
+ const size = new Uint32Array(numbers).byteLength;
+
+ const stagingBuffer = device.createBuffer({
+ size: size,
+ usage: 1 | 8,
+ });
+
+ const storageBuffer = device.createBuffer({
+ label: "Storage Buffer",
+ size: size,
+ usage: 0x80 | 8 | 4,
+ mappedAtCreation: true,
+ });
+
+ const buf = new Uint32Array(storageBuffer.getMappedRange());
+
+ buf.set(numbers);
+
+ storageBuffer.unmap();
+
+ const bindGroupLayout = device.createBindGroupLayout({
+ entries: [
+ {
+ binding: 0,
+ visibility: 4,
+ buffer: {
+ type: "storage",
+ minBindingSize: 4,
+ },
+ },
+ ],
+ });
+
+ const bindGroup = device.createBindGroup({
+ layout: bindGroupLayout,
+ entries: [
+ {
+ binding: 0,
+ resource: {
+ buffer: storageBuffer,
+ },
+ },
+ ],
+ });
+
+ const pipelineLayout = device.createPipelineLayout({
+ bindGroupLayouts: [bindGroupLayout],
+ });
+
+ const computePipeline = device.createComputePipeline({
+ layout: pipelineLayout,
+ compute: {
+ module: shaderModule,
+ entryPoint: "main",
+ },
+ });
+
+ const encoder = device.createCommandEncoder();
+
+ const computePass = encoder.beginComputePass();
+ computePass.setPipeline(computePipeline);
+ computePass.setBindGroup(0, bindGroup);
+ computePass.insertDebugMarker("compute collatz iterations");
+ computePass.dispatch(numbers.length);
+ computePass.endPass();
+
+ encoder.copyBufferToBuffer(storageBuffer, 0, stagingBuffer, 0, size);
+
+ device.queue.submit([encoder.finish()]);
+
+ await stagingBuffer.mapAsync(1);
+
+ const data = stagingBuffer.getMappedRange();
+
+ assertEquals(new Uint32Array(data), new Uint32Array([0, 2, 7, 55]));
+
+ stagingBuffer.unmap();
+
+ device.destroy();
+
+ // TODO(lucacasonato): webgpu spec should add a explicit destroy method for
+ // adapters.
+ const resources = Object.keys(Deno.resources());
+ Deno.close(Number(resources[resources.length - 1]));
+});
+
+// Skip this test on linux CI, because the vulkan emulator is not good enough
+// yet, and skip on macOS because these do not have virtual GPUs.
+unitTest({
+ perms: { read: true, env: true },
+ ignore: (Deno.build.os === "linux" || Deno.build.os === "darwin") && isCI,
+}, async function webgpuHelloTriangle() {
+ const adapter = await navigator.gpu.requestAdapter();
+ assert(adapter);
+
+ const device = await adapter.requestDevice();
+ assert(device);
+
+ const shaderCode = await Deno.readTextFile(
+ "cli/tests/webgpu_hellotriangle_shader.wgsl",
+ );
+
+ const shaderModule = device.createShaderModule({
+ code: shaderCode,
+ });
+
+ const pipelineLayout = device.createPipelineLayout({
+ bindGroupLayouts: [],
+ });
+
+ const renderPipeline = device.createRenderPipeline({
+ layout: pipelineLayout,
+ vertex: {
+ module: shaderModule,
+ entryPoint: "vs_main",
+ },
+ fragment: {
+ module: shaderModule,
+ entryPoint: "fs_main",
+ targets: [
+ {
+ format: "rgba8unorm-srgb",
+ },
+ ],
+ },
+ });
+
+ const dimensions = {
+ width: 200,
+ height: 200,
+ };
+ const unpaddedBytesPerRow = dimensions.width * 4;
+ const align = 256;
+ const paddedBytesPerRowPadding = (align - unpaddedBytesPerRow % align) %
+ align;
+ const paddedBytesPerRow = unpaddedBytesPerRow + paddedBytesPerRowPadding;
+
+ const outputBuffer = device.createBuffer({
+ label: "Capture",
+ size: paddedBytesPerRow * dimensions.height,
+ usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
+ });
+ const texture = device.createTexture({
+ label: "Capture",
+ size: dimensions,
+ format: "rgba8unorm-srgb",
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.COPY_SRC,
+ });
+
+ const encoder = device.createCommandEncoder();
+ const renderPass = encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view: texture.createView(),
+ storeOp: "store",
+ loadValue: [0, 1, 0, 1],
+ },
+ ],
+ });
+ renderPass.setPipeline(renderPipeline);
+ renderPass.draw(3, 1);
+ renderPass.endPass();
+
+ encoder.copyTextureToBuffer(
+ {
+ texture,
+ },
+ {
+ buffer: outputBuffer,
+ bytesPerRow: paddedBytesPerRow,
+ rowsPerImage: 0,
+ },
+ dimensions,
+ );
+
+ device.queue.submit([encoder.finish()]);
+
+ await outputBuffer.mapAsync(1);
+ const data = new Uint8Array(outputBuffer.getMappedRange());
+
+ assertEquals(data, await Deno.readFile("cli/tests/webgpu_hellotriangle.out"));
+
+ outputBuffer.unmap();
+
+ device.destroy();
+
+ // TODO(lucacasonato): webgpu spec should add a explicit destroy method for
+ // adapters.
+ const resources = Object.keys(Deno.resources());
+ Deno.close(Number(resources[resources.length - 1]));
+});
diff --git a/cli/tests/webgpu_computepass_shader.wgsl b/cli/tests/webgpu_computepass_shader.wgsl
new file mode 100644
index 000000000..2433f3243
--- /dev/null
+++ b/cli/tests/webgpu_computepass_shader.wgsl
@@ -0,0 +1,39 @@
+[[builtin(global_invocation_id)]]
+var global_id: vec3<u32>;
+
+[[block]]
+struct PrimeIndices {
+ data: [[stride(4)]] array<u32>;
+}; // this is used as both input and output for convenience
+
+[[group(0), binding(0)]]
+var<storage> v_indices: [[access(read_write)]] PrimeIndices;
+
+// The Collatz Conjecture states that for any integer n:
+// If n is even, n = n/2
+// If n is odd, n = 3n+1
+// And repeat this process for each new n, you will always eventually reach 1.
+// Though the conjecture has not been proven, no counterexample has ever been found.
+// This function returns how many times this recurrence needs to be applied to reach 1.
+fn collatz_iterations(n_base: u32) -> u32{
+ var n: u32 = n_base;
+ var i: u32 = 0u;
+ loop {
+ if (n <= 1u) {
+ break;
+ }
+ if (n % 2u == 0u) {
+ n = n / 2u;
+ }
+ else {
+ n = 3u * n + 1u;
+ }
+ i = i + 1u;
+ }
+ return i;
+}
+
+[[stage(compute), workgroup_size(1)]]
+fn main() {
+ v_indices.data[global_id.x] = collatz_iterations(v_indices.data[global_id.x]);
+}
diff --git a/cli/tests/webgpu_hellotriangle.out b/cli/tests/webgpu_hellotriangle.out
new file mode 100644
index 000000000..91454dbfc
--- /dev/null
+++ b/cli/tests/webgpu_hellotriangle.out
Binary files differ
diff --git a/cli/tests/webgpu_hellotriangle_shader.wgsl b/cli/tests/webgpu_hellotriangle_shader.wgsl
new file mode 100644
index 000000000..71934415b
--- /dev/null
+++ b/cli/tests/webgpu_hellotriangle_shader.wgsl
@@ -0,0 +1,19 @@
+[[builtin(vertex_index)]]
+var<in> in_vertex_index: u32;
+[[builtin(position)]]
+var<out> out_pos: vec4<f32>;
+
+[[stage(vertex)]]
+fn vs_main() {
+ var x: f32 = f32(i32(in_vertex_index) - 1);
+ var y: f32 = f32(i32(in_vertex_index & 1) * 2 - 1);
+ out_pos = vec4<f32>(x, y, 0.0, 1.0);
+}
+
+[[location(0)]]
+var<out> out_color: vec4<f32>;
+
+[[stage(fragment)]]
+fn fs_main() {
+ out_color = vec4<f32>(1.0, 0.0, 0.0, 1.0);
+}
diff --git a/cli/tsc.rs b/cli/tsc.rs
index 6328175b9..8d28b959d 100644
--- a/cli/tsc.rs
+++ b/cli/tsc.rs
@@ -31,6 +31,7 @@ use std::sync::Mutex;
pub static DENO_NS_LIB: &str = include_str!("dts/lib.deno.ns.d.ts");
pub static DENO_WEB_LIB: &str = include_str!(env!("DENO_WEB_LIB_PATH"));
pub static DENO_FETCH_LIB: &str = include_str!(env!("DENO_FETCH_LIB_PATH"));
+pub static DENO_WEBGPU_LIB: &str = include_str!(env!("DENO_WEBGPU_LIB_PATH"));
pub static DENO_WEBSOCKET_LIB: &str =
include_str!(env!("DENO_WEBSOCKET_LIB_PATH"));
pub static SHARED_GLOBALS_LIB: &str =
diff --git a/op_crates/web/02_event.js b/op_crates/web/02_event.js
index d253599e5..ac813d091 100644
--- a/op_crates/web/02_event.js
+++ b/op_crates/web/02_event.js
@@ -1204,6 +1204,7 @@
window.removeEventListener = EventTarget.prototype.removeEventListener;
window.__bootstrap = (window.__bootstrap || {});
window.__bootstrap.eventTarget = {
+ EventTarget,
setEventTargetData,
};
window.__bootstrap.event = {
diff --git a/op_crates/web/internal.d.ts b/op_crates/web/internal.d.ts
index d43332e4b..8be80c8a5 100644
--- a/op_crates/web/internal.d.ts
+++ b/op_crates/web/internal.d.ts
@@ -270,6 +270,10 @@ declare namespace globalThis {
): (v: any, opts: ValueConverterOpts) => any;
}
+ declare var eventTarget: {
+ EventTarget: typeof EventTarget;
+ };
+
declare var url: {
URLSearchParams: typeof URLSearchParams;
};
diff --git a/op_crates/webgpu/01_webgpu.js b/op_crates/webgpu/01_webgpu.js
new file mode 100644
index 000000000..7a078619e
--- /dev/null
+++ b/op_crates/webgpu/01_webgpu.js
@@ -0,0 +1,5048 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+/// <reference path="../../core/lib.deno_core.d.ts" />
+/// <reference path="../web/internal.d.ts" />
+/// <reference path="../web/lib.deno_web.d.ts" />
+/// <reference path="./lib.deno_webgpu.d.ts" />
+
+"use strict";
+
+((window) => {
+ const core = window.Deno.core;
+ const webidl = window.__bootstrap.webidl;
+ const eventTarget = window.__bootstrap.eventTarget;
+
+ /**
+ * @param {any} self
+ * @param {{prefix: string, context: string}} opts
+ * @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,
+ context: resourceContext,
+ });
+ if (resourceDevice.rid !== self.rid) {
+ throw new DOMException(
+ `${prefix}: ${resourceContext} belongs to a diffent device than ${selfContext}.`,
+ "OperationError",
+ );
+ }
+ return { ...resourceDevice, rid: resourceDevice.rid };
+ }
+
+ /**
+ * @param {any} self
+ * @param {{prefix: string, context: string}} opts
+ * @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 (Array.isArray(data)) {
+ return {
+ width: data[0],
+ height: data[1],
+ depth: data[2],
+ };
+ } else {
+ return data;
+ }
+ }
+
+ /**
+ * @param {number[] | GPUOrigin3DDict} data
+ * @returns {GPUOrigin3DDict}
+ */
+ function normalizeGPUOrigin3D(data) {
+ if (Array.isArray(data)) {
+ return {
+ x: data[0],
+ y: data[1],
+ z: data[2],
+ };
+ } else {
+ return data;
+ }
+ }
+
+ /**
+ * @param {number[] | GPUColor} data
+ * @returns {GPUColor}
+ */
+ function normalizeGPUColor(data) {
+ if (Array.isArray(data)) {
+ return {
+ r: data[0],
+ g: data[1],
+ b: data[2],
+ a: data[3],
+ };
+ } else {
+ return data;
+ }
+ }
+
+ class GPUOutOfMemoryError extends Error {
+ constructor() {
+ super();
+ }
+ }
+
+ class GPUValidationError extends Error {
+ /** @param {string} message */
+ constructor(message) {
+ const prefix = "Failed to construct 'GPUValidationError'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ message = webidl.converters.DOMString(message, {
+ prefix,
+ context: "Argument 1",
+ });
+ super(message);
+ }
+ }
+
+ class GPU {
+ [webidl.brand] = webidl.brand;
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPURequestAdapterOptions} options
+ */
+ async requestAdapter(options = {}) {
+ webidl.assertBranded(this, GPU);
+ options = webidl.converters.GPURequestAdapterOptions(options, {
+ prefix: "Failed to execute 'requestAdapter' on 'GPU'",
+ context: "Argument 1",
+ });
+
+ const { err, ...data } = await core.jsonOpAsync(
+ "op_webgpu_request_adapter",
+ { ...options },
+ );
+
+ if (err) {
+ return null;
+ } else {
+ return createGPUAdapter(data.name, data);
+ }
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${inspect({})}`;
+ }
+ }
+
+ const _name = Symbol("[[name]]");
+ const _adapter = Symbol("[[adapter]]");
+ const _cleanup = Symbol("[[cleanup]]");
+
+ /**
+ * @typedef InnerGPUAdapter
+ * @property {number} rid
+ * @property {GPUAdapterFeatures} features
+ * @property {GPUAdapterLimits} limits
+ */
+
+ /**
+ * @param {string} name
+ * @param {InnerGPUAdapter} inner
+ * @returns {GPUAdapter}
+ */
+ function createGPUAdapter(name, inner) {
+ /** @type {GPUAdapter} */
+ const adapter = webidl.createBranded(GPUAdapter);
+ adapter[_name] = name;
+ adapter[_adapter] = {
+ ...inner,
+ features: createGPUAdapterFeatures(inner.features),
+ limits: createGPUAdapterLimits(inner.limits),
+ };
+ return adapter;
+ }
+
+ class GPUAdapter {
+ /** @type {string} */
+ [_name];
+ /** @type {InnerGPUAdapter} */
+ [_adapter];
+
+ /** @returns {string} */
+ get name() {
+ webidl.assertBranded(this, GPUAdapter);
+ return this[_name];
+ }
+ /** @returns {GPUAdapterFeatures} */
+ get features() {
+ webidl.assertBranded(this, GPUAdapter);
+ return this[_adapter].features;
+ }
+ /** @returns {GPUAdapterLimits} */
+ get limits() {
+ webidl.assertBranded(this, GPUAdapter);
+ return this[_adapter].limits;
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPUDeviceDescriptor} descriptor
+ * @returns {Promise<GPUDevice>}
+ */
+ async requestDevice(descriptor = {}) {
+ webidl.assertBranded(this, GPUAdapter);
+ const prefix = "Failed to execute 'requestDevice' on 'GPUAdapter'";
+ descriptor = webidl.converters.GPUDeviceDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const nonGuaranteedFeatures = descriptor.nonGuaranteedFeatures ?? [];
+ for (const feature of nonGuaranteedFeatures) {
+ if (!this[_adapter].features.has(feature)) {
+ throw new TypeError(
+ `${prefix}: nonGuaranteedFeatures must be a subset of the adapter features.`,
+ );
+ }
+ }
+ const nonGuaranteedLimits = descriptor.nonGuaranteedLimits ?? [];
+ // TODO(lucacasonato): validate nonGuaranteedLimits
+
+ const { rid, features, limits } = await core.jsonOpAsync(
+ "op_webgpu_request_device",
+ {
+ adapterRid: this[_adapter].rid,
+ labe: descriptor.label,
+ nonGuaranteedFeatures,
+ nonGuaranteedLimits,
+ },
+ );
+
+ const inner = new InnerGPUDevice({
+ rid,
+ adapter: this,
+ features: Object.freeze(features),
+ limits: Object.freeze(limits),
+ });
+ return createGPUDevice(
+ descriptor.label ?? null,
+ inner,
+ createGPUQueue(descriptor.label ?? null, inner),
+ );
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ name: this.name,
+ features: this.features,
+ limits: this.limits,
+ })
+ }`;
+ }
+ }
+
+ const _limits = Symbol("[[limits]]");
+
+ function createGPUAdapterLimits(features) {
+ /** @type {GPUAdapterLimits} */
+ const adapterFeatures = webidl.createBranded(GPUAdapterLimits);
+ adapterFeatures[_limits] = features;
+ return adapterFeatures;
+ }
+
+ /**
+ * @typedef InnerAdapterLimits
+ * @property {number} maxTextureDimension1D
+ * @property {number} maxTextureDimension2D
+ * @property {number} maxTextureDimension3D
+ * @property {number} maxTextureArrayLayers
+ * @property {number} maxBindGroups
+ * @property {number} maxDynamicUniformBuffersPerPipelineLayout
+ * @property {number} maxDynamicStorageBuffersPerPipelineLayout
+ * @property {number} maxSampledTexturesPerShaderStage
+ * @property {number} maxSamplersPerShaderStage
+ * @property {number} maxStorageBuffersPerShaderStage
+ * @property {number} maxStorageTexturesPerShaderStage
+ * @property {number} maxUniformBuffersPerShaderStage
+ * @property {number} maxUniformBufferBindingSize
+ * @property {number} maxStorageBufferBindingSize
+ * @property {number} maxVertexBuffers
+ * @property {number} maxVertexAttributes
+ * @property {number} maxVertexBufferArrayStride
+ */
+
+ class GPUAdapterLimits {
+ /** @type {InnerAdapterLimits} */
+ [_limits];
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ get maxTextureDimension1D() {
+ throw new TypeError("Not yet implemented");
+ }
+ get maxTextureDimension2D() {
+ throw new TypeError("Not yet implemented");
+ }
+ get maxTextureDimension3D() {
+ throw new TypeError("Not yet implemented");
+ }
+ get maxTextureArrayLayers() {
+ throw new TypeError("Not yet implemented");
+ }
+ get maxBindGroups() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxBindGroups;
+ }
+ get maxDynamicUniformBuffersPerPipelineLayout() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxDynamicUniformBuffersPerPipelineLayout;
+ }
+ get maxDynamicStorageBuffersPerPipelineLayout() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxDynamicStorageBuffersPerPipelineLayout;
+ }
+ get maxSampledTexturesPerShaderStage() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxSampledTexturesPerShaderStage;
+ }
+ get maxSamplersPerShaderStage() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxSamplersPerShaderStage;
+ }
+ get maxStorageBuffersPerShaderStage() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxStorageBuffersPerShaderStage;
+ }
+ get maxStorageTexturesPerShaderStage() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxStorageTexturesPerShaderStage;
+ }
+ get maxUniformBuffersPerShaderStage() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxUniformBuffersPerShaderStage;
+ }
+ get maxUniformBufferBindingSize() {
+ webidl.assertBranded(this, GPUAdapterLimits);
+ return this[_limits].maxUniformBufferBindingSize;
+ }
+ get maxStorageBufferBindingSize() {
+ throw new TypeError("Not yet implemented");
+ }
+ get maxVertexBuffers() {
+ throw new TypeError("Not yet implemented");
+ }
+ get maxVertexAttributes() {
+ throw new TypeError("Not yet implemented");
+ }
+ get maxVertexBufferArrayStride() {
+ throw new TypeError("Not yet implemented");
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${inspect(this[_limits])}`;
+ }
+ }
+
+ const _features = Symbol("[[features]]");
+
+ function createGPUAdapterFeatures(features) {
+ /** @type {GPUAdapterFeatures} */
+ const adapterFeatures = webidl.createBranded(GPUAdapterFeatures);
+ adapterFeatures[_features] = new Set(features);
+ return adapterFeatures;
+ }
+
+ class GPUAdapterFeatures {
+ /** @type {Set<string>} */
+ [_features];
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /** @return {IterableIterator<[string, string]>} */
+ entries() {
+ webidl.assertBranded(this, GPUAdapterFeatures);
+ return this[_features].entries();
+ }
+
+ /** @return {void} */
+ forEach(callbackfn, thisArg) {
+ webidl.assertBranded(this, GPUAdapterFeatures);
+ this[_features].forEach(callbackfn, thisArg);
+ }
+
+ /** @return {boolean} */
+ has(value) {
+ webidl.assertBranded(this, GPUAdapterFeatures);
+ return this[_features].has(value);
+ }
+
+ /** @return {IterableIterator<string>} */
+ keys() {
+ webidl.assertBranded(this, GPUAdapterFeatures);
+ return this[_features].keys();
+ }
+
+ /** @return {IterableIterator<string>} */
+ values() {
+ webidl.assertBranded(this, GPUAdapterFeatures);
+ return this[_features].values();
+ }
+
+ /** @return {number} */
+ get size() {
+ webidl.assertBranded(this, GPUAdapterFeatures);
+ return this[_features].size;
+ }
+
+ [Symbol.iterator]() {
+ webidl.assertBranded(this, GPUAdapterFeatures);
+ return this[_features][Symbol.iterator]();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${inspect([...this.values()])}`;
+ }
+ }
+
+ const _reason = Symbol("[[reason]]");
+ const _message = Symbol("[[message]]");
+
+ /**
+ *
+ * @param {string | undefined} reason
+ * @param {string} message
+ * @returns {GPUDeviceLostInfo}
+ */
+ function createGPUDeviceLostInfo(reason, message) {
+ /** @type {GPUDeviceLostInfo} */
+ const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo);
+ deviceLostInfo[_reason] = reason;
+ deviceLostInfo[_message] = message;
+ return deviceLostInfo;
+ }
+
+ class GPUDeviceLostInfo {
+ /** @type {string | undefined} */
+ [_reason];
+ /** @type {string} */
+ [_message];
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ get reason() {
+ webidl.assertBranded(this, GPUDeviceLostInfo);
+ return this[_reason];
+ }
+ get message() {
+ webidl.assertBranded(this, GPUDeviceLostInfo);
+ return this[_message];
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({ reason: this[_reason], message: this[_message] })
+ }`;
+ }
+ }
+
+ const _label = Symbol("[[label]]");
+
+ /**
+ * @param {string} name
+ * @param {any} type
+ */
+ function GPUObjectBaseMixin(name, type) {
+ type.prototype[_label] = null;
+ Object.defineProperty(type.prototype, "label", {
+ /**
+ * @return {string | null}
+ */
+ get() {
+ webidl.assertBranded(this, type);
+ return this[_label];
+ },
+ /**
+ * @param {string | null} label
+ */
+ set(label) {
+ webidl.assertBranded(this, type);
+ label = webidl.converters["UVString?"](label, {
+ prefix: `Failed to set 'label' on '${name}'`,
+ context: "Argument 1",
+ });
+ this[_label] = label;
+ },
+ });
+ }
+
+ const _device = Symbol("[[device]]");
+ const _queue = Symbol("[[queue]]");
+
+ /**
+ * @typedef ErrorScope
+ * @property {string} filter
+ * @property {GPUError | undefined} error
+ */
+
+ /**
+ * @typedef InnerGPUDeviceOptions
+ * @property {GPUAdapter} adapter
+ * @property {number | undefined} rid
+ * @property {GPUFeatureName[]} features
+ * @property {object} limits
+ */
+
+ class InnerGPUDevice {
+ /** @type {GPUAdapter} */
+ adapter;
+ /** @type {number | undefined} */
+ rid;
+ /** @type {GPUFeatureName[]} */
+ features;
+ /** @type {object} */
+ limits;
+ /** @type {WeakRef<any>[]} */
+ resources;
+ /** @type {boolean} */
+ isLost;
+ /** @type {Promise<GPUDeviceLostInfo>} */
+ lost;
+ /** @type {(info: GPUDeviceLostInfo) => void} */
+ resolveLost;
+ /** @type {ErrorScope[]} */
+ errorScopeStack;
+
+ /**
+ * @param {InnerGPUDeviceOptions} options
+ */
+ constructor(options) {
+ this.adapter = options.adapter;
+ this.rid = options.rid;
+ this.features = options.features;
+ this.limits = options.limits;
+ this.resources = [];
+ this.isLost = false;
+ this.resolveLost = () => {};
+ this.lost = new Promise((resolve) => {
+ this.resolveLost = resolve;
+ });
+ this.errorScopeStack = [];
+ }
+
+ /** @param {any} resource */
+ trackResource(resource) {
+ this.resources.push(new WeakRef(resource));
+ }
+
+ /** @param {{ type: string, value: string | null } | undefined} err */
+ pushError(err) {
+ if (err) {
+ switch (err.type) {
+ case "lost":
+ this.isLost = true;
+ this.resolveLost(
+ createGPUDeviceLostInfo(undefined, "device was lost"),
+ );
+ break;
+ case "validation":
+ case "out-of-memory":
+ for (
+ let i = this.errorScopeStack.length - 1;
+ i >= 0;
+ i--
+ ) {
+ const scope = this.errorScopeStack[i];
+ if (scope.filter == err.type) {
+ if (!scope.error) {
+ switch (err.type) {
+ case "validation":
+ scope.error = new GPUValidationError(
+ err.value ?? "validation error",
+ );
+ break;
+ case "out-of-memory":
+ scope.error = new GPUOutOfMemoryError();
+ break;
+ }
+ }
+ return;
+ }
+ }
+ // TODO(lucacasonato): emit a UncapturedErrorEvent
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} inner
+ * @param {GPUQueue} queue
+ * @returns {GPUDevice}
+ */
+ function createGPUDevice(label, inner, queue) {
+ /** @type {GPUDevice} */
+ const device = webidl.createBranded(GPUDevice);
+ device[_label] = label;
+ device[_device] = inner;
+ device[_queue] = queue;
+ return device;
+ }
+
+ // TODO(@crowlKats): https://gpuweb.github.io/gpuweb/#errors-and-debugging
+ class GPUDevice extends eventTarget.EventTarget {
+ /** @type {InnerGPUDevice} */
+ [_device];
+
+ /** @type {GPUQueue} */
+ [_queue];
+
+ [_cleanup]() {
+ const device = this[_device];
+ const resources = device.resources;
+ while (resources.length > 0) {
+ const resource = resources.pop()?.deref();
+ if (resource) {
+ resource[_cleanup]();
+ }
+ }
+ const rid = device.rid;
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ device.rid = undefined;
+ }
+ }
+
+ get adapter() {
+ webidl.assertBranded(this, GPUDevice);
+ return this[_device].adapter;
+ }
+ get features() {
+ webidl.assertBranded(this, GPUDevice);
+ return this[_device].features;
+ }
+ get limits() {
+ webidl.assertBranded(this, GPUDevice);
+ return this[_device].limits;
+ }
+ get queue() {
+ webidl.assertBranded(this, GPUDevice);
+ return this[_queue];
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ super();
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUDevice);
+ this[_cleanup]();
+ }
+
+ /**
+ * @param {GPUBufferDescriptor} descriptor
+ * @returns {GPUBuffer}
+ */
+ createBuffer(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createBuffer' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUBufferDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_buffer", {
+ deviceRid: device.rid,
+ ...descriptor,
+ });
+ 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 ?? null,
+ device,
+ rid,
+ descriptor.size,
+ descriptor.usage,
+ options,
+ );
+ device.trackResource((buffer));
+ return buffer;
+ }
+
+ /**
+ * @param {GPUTextureDescriptor} descriptor
+ * @returns {GPUTexture}
+ */
+ createTexture(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createTexture' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUTextureDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_texture", {
+ deviceRid: device.rid,
+ ...descriptor,
+ size: normalizeGPUExtent3D(descriptor.size),
+ });
+ device.pushError(err);
+
+ const texture = createGPUTexture(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((texture));
+ return texture;
+ }
+
+ /**
+ * @param {GPUSamplerDescriptor} descriptor
+ * @returns {GPUSampler}
+ */
+ createSampler(descriptor = {}) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createSampler' on 'GPUDevice'";
+ descriptor = webidl.converters.GPUSamplerDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_sampler", {
+ deviceRid: device.rid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const sampler = createGPUSampler(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((sampler));
+ return sampler;
+ }
+
+ /**
+ * @param {GPUBindGroupLayoutDescriptor} descriptor
+ * @returns {GPUBindGroupLayout}
+ */
+ createBindGroupLayout(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createBindGroupLayout' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUBindGroupLayoutDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ for (const entry of descriptor.entries) {
+ let i = 0;
+ if (entry.buffer) i++;
+ if (entry.sampler) i++;
+ if (entry.texture) i++;
+ if (entry.storageTexture) i++;
+
+ if (i !== 1) {
+ throw new Error(); // TODO(@crowlKats): correct error
+ }
+ }
+
+ const { rid, err } = core.jsonOpSync(
+ "op_webgpu_create_bind_group_layout",
+ {
+ deviceRid: device.rid,
+ ...descriptor,
+ },
+ );
+ device.pushError(err);
+
+ const bindGroupLayout = createGPUBindGroupLayout(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((bindGroupLayout));
+ return bindGroupLayout;
+ }
+
+ /**
+ * @param {GPUPipelineLayoutDescriptor} descriptor
+ * @returns {GPUPipelineLayout}
+ */
+ createPipelineLayout(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createPipelineLayout' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUPipelineLayoutDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const bindGroupLayouts = descriptor.bindGroupLayouts.map(
+ (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 } = core.jsonOpSync("op_webgpu_create_pipeline_layout", {
+ deviceRid: device.rid,
+ label: descriptor.label,
+ bindGroupLayouts,
+ });
+ device.pushError(err);
+
+ const pipelineLayout = createGPUPipelineLayout(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((pipelineLayout));
+ return pipelineLayout;
+ }
+
+ /**
+ * @param {GPUBindGroupDescriptor} descriptor
+ * @returns {GPUBindGroup}
+ */
+ createBindGroup(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createBindGroup' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUBindGroupDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const layout = assertResource(descriptor.layout, {
+ prefix,
+ context: "layout",
+ });
+ assertDeviceMatch(device, descriptor.layout, {
+ prefix,
+ resourceContext: "layout",
+ selfContext: "this",
+ });
+ const entries = descriptor.entries.map((entry, i) => {
+ const context = `entry ${i + 1}`;
+ const resource = entry.resource;
+ if (resource instanceof GPUSampler) {
+ const rid = assertResource(resource, {
+ prefix,
+ context,
+ });
+ assertDeviceMatch(device, resource, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return {
+ binding: entry.binding,
+ kind: "GPUSampler",
+ resource: rid,
+ };
+ } else if (resource instanceof GPUTextureView) {
+ const rid = assertResource(resource, {
+ prefix,
+ context,
+ });
+ assertResource(resource[_texture], {
+ prefix,
+ context,
+ });
+ assertDeviceMatch(device, resource[_texture], {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return {
+ binding: entry.binding,
+ kind: "GPUTextureView",
+ resource: rid,
+ };
+ } else {
+ const rid = assertResource(resource.buffer, { prefix, context });
+ assertDeviceMatch(device, resource.buffer, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return {
+ binding: entry.binding,
+ kind: "GPUBufferBinding",
+ resource: rid,
+ offset: entry.resource.offset,
+ size: entry.resource.size,
+ };
+ }
+ });
+
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_bind_group", {
+ deviceRid: device.rid,
+ label: descriptor.label,
+ layout,
+ entries,
+ });
+ device.pushError(err);
+
+ const bindGroup = createGPUBindGroup(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((bindGroup));
+ return bindGroup;
+ }
+
+ /**
+ * @param {GPUShaderModuleDescriptor} descriptor
+ */
+ createShaderModule(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createShaderModule' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUShaderModuleDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync(
+ "op_webgpu_create_shader_module",
+ {
+ deviceRid: device.rid,
+ label: descriptor.label,
+ code: (typeof descriptor.code === "string")
+ ? descriptor.code
+ : undefined,
+ sourceMap: descriptor.sourceMap,
+ },
+ ...(descriptor.code instanceof Uint32Array
+ ? [new Uint8Array(descriptor.code.buffer)]
+ : []),
+ );
+ device.pushError(err);
+
+ const shaderModule = createGPUShaderModule(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((shaderModule));
+ return shaderModule;
+ }
+
+ /**
+ * @param {GPUComputePipelineDescriptor} descriptor
+ * @returns {GPUComputePipeline}
+ */
+ createComputePipeline(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createComputePipeline' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUComputePipelineDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ let layout = undefined;
+ if (descriptor.layout) {
+ 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,
+ context: "compute shader module",
+ });
+ assertDeviceMatch(device, descriptor.compute.module, {
+ prefix,
+ resourceContext: "compute shader module",
+ selfContext: "this",
+ });
+
+ const { rid, err } = core.jsonOpSync(
+ "op_webgpu_create_compute_pipeline",
+ {
+ deviceRid: device.rid,
+ label: descriptor.label,
+ layout,
+ compute: {
+ module,
+ entryPoint: descriptor.compute.entryPoint,
+ },
+ },
+ );
+ device.pushError(err);
+
+ const computePipeline = createGPUComputePipeline(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((computePipeline));
+ return computePipeline;
+ }
+
+ /**
+ * @param {GPURenderPipelineDescriptor} descriptor
+ * @returns {GPURenderPipeline}
+ */
+ createRenderPipeline(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createRenderPipeline' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPURenderPipelineDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ let layout = undefined;
+ if (descriptor.layout) {
+ 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,
+ context: "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,
+ context: "fragment shader module",
+ });
+ assertDeviceMatch(device, descriptor.fragment.module, {
+ prefix,
+ resourceContext: "fragment shader module",
+ selfContext: "this",
+ });
+ fragment = {
+ module,
+ entryPoint: descriptor.fragment.entryPoint,
+ targets: descriptor.fragment.targets,
+ };
+ }
+
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_render_pipeline", {
+ deviceRid: device.rid,
+ label: descriptor.label,
+ layout,
+ vertex: {
+ module,
+ entryPoint: descriptor.vertex.entryPoint,
+ buffers: descriptor.vertex.buffers,
+ },
+ primitive: descriptor.primitive,
+ depthStencil: descriptor.depthStencil,
+ multisample: descriptor.multisample,
+ fragment,
+ });
+ device.pushError(err);
+
+ const renderPipeline = createGPURenderPipeline(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((renderPipeline));
+ return renderPipeline;
+ }
+
+ createComputePipelineAsync(descriptor) {
+ // TODO(lucacasonato): this should be real async
+ return Promise.resolve(this.createComputePipeline(descriptor));
+ }
+
+ createRenderPipelineAsync(descriptor) {
+ // TODO(lucacasonato): this should be real async
+ return Promise.resolve(this.createRenderPipeline(descriptor));
+ }
+
+ /**
+ * @param {GPUCommandEncoderDescriptor} descriptor
+ * @returns {GPUCommandEncoder}
+ */
+ createCommandEncoder(descriptor = {}) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createCommandEncoder' on 'GPUDevice'";
+ descriptor = webidl.converters.GPUCommandEncoderDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_command_encoder", {
+ deviceRid: device.rid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const commandEncoder = createGPUCommandEncoder(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((commandEncoder));
+ return commandEncoder;
+ }
+
+ /**
+ * @param {GPURenderBundleEncoderDescriptor} descriptor
+ * @returns {GPURenderBundleEncoder}
+ */
+ createRenderBundleEncoder(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix =
+ "Failed to execute 'createRenderBundleEncoder' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPURenderBundleEncoderDescriptor(
+ descriptor,
+ {
+ prefix,
+ context: "Argument 1",
+ },
+ );
+ const device = assertDevice(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync(
+ "op_webgpu_create_render_bundle_encoder",
+ {
+ deviceRid: device.rid,
+ ...descriptor,
+ },
+ );
+ device.pushError(err);
+
+ const renderBundleEncoder = createGPURenderBundleEncoder(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((renderBundleEncoder));
+ return renderBundleEncoder;
+ }
+
+ /**
+ * @param {GPUQuerySetDescriptor} descriptor
+ * @returns {GPUQuerySet}
+ */
+ createQuerySet(descriptor) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'createQuerySet' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPUQuerySetDescriptor(
+ descriptor,
+ {
+ prefix,
+ context: "Argument 1",
+ },
+ );
+ const device = assertDevice(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_query_set", {
+ deviceRid: device.rid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const querySet = createGPUQuerySet(
+ descriptor.label ?? null,
+ device,
+ rid,
+ descriptor,
+ );
+ device.trackResource((querySet));
+ return querySet;
+ }
+
+ get lost() {
+ webidl.assertBranded(this, GPUDevice);
+ const device = this[_device];
+ if (!device) {
+ return Promise.resolve(true);
+ }
+ if (device.rid === undefined) {
+ return Promise.resolve(true);
+ }
+ return device.lost;
+ }
+
+ /**
+ * @param {GPUErrorFilter} filter
+ */
+ pushErrorScope(filter) {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'pushErrorScope' on 'GPUDevice'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ filter = webidl.converters.GPUErrorFilter(filter, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ device.errorScopeStack.push({ filter, error: undefined });
+ }
+
+ /**
+ * @returns {Promise<GPUError | null>}
+ */
+ // deno-lint-ignore require-await
+ async popErrorScope() {
+ webidl.assertBranded(this, GPUDevice);
+ const prefix = "Failed to execute 'pushErrorScope' on 'GPUDevice'";
+ const device = assertDevice(this, { prefix, context: "this" });
+ if (device.isLost) {
+ throw new DOMException("Device has been lost.", "OperationError");
+ }
+ const scope = device.errorScopeStack.pop();
+ if (!scope) {
+ throw new DOMException(
+ "There are no error scopes on that stack.",
+ "OperationError",
+ );
+ }
+ return scope.error ?? null;
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ adapter: this.adapter,
+ features: this.features,
+ label: this.label,
+ limits: this.limits,
+ queue: this.queue,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUDevice", GPUDevice);
+
+ /**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @returns {GPUQueue}
+ */
+ function createGPUQueue(label, device) {
+ /** @type {GPUQueue} */
+ const queue = webidl.createBranded(GPUQueue);
+ queue[_label] = label;
+ queue[_device] = device;
+ return queue;
+ }
+
+ class GPUQueue {
+ /** @type {InnerGPUDevice} */
+ [_device];
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPUCommandBuffer[]} commandBuffers
+ */
+ submit(commandBuffers) {
+ webidl.assertBranded(this, GPUQueue);
+ const prefix = "Failed to execute 'submit' on 'GPUQueue'";
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix,
+ });
+ commandBuffers = webidl.converters["sequence<GPUCommandBuffer>"](
+ commandBuffers,
+ { prefix, context: "Argument 1" },
+ );
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandBufferRids = commandBuffers.map((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 } = core.jsonOpSync("op_webgpu_queue_submit", {
+ queueRid: device.rid,
+ commandBuffers: commandBufferRids,
+ });
+ device.pushError(err);
+ }
+
+ onSubmittedWorkDone() {
+ webidl.assertBranded(this, GPUQueue);
+ return Promise.resolve();
+ }
+
+ /**
+ * @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, GPUQueue);
+ const prefix = "Failed to execute 'writeBuffer' on 'GPUQueue'";
+ webidl.requiredArguments(arguments.length, 3, { prefix });
+ buffer = webidl.converters["GPUBuffer"](buffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ bufferOffset = webidl.converters["GPUSize64"](bufferOffset, {
+ prefix,
+ context: "Argument 2",
+ });
+ data = webidl.converters.BufferSource(data, {
+ prefix,
+ context: "Argument 3",
+ });
+ dataOffset = webidl.converters["GPUSize64"](dataOffset, {
+ prefix,
+ context: "Argument 4",
+ });
+ size = size === undefined
+ ? undefined
+ : webidl.converters["GPUSize64"](size, {
+ prefix,
+ context: "Argument 5",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const bufferRid = assertResource(buffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ selfContext: "this",
+ resourceContext: "Argument 1",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_write_buffer",
+ {
+ queueRid: device.rid,
+ buffer: bufferRid,
+ bufferOffset,
+ dataOffset,
+ size,
+ },
+ new Uint8Array(ArrayBuffer.isView(data) ? data.buffer : data),
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyTexture} destination
+ * @param {BufferSource} data
+ * @param {GPUImageDataLayout} dataLayout
+ * @param {GPUExtent3D} size
+ */
+ writeTexture(destination, data, dataLayout, size) {
+ webidl.assertBranded(this, GPUQueue);
+ const prefix = "Failed to execute 'writeTexture' on 'GPUQueue'";
+ webidl.requiredArguments(arguments.length, 4, { prefix });
+ destination = webidl.converters.GPUImageCopyTexture(destination, {
+ prefix,
+ context: "Argument 1",
+ });
+ data = webidl.converters.BufferSource(data, {
+ prefix,
+ context: "Argument 2",
+ });
+ dataLayout = webidl.converters.GPUImageDataLayout(dataLayout, {
+ prefix,
+ context: "Argument 3",
+ });
+ size = webidl.converters.GPUExtent3D(size, {
+ prefix,
+ context: "Argument 4",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const textureRid = assertResource(destination.texture, {
+ prefix,
+ context: "texture",
+ });
+ assertDeviceMatch(device, destination.texture, {
+ prefix,
+ selfContext: "this",
+ resourceContext: "texture",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_write_texture",
+ {
+ queueRid: device.rid,
+ destination: {
+ texture: textureRid,
+ mipLevel: destination.mipLevel,
+ origin: destination.origin
+ ? normalizeGPUOrigin3D(destination.origin)
+ : undefined,
+ },
+ dataLayout,
+ size: normalizeGPUExtent3D(size),
+ },
+ new Uint8Array(ArrayBuffer.isView(data) ? data.buffer : data),
+ );
+ device.pushError(err);
+ }
+
+ copyImageBitmapToTexture(_source, _destination, _copySize) {
+ throw new Error("Not yet implemented");
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUQueue", GPUQueue);
+
+ 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]]");
+
+ /**
+ * @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" | "mapped 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 = mappedRanges.pop();
+ 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();
+ }
+
+ /**
+ * @param {number} mode
+ * @param {number} offset
+ * @param {number} [size]
+ */
+ async mapAsync(mode, offset = 0, size) {
+ webidl.assertBranded(this, GPUBuffer);
+ const prefix = "Failed to execute 'mapAsync' on 'GPUBuffer'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ mode = webidl.converters.GPUMapModeFlags(mode, {
+ prefix,
+ context: "Argument 1",
+ });
+ offset = webidl.converters.GPUSize64(offset, {
+ prefix,
+ context: "Argument 2",
+ });
+ size = size === undefined
+ ? undefined
+ : webidl.converters.GPUSize64(size, {
+ prefix,
+ context: "Argument 3",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const bufferRid = assertResource(this, { prefix, context: "this" });
+ /** @type {number} */
+ let rangeSize;
+ if (size === undefined) {
+ rangeSize = Math.max(0, this[_size] - offset);
+ } else {
+ rangeSize = this[_size];
+ }
+ if ((offset % 8) !== 0) {
+ throw new DOMException(
+ `${prefix}: offset must be a multiple of 8.`,
+ "OperationError",
+ );
+ }
+ if ((rangeSize % 4) !== 0) {
+ throw new DOMException(
+ `${prefix}: rangeSize must be a multiple of 4.`,
+ "OperationError",
+ );
+ }
+ if ((offset + rangeSize) > this[_size]) {
+ throw new DOMException(
+ `${prefix}: offset + rangeSize must be less than or equal to buffer size.`,
+ "OperationError",
+ );
+ }
+ if (this[_state] !== "unmapped") {
+ throw new DOMException(
+ `${prefix}: GPUBuffer is not currently unmapped.`,
+ "OperationError",
+ );
+ }
+ const readMode = (mode & 0x0001) === 0x0001;
+ const writeMode = (mode & 0x0002) === 0x0002;
+ if ((readMode && writeMode) || (!readMode && !writeMode)) {
+ throw new DOMException(
+ `${prefix}: exactly one of READ or WRITE map mode must be set.`,
+ "OperationError",
+ );
+ }
+ if (readMode && !((this[_usage] && 0x0001) === 0x0001)) {
+ throw new DOMException(
+ `${prefix}: READ map mode not valid because buffer does not have MAP_READ usage.`,
+ "OperationError",
+ );
+ }
+ if (writeMode && !((this[_usage] && 0x0002) === 0x0002)) {
+ throw new DOMException(
+ `${prefix}: WRITE map mode not valid because buffer does not have MAP_WRITE usage.`,
+ "OperationError",
+ );
+ }
+
+ this[_mapMode] = mode;
+ this[_state] = "mapping pending";
+ const { err } = await core.jsonOpAsync(
+ "op_webgpu_buffer_get_map_async",
+ {
+ bufferRid,
+ deviceRid: device.rid,
+ mode,
+ offset,
+ size: rangeSize,
+ },
+ );
+ device.pushError(err);
+ 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, GPUBuffer);
+ const prefix = "Failed to execute 'getMappedRange' on 'GPUBuffer'";
+ offset = webidl.converters.GPUSize64(offset, {
+ prefix,
+ context: "Argument 1",
+ });
+ size = size === undefined
+ ? undefined
+ : webidl.converters.GPUSize64(size, {
+ prefix,
+ context: "Argument 2",
+ });
+ assertDevice(this, { prefix, context: "this" });
+ const bufferRid = assertResource(this, { prefix, context: "this" });
+ /** @type {number} */
+ let rangeSize;
+ if (size === undefined) {
+ rangeSize = Math.max(0, this[_size] - offset);
+ } else {
+ rangeSize = this[_size];
+ }
+ if (this[_state] !== "mapped" && this[_state] !== "mapped at creation") {
+ throw new DOMException(
+ `${prefix}: buffer is not mapped.`,
+ "OperationError",
+ );
+ }
+ if ((offset % 8) !== 0) {
+ throw new DOMException(
+ `${prefix}: offset must be a multiple of 8.`,
+ "OperationError",
+ );
+ }
+ if ((rangeSize % 4) !== 0) {
+ throw new DOMException(
+ `${prefix}: rangeSize must be a multiple of 4.`,
+ "OperationError",
+ );
+ }
+ const mappingRange = this[_mappingRange];
+ if (!mappingRange) {
+ throw new DOMException(`${prefix}: invalid state.`, "OperationError");
+ }
+ if (offset < mappingRange[0]) {
+ throw new DOMException(
+ `${prefix}: offset is out of bounds.`,
+ "OperationError",
+ );
+ }
+ if ((offset + rangeSize) > mappingRange[1]) {
+ throw new DOMException(
+ `${prefix}: offset is out of bounds.`,
+ "OperationError",
+ );
+ }
+ const mappedRanges = this[_mappedRanges];
+ if (!mappedRanges) {
+ throw new DOMException(`${prefix}: invalid state.`, "OperationError");
+ }
+ for (const [buffer, _rid, start] of mappedRanges) {
+ // TODO(lucacasonato): is this logic correct?
+ const end = start + buffer.byteLength;
+ 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 } = core.jsonOpSync(
+ "op_webgpu_buffer_get_mapped_range",
+ {
+ bufferRid,
+ offset: offset - mappingRange[0],
+ size: rangeSize,
+ },
+ new Uint8Array(buffer),
+ );
+
+ mappedRanges.push([buffer, rid, offset]);
+
+ return buffer;
+ }
+
+ unmap() {
+ webidl.assertBranded(this, GPUBuffer);
+ const prefix = "Failed to execute 'unmap' on 'GPUBuffer'";
+ const device = assertDevice(this, { prefix, context: "this" });
+ const bufferRid = assertResource(this, { prefix, context: "this" });
+ if (this[_state] === "unmapped" || this[_state] === "destroyed") {
+ throw new DOMException(
+ `${prefix}: buffer is not ready to be unmapped.`,
+ "OperationError",
+ );
+ }
+ if (this[_state] === "mapping 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 (const [buffer, mappedRid] of mappedRanges) {
+ const { err } = core.jsonOpSync("op_webgpu_buffer_unmap", {
+ bufferRid,
+ mappedRid,
+ }, ...(write ? [new Uint8Array(buffer)] : []));
+ device.pushError(err);
+ if (err) return;
+ }
+ this[_mappingRange] = null;
+ this[_mappedRanges] = null;
+ }
+
+ this[_state] = "unmapped";
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUBuffer);
+ this[_cleanup]();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUBuffer", GPUBuffer);
+
+ class GPUBufferUsage {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get MAP_READ() {
+ return 0x0001;
+ }
+ static get MAP_WRITE() {
+ return 0x0002;
+ }
+ static get COPY_SRC() {
+ return 0x0004;
+ }
+ static get COPY_DST() {
+ return 0x0008;
+ }
+ static get INDEX() {
+ return 0x0010;
+ }
+ static get VERTEX() {
+ return 0x0020;
+ }
+ static get UNIFORM() {
+ return 0x0040;
+ }
+ static get STORAGE() {
+ return 0x0080;
+ }
+ static get INDIRECT() {
+ return 0x0100;
+ }
+ static get QUERY_RESOLVE() {
+ return 0x0200;
+ }
+ }
+
+ class GPUMapMode {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get READ() {
+ return 0x0001;
+ }
+ static get WRITE() {
+ return 0x0002;
+ }
+ }
+
+ const _views = Symbol("[[views]]");
+
+ /**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUTexture}
+ */
+ function createGPUTexture(label, device, rid) {
+ /** @type {GPUTexture} */
+ const texture = webidl.createBranded(GPUTexture);
+ texture[_label] = label;
+ texture[_device] = device;
+ texture[_rid] = rid;
+ texture[_views] = [];
+ return texture;
+ }
+
+ class GPUTexture {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+ /** @type {WeakRef<GPUTextureView>[]} */
+ [_views];
+
+ [_cleanup]() {
+ const views = this[_views];
+ while (views.length > 0) {
+ const view = views.pop()?.deref();
+ if (view) {
+ view[_cleanup]();
+ }
+ }
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPUTextureViewDescriptor} descriptor
+ */
+ createView(descriptor = {}) {
+ webidl.assertBranded(this, GPUTexture);
+ const prefix = "Failed to execute 'createView' on 'GPUTexture'";
+ webidl.requiredArguments(arguments.length, 0, { prefix });
+ descriptor = webidl.converters.GPUTextureViewDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const textureRid = assertResource(this, { prefix, context: "this" });
+ const { rid, err } = core.jsonOpSync("op_webgpu_create_texture_view", {
+ textureRid,
+ ...descriptor,
+ });
+ device.pushError(err);
+
+ const textureView = createGPUTextureView(
+ descriptor.label ?? null,
+ this,
+ rid,
+ );
+ this[_views].push(new WeakRef(textureView));
+ return textureView;
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUTexture);
+ this[_cleanup]();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUTexture", GPUTexture);
+
+ class GPUTextureUsage {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get COPY_SRC() {
+ return 0x01;
+ }
+ static get COPY_DST() {
+ return 0x02;
+ }
+ static get SAMPLED() {
+ return 0x04;
+ }
+ static get STORAGE() {
+ return 0x08;
+ }
+ static get RENDER_ATTACHMENT() {
+ return 0x10;
+ }
+ }
+
+ const _texture = Symbol("[[texture]]");
+
+ /**
+ * @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();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUTextureView", GPUTextureView);
+
+ /**
+ * @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();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUSampler", GPUSampler);
+
+ /**
+ * @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();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUBindGroupLayout", GPUBindGroupLayout);
+
+ /**
+ * @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();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUPipelineLayout", GPUPipelineLayout);
+
+ /**
+ * @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();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUBindGroup", GPUBindGroup);
+
+ /**
+ * @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();
+ }
+
+ compilationInfo() {
+ throw new Error("Not yet implemented");
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUShaderModule", GPUShaderModule);
+
+ class GPUShaderStage {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get VERTEX() {
+ return 0x1;
+ }
+
+ static get FRAGMENT() {
+ return 0x2;
+ }
+
+ static get COMPUTE() {
+ return 0x4;
+ }
+ }
+
+ /**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPUComputePipeline}
+ */
+ function createGPUComputePipeline(label, device, rid) {
+ /** @type {GPUComputePipeline} */
+ const pipeline = webidl.createBranded(GPUComputePipeline);
+ pipeline[_label] = label;
+ pipeline[_device] = device;
+ pipeline[_rid] = rid;
+ return pipeline;
+ }
+ class GPUComputePipeline {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {number} index
+ * @returns {GPUBindGroupLayout}
+ */
+ getBindGroupLayout(index) {
+ webidl.assertBranded(this, GPURenderPipeline);
+ const prefix =
+ "Failed to execute 'getBindGroupLayout' on 'GPUComputePipeline'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ index = webidl.converters["unsigned long"](index, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const computePipelineRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const { rid, label, err } = core.jsonOpSync(
+ "op_webgpu_compute_pipeline_get_bind_group_layout",
+ { computePipelineRid, index },
+ );
+ device.pushError(err);
+
+ const bindGroupLayout = createGPUBindGroupLayout(
+ label,
+ device,
+ rid,
+ );
+ device.trackResource((bindGroupLayout));
+ return bindGroupLayout;
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUComputePipeline", GPUComputePipeline);
+
+ /**
+ * @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, GPURenderPipeline);
+ const prefix =
+ "Failed to execute 'getBindGroupLayout' on 'GPURenderPipeline'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ index = webidl.converters["unsigned long"](index, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const renderPipelineRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const { rid, label, err } = core.jsonOpSync(
+ "op_webgpu_render_pipeline_get_bind_group_layout",
+ { renderPipelineRid, index },
+ );
+ device.pushError(err);
+
+ const bindGroupLayout = createGPUBindGroupLayout(
+ label,
+ device,
+ rid,
+ );
+ device.trackResource((bindGroupLayout));
+ return bindGroupLayout;
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPURenderPipeline", GPURenderPipeline);
+
+ class GPUColorWrite {
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ static get RED() {
+ return 0x1;
+ }
+ static get GREEN() {
+ return 0x2;
+ }
+ static get BLUE() {
+ return 0x4;
+ }
+ static get ALPHA() {
+ return 0x8;
+ }
+ static get ALL() {
+ return 0xF;
+ }
+ }
+
+ const _encoders = Symbol("[[encoders]]");
+
+ /**
+ * @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 {WeakRef<GPURenderPassEncoder | GPUComputePassEncoder>[]} */
+ [_encoders];
+
+ [_cleanup]() {
+ const encoders = this[_encoders];
+ while (encoders.length > 0) {
+ const encoder = encoders.pop()?.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, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ descriptor = webidl.converters.GPURenderPassDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+
+ if (this[_rid] === undefined) {
+ throw new DOMException(
+ "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder': already consumed",
+ "OperationError",
+ );
+ }
+
+ let depthStencilAttachment;
+ if (descriptor.depthStencilAttachment) {
+ const view = assertResource(descriptor.depthStencilAttachment.view, {
+ prefix,
+ context: "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,
+ };
+
+ if (
+ typeof descriptor.depthStencilAttachment.depthLoadValue === "string"
+ ) {
+ depthStencilAttachment.depthLoadOp =
+ descriptor.depthStencilAttachment.depthLoadValue;
+ } else {
+ depthStencilAttachment.depthLoadOp = "clear";
+ depthStencilAttachment.depthLoadValue =
+ descriptor.depthStencilAttachment.depthLoadValue;
+ }
+
+ if (
+ typeof descriptor.depthStencilAttachment.stencilLoadValue === "string"
+ ) {
+ depthStencilAttachment.stencilLoadOp =
+ descriptor.depthStencilAttachment.stencilLoadValue;
+ depthStencilAttachment.stencilLoadValue = undefined;
+ } else {
+ depthStencilAttachment.stencilLoadOp = "clear";
+ depthStencilAttachment.stencilLoadValue =
+ descriptor.depthStencilAttachment.stencilLoadValue;
+ }
+ }
+ const colorAttachments = descriptor.colorAttachments.map(
+ (colorAttachment, i) => {
+ const context = `color attachment ${i + 1}`;
+ const view = assertResource(colorAttachment.view, {
+ prefix,
+ context: `texture view for ${context}`,
+ });
+ assertResource(colorAttachment.view[_texture], {
+ prefix,
+ context: `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,
+ context: `resolve target texture view for ${context}`,
+ },
+ );
+ assertResource(colorAttachment.resolveTarget[_texture], {
+ prefix,
+ context:
+ `texture backing resolve target texture view for ${context}`,
+ });
+ assertDeviceMatch(
+ device,
+ colorAttachment.resolveTarget[_texture],
+ {
+ prefix,
+ resourceContext: `resolve target texture view for ${context}`,
+ selfContext: "this",
+ },
+ );
+ }
+ const attachment = {
+ view: view,
+ resolveTarget,
+ storeOp: colorAttachment.storeOp,
+ };
+
+ if (typeof colorAttachment.loadValue === "string") {
+ attachment.loadOp = colorAttachment.loadValue;
+ } else {
+ attachment.loadOp = "clear";
+ attachment.loadValue = normalizeGPUColor(
+ colorAttachment.loadValue,
+ );
+ }
+
+ return attachment;
+ },
+ );
+
+ const { rid } = core.jsonOpSync(
+ "op_webgpu_command_encoder_begin_render_pass",
+ {
+ commandEncoderRid,
+ ...descriptor,
+ colorAttachments,
+ depthStencilAttachment,
+ },
+ );
+
+ const renderPassEncoder = createGPURenderPassEncoder(
+ descriptor.label ?? null,
+ this,
+ rid,
+ );
+ this[_encoders].push(new WeakRef(renderPassEncoder));
+ return renderPassEncoder;
+ }
+
+ /**
+ * @param {GPUComputePassDescriptor} descriptor
+ */
+ beginComputePass(descriptor = {}) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'beginComputePass' on 'GPUCommandEncoder'";
+ descriptor = webidl.converters.GPUComputePassDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+
+ const { rid } = core.jsonOpSync(
+ "op_webgpu_command_encoder_begin_compute_pass",
+ {
+ commandEncoderRid,
+ ...descriptor,
+ },
+ );
+
+ const computePassEncoder = createGPUComputePassEncoder(
+ descriptor.label ?? null,
+ this,
+ rid,
+ );
+ this[_encoders].push(new WeakRef(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, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'copyBufferToBuffer' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 5, { prefix });
+ source = webidl.converters.GPUBuffer(source, {
+ prefix,
+ context: "Argument 1",
+ });
+ sourceOffset = webidl.converters.GPUSize64(sourceOffset, {
+ prefix,
+ context: "Argument 2",
+ });
+ destination = webidl.converters.GPUBuffer(destination, {
+ prefix,
+ context: "Argument 3",
+ });
+ destinationOffset = webidl.converters.GPUSize64(destinationOffset, {
+ prefix,
+ context: "Argument 4",
+ });
+ size = webidl.converters.GPUSize64(size, {
+ prefix,
+ context: "Argument 5",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const sourceRid = assertResource(source, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, source, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ const destinationRid = assertResource(destination, {
+ prefix,
+ context: "Argument 3",
+ });
+ assertDeviceMatch(device, destination, {
+ prefix,
+ resourceContext: "Argument 3",
+ selfContext: "this",
+ });
+
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_copy_buffer_to_buffer",
+ {
+ commandEncoderRid,
+ source: sourceRid,
+ sourceOffset,
+ destination: destinationRid,
+ destinationOffset,
+ size,
+ },
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyBuffer} source
+ * @param {GPUImageCopyTexture} destination
+ * @param {GPUExtent3D} copySize
+ */
+ copyBufferToTexture(source, destination, copySize) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'copyBufferToTexture' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 3, { prefix });
+ source = webidl.converters.GPUImageCopyBuffer(source, {
+ prefix,
+ context: "Argument 1",
+ });
+ destination = webidl.converters.GPUImageCopyTexture(destination, {
+ prefix,
+ context: "Argument 2",
+ });
+ copySize = webidl.converters.GPUExtent3D(copySize, {
+ prefix,
+ context: "Argument 3",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const sourceBufferRid = assertResource(source.buffer, {
+ prefix,
+ context: "source in Argument 1",
+ });
+ assertDeviceMatch(device, source.buffer, {
+ prefix,
+ resourceContext: "source in Argument 1",
+ selfContext: "this",
+ });
+ const destinationTextureRid = assertResource(destination.texture, {
+ prefix,
+ context: "texture in Argument 2",
+ });
+ assertDeviceMatch(device, destination.texture, {
+ prefix,
+ resourceContext: "texture in Argument 2",
+ selfContext: "this",
+ });
+
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_copy_buffer_to_texture",
+ {
+ commandEncoderRid,
+ source: {
+ ...source,
+ buffer: sourceBufferRid,
+ },
+ destination: {
+ texture: destinationTextureRid,
+ mipLevel: destination.mipLevel,
+ origin: destination.origin
+ ? normalizeGPUOrigin3D(destination.origin)
+ : undefined,
+ },
+ copySize: normalizeGPUExtent3D(copySize),
+ },
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyTexture} source
+ * @param {GPUImageCopyBuffer} destination
+ * @param {GPUExtent3D} copySize
+ */
+ copyTextureToBuffer(source, destination, copySize) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'copyTextureToBuffer' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 3, { prefix });
+ source = webidl.converters.GPUImageCopyTexture(source, {
+ prefix,
+ context: "Argument 1",
+ });
+ destination = webidl.converters.GPUImageCopyBuffer(destination, {
+ prefix,
+ context: "Argument 2",
+ });
+ copySize = webidl.converters.GPUExtent3D(copySize, {
+ prefix,
+ context: "Argument 3",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const sourceTextureRid = assertResource(source.texture, {
+ prefix,
+ context: "texture in Argument 1",
+ });
+ assertDeviceMatch(device, source.texture, {
+ prefix,
+ resourceContext: "texture in Argument 1",
+ selfContext: "this",
+ });
+ const destinationBufferRid = assertResource(destination.buffer, {
+ prefix,
+ context: "buffer in Argument 2",
+ });
+ assertDeviceMatch(device, destination.buffer, {
+ prefix,
+ resourceContext: "buffer in Argument 2",
+ selfContext: "this",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_copy_texture_to_buffer",
+ {
+ commandEncoderRid,
+ source: {
+ texture: sourceTextureRid,
+ mipLevel: source.mipLevel,
+ origin: source.origin
+ ? normalizeGPUOrigin3D(source.origin)
+ : undefined,
+ },
+ destination: {
+ ...destination,
+ buffer: destinationBufferRid,
+ },
+ copySize: normalizeGPUExtent3D(copySize),
+ },
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUImageCopyTexture} source
+ * @param {GPUImageCopyTexture} destination
+ * @param {GPUExtent3D} copySize
+ */
+ copyTextureToTexture(source, destination, copySize) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'copyTextureToTexture' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 3, { prefix });
+ source = webidl.converters.GPUImageCopyTexture(source, {
+ prefix,
+ context: "Argument 1",
+ });
+ destination = webidl.converters.GPUImageCopyTexture(destination, {
+ prefix,
+ context: "Argument 2",
+ });
+ copySize = webidl.converters.GPUExtent3D(copySize, {
+ prefix,
+ context: "Argument 3",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const sourceTextureRid = assertResource(source.texture, {
+ prefix,
+ context: "texture in Argument 1",
+ });
+ assertDeviceMatch(device, source.texture, {
+ prefix,
+ resourceContext: "texture in Argument 1",
+ selfContext: "this",
+ });
+ const destinationTextureRid = assertResource(destination.texture, {
+ prefix,
+ context: "texture in Argument 2",
+ });
+ assertDeviceMatch(device, destination.texture, {
+ prefix,
+ resourceContext: "texture in Argument 2",
+ selfContext: "this",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_copy_texture_to_texture",
+ {
+ commandEncoderRid,
+ source: {
+ texture: sourceTextureRid,
+ mipLevel: source.mipLevel,
+ origin: source.origin
+ ? normalizeGPUOrigin3D(source.origin)
+ : undefined,
+ },
+ destination: {
+ texture: destinationTextureRid,
+ mipLevel: destination.mipLevel,
+ origin: destination.origin
+ ? normalizeGPUOrigin3D(destination.origin)
+ : undefined,
+ },
+ copySize: normalizeGPUExtent3D(copySize),
+ },
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'pushDebugGroup' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ groupLabel = webidl.converters.USVString(groupLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_push_debug_group",
+ {
+ commandEncoderRid,
+ groupLabel,
+ },
+ );
+ device.pushError(err);
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix = "Failed to execute 'popDebugGroup' on 'GPUCommandEncoder'";
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_pop_debug_group",
+ {
+ commandEncoderRid,
+ },
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ markerLabel = webidl.converters.USVString(markerLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_insert_debug_marker",
+ {
+ commandEncoderRid,
+ markerLabel,
+ },
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUQuerySet} querySet
+ * @param {number} queryIndex
+ */
+ writeTimestamp(querySet, queryIndex) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'writeTimestamp' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ querySet = webidl.converters.GPUQuerySet(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ queryIndex = webidl.converters.GPUSize32(queryIndex, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const querySetRid = assertResource(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_write_timestamp",
+ {
+ commandEncoderRid,
+ querySet: 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, GPUCommandEncoder);
+ const prefix =
+ "Failed to execute 'resolveQuerySet' on 'GPUCommandEncoder'";
+ webidl.requiredArguments(arguments.length, 5, { prefix });
+ querySet = webidl.converters.GPUQuerySet(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ firstQuery = webidl.converters.GPUSize32(firstQuery, {
+ prefix,
+ context: "Argument 2",
+ });
+ queryCount = webidl.converters.GPUSize32(queryCount, {
+ prefix,
+ context: "Argument 3",
+ });
+ destination = webidl.converters.GPUQuerySet(destination, {
+ prefix,
+ context: "Argument 4",
+ });
+ destinationOffset = webidl.converters.GPUSize64(destinationOffset, {
+ prefix,
+ context: "Argument 5",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const querySetRid = assertResource(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ const destinationRid = assertResource(destination, {
+ prefix,
+ context: "Argument 3",
+ });
+ assertDeviceMatch(device, destination, {
+ prefix,
+ resourceContext: "Argument 3",
+ selfContext: "this",
+ });
+ const { err } = core.jsonOpSync(
+ "op_webgpu_command_encoder_resolve_query_set",
+ {
+ commandEncoderRid,
+ querySet: querySetRid,
+ firstQuery,
+ queryCount,
+ destination: destinationRid,
+ destinationOffset,
+ },
+ );
+ device.pushError(err);
+ }
+
+ /**
+ * @param {GPUCommandBufferDescriptor} descriptor
+ * @returns {GPUCommandBuffer}
+ */
+ finish(descriptor = {}) {
+ webidl.assertBranded(this, GPUCommandEncoder);
+ const prefix = "Failed to execute 'finish' on 'GPUCommandEncoder'";
+ descriptor = webidl.converters.GPUCommandBufferDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const commandEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const { rid, err } = core.jsonOpSync("op_webgpu_command_encoder_finish", {
+ commandEncoderRid,
+ ...descriptor,
+ });
+ device.pushError(err);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+
+ const commandBuffer = createGPUCommandBuffer(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((commandBuffer));
+ return commandBuffer;
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUCommandEncoder", GPUCommandEncoder);
+
+ const _encoder = Symbol("[[encoder]]");
+
+ /**
+ * @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, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setViewport' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 6, { prefix });
+ x = webidl.converters.float(x, { prefix, context: "Argument 1" });
+ y = webidl.converters.float(y, { prefix, context: "Argument 2" });
+ width = webidl.converters.float(width, { prefix, context: "Argument 3" });
+ height = webidl.converters.float(height, {
+ prefix,
+ context: "Argument 4",
+ });
+ minDepth = webidl.converters.float(minDepth, {
+ prefix,
+ context: "Argument 5",
+ });
+ maxDepth = webidl.converters.float(maxDepth, {
+ prefix,
+ context: "Argument 6",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("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, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setScissorRect' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 4, { prefix });
+ x = webidl.converters.GPUIntegerCoordinate(x, {
+ prefix,
+ context: "Argument 1",
+ });
+ y = webidl.converters.GPUIntegerCoordinate(y, {
+ prefix,
+ context: "Argument 2",
+ });
+ width = webidl.converters.GPUIntegerCoordinate(width, {
+ prefix,
+ context: "Argument 3",
+ });
+ height = webidl.converters.GPUIntegerCoordinate(height, {
+ prefix,
+ context: "Argument 4",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_set_scissor_rect", {
+ renderPassRid,
+ x,
+ y,
+ width,
+ height,
+ });
+ }
+
+ /**
+ * @param {GPUColor} color
+ */
+ setBlendColor(color) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setBlendColor' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ color = webidl.converters.GPUColor(color, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_set_blend_color", {
+ renderPassRid,
+ color: normalizeGPUColor(color),
+ });
+ }
+
+ /**
+ * @param {number} reference
+ */
+ setStencilReference(reference) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setStencilReference' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ reference = webidl.converters.GPUStencilValue(reference, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_set_stencil_reference", {
+ renderPassRid,
+ reference,
+ });
+ }
+
+ beginOcclusionQuery(_queryIndex) {
+ throw new Error("Not yet implemented");
+ }
+
+ endOcclusionQuery() {
+ throw new Error("Not yet implemented");
+ }
+
+ /**
+ * @param {GPUQuerySet} querySet
+ * @param {number} queryIndex
+ */
+ beginPipelineStatisticsQuery(querySet, queryIndex) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'beginPipelineStatisticsQuery' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ querySet = webidl.converters.GPUQuerySet(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ queryIndex = webidl.converters.GPUSize32(queryIndex, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const querySetRid = assertResource(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_pass_begin_pipeline_statistics_query", {
+ renderPassRid,
+ querySet: querySetRid,
+ queryIndex,
+ });
+ }
+
+ endPipelineStatisticsQuery() {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'endPipelineStatisticsQuery' on 'GPURenderPassEncoder'";
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_end_pipeline_statistics_query", {
+ renderPassRid,
+ });
+ }
+
+ /**
+ * @param {GPUQuerySet} querySet
+ * @param {number} queryIndex
+ */
+ writeTimestamp(querySet, queryIndex) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'writeTimestamp' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ querySet = webidl.converters.GPUQuerySet(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ queryIndex = webidl.converters.GPUSize32(queryIndex, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const querySetRid = assertResource(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_pass_write_timestamp", {
+ renderPassRid,
+ querySet: querySetRid,
+ queryIndex,
+ });
+ }
+
+ /**
+ * @param {GPURenderBundle[]} bundles
+ */
+ executeBundles(bundles) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'executeBundles' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ bundles = webidl.converters["sequence<GPURenderBundle>"](bundles, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const bundleRids = bundles.map((bundle, i) => {
+ const context = `bundle ${i + 1}`;
+ const rid = assertResource(bundle, { prefix, context });
+ assertDeviceMatch(device, bundle, {
+ prefix,
+ resourceContext: context,
+ selfContext: "this",
+ });
+ return rid;
+ });
+ core.jsonOpSync("op_webgpu_render_pass_execute_bundles", {
+ renderPassRid,
+ bundles: bundleRids,
+ });
+ }
+
+ endPass() {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix = "Failed to execute 'endPass' on 'GPURenderPassEncoder'";
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const commandEncoderRid = assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const { err } = core.jsonOpSync("op_webgpu_render_pass_end_pass", {
+ commandEncoderRid,
+ renderPassRid,
+ });
+ device.pushError(err);
+ this[_rid] = undefined;
+ }
+
+ // TODO(lucacasonato): has an overload
+ setBindGroup(
+ index,
+ bindGroup,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ ) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setBindGroup' on 'GPURenderPassEncoder'";
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const bindGroupRid = assertResource(bindGroup, {
+ prefix,
+ context: "Argument 2",
+ });
+ assertDeviceMatch(device, bindGroup, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ if (dynamicOffsetsData instanceof Uint32Array) {
+ core.jsonOpSync(
+ "op_webgpu_render_pass_set_bind_group",
+ {
+ renderPassRid,
+ index,
+ bindGroup: bindGroupRid,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ },
+ dynamicOffsetsData,
+ );
+ } else {
+ dynamicOffsetsData ??= [];
+ core.jsonOpSync("op_webgpu_render_pass_set_bind_group", {
+ renderPassRid,
+ index,
+ bindGroup: bindGroupRid,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart: 0,
+ dynamicOffsetsDataLength: dynamicOffsetsData.length,
+ });
+ }
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'pushDebugGroup' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ groupLabel = webidl.converters.USVString(groupLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_push_debug_group", {
+ renderPassRid,
+ groupLabel,
+ });
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'popDebugGroup' on 'GPURenderPassEncoder'";
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_pop_debug_group", {
+ renderPassRid,
+ });
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ markerLabel = webidl.converters.USVString(markerLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_insert_debug_marker", {
+ renderPassRid,
+ markerLabel,
+ });
+ }
+
+ /**
+ * @param {GPURenderPipeline} pipeline
+ */
+ setPipeline(pipeline) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setPipeline' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ pipeline = webidl.converters.GPURenderPipeline(pipeline, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const pipelineRid = assertResource(pipeline, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, pipeline, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_pass_set_pipeline", {
+ renderPassRid,
+ pipeline: pipelineRid,
+ });
+ }
+
+ /**
+ * @param {GPUBuffer} buffer
+ * @param {GPUIndexFormat} indexFormat
+ * @param {number} offset
+ * @param {number} size
+ */
+ setIndexBuffer(buffer, indexFormat, offset = 0, size = 0) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setIndexBuffer' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ buffer = webidl.converters.GPUBuffer(buffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ indexFormat = webidl.converters.GPUIndexFormat(indexFormat, {
+ prefix,
+ context: "Argument 2",
+ });
+ offset = webidl.converters.GPUSize64(offset, {
+ prefix,
+ context: "Argument 3",
+ });
+ size = webidl.converters.GPUSize64(size, {
+ prefix,
+ context: "Argument 4",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const bufferRid = assertResource(buffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_pass_set_index_buffer", {
+ renderPassRid,
+ buffer: bufferRid,
+ indexFormat,
+ offset,
+ size,
+ });
+ }
+
+ /**
+ * @param {number} slot
+ * @param {GPUBuffer} buffer
+ * @param {number} offset
+ * @param {number} size
+ */
+ setVertexBuffer(slot, buffer, offset = 0, size = 0) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'setVertexBuffer' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ slot = webidl.converters.GPUSize32(slot, {
+ prefix,
+ context: "Argument 2",
+ });
+ buffer = webidl.converters.GPUBuffer(buffer, {
+ prefix,
+ context: "Argument 2",
+ });
+ offset = webidl.converters.GPUSize64(offset, {
+ prefix,
+ context: "Argument 3",
+ });
+ size = webidl.converters.GPUSize64(size, {
+ prefix,
+ context: "Argument 4",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const bufferRid = assertResource(buffer, {
+ prefix,
+ context: "Argument 2",
+ });
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_pass_set_vertex_buffer", {
+ renderPassRid,
+ slot,
+ buffer: 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, GPURenderPassEncoder);
+ const prefix = "Failed to execute 'draw' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ vertexCount = webidl.converters.GPUSize32(vertexCount, {
+ prefix,
+ context: "Argument 1",
+ });
+ instanceCount = webidl.converters.GPUSize32(instanceCount, {
+ prefix,
+ context: "Argument 2",
+ });
+ firstVertex = webidl.converters.GPUSize32(firstVertex, {
+ prefix,
+ context: "Argument 3",
+ });
+ firstInstance = webidl.converters.GPUSize32(firstInstance, {
+ prefix,
+ context: "Argument 4",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("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, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'drawIndexed' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ indexCount = webidl.converters.GPUSize32(indexCount, {
+ prefix,
+ context: "Argument 1",
+ });
+ instanceCount = webidl.converters.GPUSize32(instanceCount, {
+ prefix,
+ context: "Argument 2",
+ });
+ firstIndex = webidl.converters.GPUSize32(firstIndex, {
+ prefix,
+ context: "Argument 3",
+ });
+ baseVertex = webidl.converters.GPUSignedOffset32(baseVertex, {
+ prefix,
+ context: "Argument 4",
+ });
+ firstInstance = webidl.converters.GPUSize32(firstInstance, {
+ prefix,
+ context: "Argument 5",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_render_pass_draw_indexed", {
+ renderPassRid,
+ indexCount,
+ instanceCount,
+ firstIndex,
+ baseVertex,
+ firstInstance,
+ });
+ }
+
+ /**
+ * @param {GPUBuffer} indirectBuffer
+ * @param {number} indirectOffset
+ */
+ drawIndirect(indirectBuffer, indirectOffset) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'drawIndirect' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const indirectBufferRid = assertResource(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_pass_draw_indirect", {
+ renderPassRid,
+ indirectBuffer: indirectBufferRid,
+ indirectOffset,
+ });
+ }
+
+ /**
+ * @param {GPUBuffer} indirectBuffer
+ * @param {number} indirectOffset
+ */
+ drawIndexedIndirect(indirectBuffer, indirectOffset) {
+ webidl.assertBranded(this, GPURenderPassEncoder);
+ const prefix =
+ "Failed to execute 'drawIndirect' on 'GPURenderPassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const renderPassRid = assertResource(this, { prefix, context: "this" });
+ const indirectBufferRid = assertResource(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_pass_draw_indexed_indirect", {
+ renderPassRid,
+ indirectBuffer: indirectBufferRid,
+ indirectOffset,
+ });
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPURenderPassEncoder", GPURenderPassEncoder);
+
+ /**
+ * @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, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'setPipeline' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ pipeline = webidl.converters.GPUComputePipeline(pipeline, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ const pipelineRid = assertResource(pipeline, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, pipeline, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_compute_pass_set_pipeline", {
+ computePassRid,
+ pipeline: pipelineRid,
+ });
+ }
+
+ /**
+ * @param {number} x
+ * @param {number} y
+ * @param {number} z
+ */
+ dispatch(x, y = 1, z = 1) {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix = "Failed to execute 'dispatch' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ x = webidl.converters.GPUSize32(x, { prefix, context: "Argument 1" });
+ y = webidl.converters.GPUSize32(y, { prefix, context: "Argument 2" });
+ z = webidl.converters.GPUSize32(z, { prefix, context: "Argument 3" });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_compute_pass_dispatch", {
+ computePassRid,
+ x,
+ y,
+ z,
+ });
+ }
+
+ /**
+ * @param {GPUBuffer} indirectBuffer
+ * @param {number} indirectOffset
+ */
+ dispatchIndirect(indirectBuffer, indirectOffset) {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'dispatchIndirect' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ const indirectBufferRid = assertResource(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_compute_pass_dispatch_indirect", {
+ computePassRid: computePassRid,
+ indirectBuffer: indirectBufferRid,
+ indirectOffset,
+ });
+ }
+
+ /**
+ * @param {GPUQuerySet} querySet
+ * @param {number} queryIndex
+ */
+ beginPipelineStatisticsQuery(querySet, queryIndex) {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'beginPipelineStatisticsQuery' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ querySet = webidl.converters.GPUQuerySet(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ queryIndex = webidl.converters.GPUSize32(queryIndex, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ const querySetRid = assertResource(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync(
+ "op_webgpu_compute_pass_begin_pipeline_statistics_query",
+ {
+ computePassRid,
+ querySet: querySetRid,
+ queryIndex,
+ },
+ );
+ }
+
+ endPipelineStatisticsQuery() {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'endPipelineStatisticsQuery' on 'GPUComputePassEncoder'";
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_compute_pass_end_pipeline_statistics_query", {
+ computePassRid,
+ });
+ }
+
+ /**
+ * @param {GPUQuerySet} querySet
+ * @param {number} queryIndex
+ */
+ writeTimestamp(querySet, queryIndex) {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'writeTimestamp' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ querySet = webidl.converters.GPUQuerySet(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ queryIndex = webidl.converters.GPUSize32(queryIndex, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ const querySetRid = assertResource(querySet, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, querySet, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_compute_pass_write_timestamp", {
+ computePassRid,
+ querySet: querySetRid,
+ queryIndex,
+ });
+ }
+
+ endPass() {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix = "Failed to execute 'endPass' on 'GPUComputePassEncoder'";
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const commandEncoderRid = assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ const { err } = core.jsonOpSync("op_webgpu_compute_pass_end_pass", {
+ commandEncoderRid,
+ computePassRid,
+ });
+ device.pushError(err);
+ this[_rid] = undefined;
+ }
+
+ // TODO(lucacasonato): has an overload
+ setBindGroup(
+ index,
+ bindGroup,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ ) {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'";
+ const device = assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ const bindGroupRid = assertResource(bindGroup, {
+ prefix,
+ context: "Argument 2",
+ });
+ assertDeviceMatch(device, bindGroup, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ if (dynamicOffsetsData instanceof Uint32Array) {
+ core.jsonOpSync(
+ "op_webgpu_compute_pass_set_bind_group",
+ {
+ computePassRid,
+ index,
+ bindGroup: bindGroupRid,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ },
+ dynamicOffsetsData,
+ );
+ } else {
+ dynamicOffsetsData ??= [];
+ core.jsonOpSync("op_webgpu_compute_pass_set_bind_group", {
+ computePassRid,
+ index,
+ bindGroup: bindGroupRid,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart: 0,
+ dynamicOffsetsDataLength: dynamicOffsetsData.length,
+ });
+ }
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'pushDebugGroup' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ groupLabel = webidl.converters.USVString(groupLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_compute_pass_push_debug_group", {
+ computePassRid,
+ groupLabel,
+ });
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'popDebugGroup' on 'GPUComputePassEncoder'";
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_compute_pass_pop_debug_group", {
+ computePassRid,
+ });
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPUComputePassEncoder);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPUComputePassEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ markerLabel = webidl.converters.USVString(markerLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ assertResource(this[_encoder], {
+ prefix,
+ context: "encoder referenced by this",
+ });
+ const computePassRid = assertResource(this, { prefix, context: "this" });
+ core.jsonOpSync("op_webgpu_compute_pass_insert_debug_marker", {
+ computePassRid,
+ markerLabel,
+ });
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUComputePassEncoder", GPUComputePassEncoder);
+
+ /**
+ * @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();
+ }
+
+ get executionTime() {
+ throw new Error("Not yet implemented");
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ // TODO(crowlKats): executionTime
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUCommandBuffer", GPUCommandBuffer);
+
+ /**
+ * @param {string | null} label
+ * @param {InnerGPUDevice} device
+ * @param {number} rid
+ * @returns {GPURenderBundleEncoder}
+ */
+ function createGPURenderBundleEncoder(label, device, rid) {
+ /** @type {GPURenderBundleEncoder} */
+ const bundleEncoder = webidl.createBranded(GPURenderBundleEncoder);
+ bundleEncoder[_label] = label;
+ bundleEncoder[_device] = device;
+ bundleEncoder[_rid] = rid;
+ return bundleEncoder;
+ }
+
+ class GPURenderBundleEncoder {
+ /** @type {InnerGPUDevice} */
+ [_device];
+ /** @type {number | undefined} */
+ [_rid];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ /**
+ * @param {GPURenderBundleDescriptor} descriptor
+ */
+ finish(descriptor = {}) {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix = "Failed to execute 'finish' on 'GPURenderBundleEncoder'";
+ descriptor = webidl.converters.GPURenderBundleDescriptor(descriptor, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const { rid, err } = core.jsonOpSync(
+ "op_webgpu_render_bundle_encoder_finish",
+ {
+ renderBundleEncoderRid,
+ ...descriptor,
+ },
+ );
+ device.pushError(err);
+ this[_rid] = undefined;
+
+ const renderBundle = createGPURenderBundle(
+ descriptor.label ?? null,
+ device,
+ rid,
+ );
+ device.trackResource((renderBundle));
+ return renderBundle;
+ }
+
+ // TODO(lucacasonato): has an overload
+ setBindGroup(
+ index,
+ bindGroup,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ ) {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'setBindGroup' on 'GPURenderBundleEncoder'";
+ const device = assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const bindGroupRid = assertResource(bindGroup, {
+ prefix,
+ context: "Argument 2",
+ });
+ assertDeviceMatch(device, bindGroup, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ if (dynamicOffsetsData instanceof Uint32Array) {
+ core.jsonOpSync(
+ "op_webgpu_render_bundle_encoder_set_bind_group",
+ {
+ renderBundleEncoderRid,
+ index,
+ bindGroup: bindGroupRid,
+ dynamicOffsetsDataStart,
+ dynamicOffsetsDataLength,
+ },
+ dynamicOffsetsData,
+ );
+ } else {
+ dynamicOffsetsData ??= [];
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_set_bind_group", {
+ renderBundleEncoderRid,
+ index,
+ bindGroup: bindGroupRid,
+ dynamicOffsetsData,
+ dynamicOffsetsDataStart: 0,
+ dynamicOffsetsDataLength: dynamicOffsetsData.length,
+ });
+ }
+ }
+
+ /**
+ * @param {string} groupLabel
+ */
+ pushDebugGroup(groupLabel) {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'pushDebugGroup' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ groupLabel = webidl.converters.USVString(groupLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_push_debug_group", {
+ renderBundleEncoderRid,
+ groupLabel,
+ });
+ }
+
+ popDebugGroup() {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'popDebugGroup' on 'GPURenderBundleEncoder'";
+ assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_pop_debug_group", {
+ renderBundleEncoderRid,
+ });
+ }
+
+ /**
+ * @param {string} markerLabel
+ */
+ insertDebugMarker(markerLabel) {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'insertDebugMarker' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ markerLabel = webidl.converters.USVString(markerLabel, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_push_debug_group", {
+ renderBundleEncoderRid,
+ markerLabel,
+ });
+ }
+
+ /**
+ * @param {GPURenderPipeline} pipeline
+ */
+ setPipeline(pipeline) {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'setPipeline' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ pipeline = webidl.converters.GPURenderPipeline(pipeline, {
+ prefix,
+ context: "Argument 1",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const pipelineRid = assertResource(pipeline, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, pipeline, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_set_pipeline", {
+ renderBundleEncoderRid,
+ pipeline: pipelineRid,
+ });
+ }
+
+ /**
+ * @param {GPUBuffer} buffer
+ * @param {GPUIndexFormat} indexFormat
+ * @param {number} offset
+ * @param {number} size
+ */
+ setIndexBuffer(buffer, indexFormat, offset = 0, size = 0) {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'setIndexBuffer' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ buffer = webidl.converters.GPUBuffer(buffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ indexFormat = webidl.converters.GPUIndexFormat(indexFormat, {
+ prefix,
+ context: "Argument 2",
+ });
+ offset = webidl.converters.GPUSize64(offset, {
+ prefix,
+ context: "Argument 3",
+ });
+ size = webidl.converters.GPUSize64(size, {
+ prefix,
+ context: "Argument 4",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const bufferRid = assertResource(buffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_set_index_buffer", {
+ renderBundleEncoderRid,
+ buffer: bufferRid,
+ indexFormat,
+ offset,
+ size,
+ });
+ }
+
+ /**
+ * @param {number} slot
+ * @param {GPUBuffer} buffer
+ * @param {number} offset
+ * @param {number} size
+ */
+ setVertexBuffer(slot, buffer, offset = 0, size = 0) {
+ webidl.assertBranded(this, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'setVertexBuffer' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ slot = webidl.converters.GPUSize32(slot, {
+ prefix,
+ context: "Argument 2",
+ });
+ buffer = webidl.converters.GPUBuffer(buffer, {
+ prefix,
+ context: "Argument 2",
+ });
+ offset = webidl.converters.GPUSize64(offset, {
+ prefix,
+ context: "Argument 3",
+ });
+ size = webidl.converters.GPUSize64(size, {
+ prefix,
+ context: "Argument 4",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const bufferRid = assertResource(buffer, {
+ prefix,
+ context: "Argument 2",
+ });
+ assertDeviceMatch(device, buffer, {
+ prefix,
+ resourceContext: "Argument 2",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_set_vertex_buffer", {
+ renderBundleEncoderRid,
+ slot,
+ buffer: 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, GPURenderBundleEncoder);
+ const prefix = "Failed to execute 'draw' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ vertexCount = webidl.converters.GPUSize32(vertexCount, {
+ prefix,
+ context: "Argument 1",
+ });
+ instanceCount = webidl.converters.GPUSize32(instanceCount, {
+ prefix,
+ context: "Argument 2",
+ });
+ firstVertex = webidl.converters.GPUSize32(firstVertex, {
+ prefix,
+ context: "Argument 3",
+ });
+ firstInstance = webidl.converters.GPUSize32(firstInstance, {
+ prefix,
+ context: "Argument 4",
+ });
+ assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ core.jsonOpSync("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, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'drawIndexed' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ indexCount = webidl.converters.GPUSize32(indexCount, {
+ prefix,
+ context: "Argument 1",
+ });
+ instanceCount = webidl.converters.GPUSize32(instanceCount, {
+ prefix,
+ context: "Argument 2",
+ });
+ firstIndex = webidl.converters.GPUSize32(firstIndex, {
+ prefix,
+ context: "Argument 3",
+ });
+ baseVertex = webidl.converters.GPUSignedOffset32(baseVertex, {
+ prefix,
+ context: "Argument 4",
+ });
+ firstInstance = webidl.converters.GPUSize32(firstInstance, {
+ prefix,
+ context: "Argument 5",
+ });
+ assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ core.jsonOpSync("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, GPURenderBundleEncoder);
+ const prefix =
+ "Failed to execute 'drawIndirect' on 'GPURenderBundleEncoder'";
+ webidl.requiredArguments(arguments.length, 2, { prefix });
+ indirectBuffer = webidl.converters.GPUBuffer(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ indirectOffset = webidl.converters.GPUSize64(indirectOffset, {
+ prefix,
+ context: "Argument 2",
+ });
+ const device = assertDevice(this, { prefix, context: "this" });
+ const renderBundleEncoderRid = assertResource(this, {
+ prefix,
+ context: "this",
+ });
+ const indirectBufferRid = assertResource(indirectBuffer, {
+ prefix,
+ context: "Argument 1",
+ });
+ assertDeviceMatch(device, indirectBuffer, {
+ prefix,
+ resourceContext: "Argument 1",
+ selfContext: "this",
+ });
+ core.jsonOpSync("op_webgpu_render_bundle_encoder_draw_indirect", {
+ renderBundleEncoderRid,
+ indirectBuffer: indirectBufferRid,
+ indirectOffset,
+ });
+ }
+
+ drawIndexedIndirect(_indirectBuffer, _indirectOffset) {
+ throw new Error("Not yet implemented");
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPURenderBundleEncoder", GPURenderBundleEncoder);
+
+ /**
+ * @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();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPURenderBundle", GPURenderBundle);
+
+ const _descriptor = Symbol("[[descriptor]]");
+
+ /**
+ * @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];
+
+ [_cleanup]() {
+ const rid = this[_rid];
+ if (rid !== undefined) {
+ core.close(rid);
+ /** @type {number | undefined} */
+ this[_rid] = undefined;
+ }
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ destroy() {
+ webidl.assertBranded(this, GPUQuerySet);
+ this[_cleanup]();
+ }
+
+ [Symbol.for("Deno.customInspect")](inspect) {
+ return `${this.constructor.name} ${
+ inspect({
+ label: this.label,
+ })
+ }`;
+ }
+ }
+ GPUObjectBaseMixin("GPUQuerySet", GPUQuerySet);
+
+ window.__bootstrap.webgpu = {
+ gpu: webidl.createBranded(GPU),
+ GPU,
+ GPUAdapter,
+ GPUAdapterLimits,
+ GPUAdapterFeatures,
+ GPUDevice,
+ GPUQueue,
+ GPUBuffer,
+ GPUBufferUsage,
+ GPUMapMode,
+ GPUTextureUsage,
+ GPUTexture,
+ GPUTextureView,
+ GPUSampler,
+ GPUBindGroupLayout,
+ GPUPipelineLayout,
+ GPUBindGroup,
+ GPUShaderModule,
+ GPUShaderStage,
+ GPUComputePipeline,
+ GPURenderPipeline,
+ GPUColorWrite,
+ GPUCommandEncoder,
+ GPURenderPassEncoder,
+ GPUComputePassEncoder,
+ GPUCommandBuffer,
+ GPURenderBundleEncoder,
+ GPURenderBundle,
+ GPUQuerySet,
+ GPUOutOfMemoryError,
+ GPUValidationError,
+ };
+})(this);
diff --git a/op_crates/webgpu/02_idl_types.js b/op_crates/webgpu/02_idl_types.js
new file mode 100644
index 000000000..68c3e3247
--- /dev/null
+++ b/op_crates/webgpu/02_idl_types.js
@@ -0,0 +1,1800 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+/// <reference path="../web/internal.d.ts" />
+
+"use strict";
+
+((window) => {
+ const webidl = window.__bootstrap.webidl;
+ const {
+ GPU,
+ GPUAdapter,
+ GPUAdapterLimits,
+ GPUAdapterFeatures,
+ GPUDevice,
+ GPUQueue,
+ GPUBuffer,
+ GPUBufferUsage,
+ GPUMapMode,
+ GPUTextureUsage,
+ GPUTexture,
+ GPUTextureView,
+ GPUSampler,
+ GPUBindGroupLayout,
+ GPUPipelineLayout,
+ GPUBindGroup,
+ GPUShaderModule,
+ GPUShaderStage,
+ GPUComputePipeline,
+ GPURenderPipeline,
+ GPUColorWrite,
+ GPUCommandEncoder,
+ GPURenderPassEncoder,
+ GPUComputePassEncoder,
+ GPUCommandBuffer,
+ GPURenderBundleEncoder,
+ GPURenderBundle,
+ GPUQuerySet,
+ GPUOutOfMemoryError,
+ GPUValidationError,
+ } = window.__bootstrap.webgpu;
+
+ // This needs to be initalized after all of the base classes are implmented,
+ // otherwise their converters might not be available yet.
+
+ // DICTIONARY: GPUObjectDescriptorBase
+ const dictMembersGPUObjectDescriptorBase = [
+ { key: "label", converter: webidl.converters["USVString"] },
+ ];
+ webidl.converters["GPUObjectDescriptorBase"] = webidl
+ .createDictionaryConverter(
+ "GPUObjectDescriptorBase",
+ dictMembersGPUObjectDescriptorBase,
+ );
+
+ // INTERFACE: GPUAdapterLimits
+ webidl.converters.GPUAdapterLimits = webidl.createInterfaceConverter(
+ "GPUAdapterLimits",
+ GPUAdapterLimits,
+ );
+
+ // INTERFACE: GPUAdapterFeatures
+ webidl.converters.GPUAdapterFeatures = webidl.createInterfaceConverter(
+ "GPUAdapterFeatures",
+ GPUAdapterFeatures,
+ );
+
+ // INTERFACE: GPU
+ webidl.converters.GPU = webidl.createInterfaceConverter("GPU", GPU);
+
+ // ENUM: GPUPowerPreference
+ webidl.converters["GPUPowerPreference"] = webidl.createEnumConverter(
+ "GPUPowerPreference",
+ [
+ "low-power",
+ "high-performance",
+ ],
+ );
+
+ // DICTIONARY: GPURequestAdapterOptions
+ const dictMembersGPURequestAdapterOptions = [
+ {
+ key: "powerPreference",
+ converter: webidl.converters["GPUPowerPreference"],
+ },
+ ];
+ webidl.converters["GPURequestAdapterOptions"] = webidl
+ .createDictionaryConverter(
+ "GPURequestAdapterOptions",
+ dictMembersGPURequestAdapterOptions,
+ );
+
+ // INTERFACE: GPUAdapter
+ webidl.converters.GPUAdapter = webidl.createInterfaceConverter(
+ "GPUAdapter",
+ GPUAdapter,
+ );
+
+ // ENUM: GPUFeatureName
+ webidl.converters["GPUFeatureName"] = webidl.createEnumConverter(
+ "GPUFeatureName",
+ [
+ "depth-clamping",
+ "depth24unorm-stencil8",
+ "depth32float-stencil8",
+ "pipeline-statistics-query",
+ "texture-compression-bc",
+ "timestamp-query",
+ ],
+ );
+
+ // DICTIONARY: GPUDeviceDescriptor
+ const dictMembersGPUDeviceDescriptor = [
+ {
+ key: "nonGuaranteedFeatures",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUFeatureName"],
+ ),
+ defaultValue: [],
+ },
+ {
+ key: "nonGuaranteedLimits",
+ converter: webidl.converters.any,
+ defaultValue: {},
+ },
+ ];
+ webidl.converters["GPUDeviceDescriptor"] = webidl.createDictionaryConverter(
+ "GPUDeviceDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUDeviceDescriptor,
+ );
+
+ // INTERFACE: GPUDevice
+ webidl.converters.GPUDevice = webidl.createInterfaceConverter(
+ "GPUDevice",
+ GPUDevice,
+ );
+
+ // INTERFACE: GPUBuffer
+ webidl.converters.GPUBuffer = webidl.createInterfaceConverter(
+ "GPUBuffer",
+ GPUBuffer,
+ );
+
+ // TYPEDEF: GPUSize64
+ webidl.converters["GPUSize64"] = (V, opts) =>
+ webidl.converters["unsigned long long"](V, { ...opts, enforceRange: true });
+
+ // 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,
+ );
+
+ // TYPEDEF: GPUMapModeFlags
+ webidl.converters["GPUMapModeFlags"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+ // INTERFACE: GPUMapMode
+ webidl.converters.GPUMapMode = webidl.createInterfaceConverter(
+ "GPUMapMode",
+ GPUMapMode,
+ );
+
+ // INTERFACE: GPUTexture
+ webidl.converters.GPUTexture = webidl.createInterfaceConverter(
+ "GPUTexture",
+ GPUTexture,
+ );
+
+ // TYPEDEF: GPUExtent3D
+ webidl.converters["GPUExtent3D"] = webidl.converters.any;
+
+ // TYPEDEF: GPUIntegerCoordinate
+ webidl.converters["GPUIntegerCoordinate"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+ // TYPEDEF: GPUSize32
+ webidl.converters["GPUSize32"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+ // ENUM: GPUTextureDimension
+ webidl.converters["GPUTextureDimension"] = webidl.createEnumConverter(
+ "GPUTextureDimension",
+ [
+ "1d",
+ "2d",
+ "3d",
+ ],
+ );
+
+ // ENUM: GPUTextureFormat
+ webidl.converters["GPUTextureFormat"] = webidl.createEnumConverter(
+ "GPUTextureFormat",
+ [
+ "r8unorm",
+ "r8snorm",
+ "r8uint",
+ "r8sint",
+ "r16uint",
+ "r16sint",
+ "r16float",
+ "rg8unorm",
+ "rg8snorm",
+ "rg8uint",
+ "rg8sint",
+ "r32uint",
+ "r32sint",
+ "r32float",
+ "rg16uint",
+ "rg16sint",
+ "rg16float",
+ "rgba8unorm",
+ "rgba8unorm-srgb",
+ "rgba8snorm",
+ "rgba8uint",
+ "rgba8sint",
+ "bgra8unorm",
+ "bgra8unorm-srgb",
+ "rgb9e5ufloat",
+ "rgb10a2unorm",
+ "rg11b10ufloat",
+ "rg32uint",
+ "rg32sint",
+ "rg32float",
+ "rgba16uint",
+ "rgba16sint",
+ "rgba16float",
+ "rgba32uint",
+ "rgba32sint",
+ "rgba32float",
+ "stencil8",
+ "depth16unorm",
+ "depth24plus",
+ "depth24plus-stencil8",
+ "depth32float",
+ "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",
+ "depth24unorm-stencil8",
+ "depth32float-stencil8",
+ ],
+ );
+
+ // 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,
+ },
+ ];
+ webidl.converters["GPUTextureDescriptor"] = webidl.createDictionaryConverter(
+ "GPUTextureDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUTextureDescriptor,
+ );
+
+ // INTERFACE: GPUTextureUsage
+ webidl.converters.GPUTextureUsage = webidl.createInterfaceConverter(
+ "GPUTextureUsage",
+ GPUTextureUsage,
+ );
+
+ // INTERFACE: GPUTextureView
+ webidl.converters.GPUTextureView = webidl.createInterfaceConverter(
+ "GPUTextureView",
+ GPUTextureView,
+ );
+
+ // 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,
+ );
+
+ // 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: 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["GPUFilterMode"],
+ 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,
+ );
+
+ // 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",
+ [
+ "read-only",
+ "write-only",
+ ],
+ );
+
+ // DICTIONARY: GPUStorageTextureBindingLayout
+ const dictMembersGPUStorageTextureBindingLayout = [
+ {
+ key: "access",
+ converter: webidl.converters["GPUStorageTextureAccess"],
+ required: true,
+ },
+ {
+ 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,
+ );
+
+ // INTERFACE: GPUBindGroup
+ webidl.converters.GPUBindGroup = webidl.createInterfaceConverter(
+ "GPUBindGroup",
+ GPUBindGroup,
+ );
+
+ // TYPEDEF: GPUBindingResource
+ webidl.converters["GPUBindingResource"] = webidl.converters.any;
+
+ // 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,
+ );
+
+ // 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,
+ );
+
+ // INTERFACE: GPUPipelineLayout
+ webidl.converters.GPUPipelineLayout = webidl.createInterfaceConverter(
+ "GPUPipelineLayout",
+ GPUPipelineLayout,
+ );
+
+ // DICTIONARY: GPUPipelineLayoutDescriptor
+ const dictMembersGPUPipelineLayoutDescriptor = [
+ {
+ key: "bindGroupLayouts",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUBindGroupLayout"],
+ ),
+ required: true,
+ },
+ ];
+ webidl.converters["GPUPipelineLayoutDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUPipelineLayoutDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUPipelineLayoutDescriptor,
+ );
+
+ // ENUM: GPUCompilationMessageType
+ webidl.converters["GPUCompilationMessageType"] = webidl.createEnumConverter(
+ "GPUCompilationMessageType",
+ [
+ "error",
+ "warning",
+ "info",
+ ],
+ );
+
+ // // INTERFACE: GPUCompilationMessage
+ // webidl.converters.GPUCompilationMessage = webidl.createInterfaceConverter(
+ // "GPUCompilationMessage",
+ // GPUCompilationMessage,
+ // );
+
+ // // INTERFACE: GPUCompilationInfo
+ // webidl.converters.GPUCompilationInfo = webidl.createInterfaceConverter(
+ // "GPUCompilationInfo",
+ // GPUCompilationInfo,
+ // );
+
+ // INTERFACE: GPUShaderModule
+ webidl.converters.GPUShaderModule = webidl.createInterfaceConverter(
+ "GPUShaderModule",
+ GPUShaderModule,
+ );
+
+ // DICTIONARY: GPUShaderModuleDescriptor
+ const dictMembersGPUShaderModuleDescriptor = [
+ { key: "code", converter: webidl.converters["USVString"], required: true },
+ { key: "sourceMap", converter: webidl.converters["object"] },
+ ];
+ webidl.converters["GPUShaderModuleDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUShaderModuleDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUShaderModuleDescriptor,
+ );
+
+ // DICTIONARY: GPUPipelineDescriptorBase
+ const dictMembersGPUPipelineDescriptorBase = [
+ { key: "layout", converter: webidl.converters["GPUPipelineLayout"] },
+ ];
+ webidl.converters["GPUPipelineDescriptorBase"] = webidl
+ .createDictionaryConverter(
+ "GPUPipelineDescriptorBase",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUPipelineDescriptorBase,
+ );
+
+ // DICTIONARY: GPUProgrammableStage
+ const dictMembersGPUProgrammableStage = [
+ {
+ key: "module",
+ converter: webidl.converters["GPUShaderModule"],
+ required: true,
+ },
+ {
+ key: "entryPoint",
+ converter: webidl.converters["USVString"],
+ required: true,
+ },
+ ];
+ webidl.converters["GPUProgrammableStage"] = webidl.createDictionaryConverter(
+ "GPUProgrammableStage",
+ dictMembersGPUProgrammableStage,
+ );
+
+ // INTERFACE: GPUComputePipeline
+ webidl.converters.GPUComputePipeline = webidl.createInterfaceConverter(
+ "GPUComputePipeline",
+ GPUComputePipeline,
+ );
+
+ // DICTIONARY: GPUComputePipelineDescriptor
+ const dictMembersGPUComputePipelineDescriptor = [
+ {
+ key: "compute",
+ converter: webidl.converters["GPUProgrammableStage"],
+ required: true,
+ },
+ ];
+ webidl.converters["GPUComputePipelineDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUComputePipelineDescriptor",
+ dictMembersGPUPipelineDescriptorBase,
+ dictMembersGPUComputePipelineDescriptor,
+ );
+
+ // INTERFACE: GPURenderPipeline
+ webidl.converters.GPURenderPipeline = webidl.createInterfaceConverter(
+ "GPURenderPipeline",
+ GPURenderPipeline,
+ );
+
+ // ENUM: GPUInputStepMode
+ webidl.converters["GPUInputStepMode"] = webidl.createEnumConverter(
+ "GPUInputStepMode",
+ [
+ "vertex",
+ "instance",
+ ],
+ );
+
+ // ENUM: GPUVertexFormat
+ webidl.converters["GPUVertexFormat"] = webidl.createEnumConverter(
+ "GPUVertexFormat",
+ [
+ "uchar2",
+ "uchar4",
+ "char2",
+ "char4",
+ "uchar2norm",
+ "uchar4norm",
+ "char2norm",
+ "char4norm",
+ "ushort2",
+ "ushort4",
+ "short2",
+ "short4",
+ "ushort2norm",
+ "ushort4norm",
+ "short2norm",
+ "short4norm",
+ "half2",
+ "half4",
+ "float",
+ "float2",
+ "float3",
+ "float4",
+ "uint",
+ "uint2",
+ "uint3",
+ "uint4",
+ "int",
+ "int2",
+ "int3",
+ "int4",
+ ],
+ );
+
+ // 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["GPUInputStepMode"],
+ 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"],
+ ),
+ ),
+ defaultValue: [],
+ },
+ ];
+ 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",
+ },
+ ];
+ 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"],
+ defaultValue: false,
+ },
+ {
+ key: "depthCompare",
+ converter: webidl.converters["GPUCompareFunction"],
+ defaultValue: "always",
+ },
+ {
+ key: "stencilFront",
+ converter: webidl.converters["GPUStencilFaceState"],
+ defaultValue: {},
+ },
+ {
+ key: "stencilBack",
+ converter: webidl.converters["GPUStencilFaceState"],
+ defaultValue: {},
+ },
+ {
+ 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,
+ },
+ {
+ key: "clampDepth",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+ ];
+ 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-color",
+ "one-minus-src-color",
+ "src-alpha",
+ "one-minus-src-alpha",
+ "dst-color",
+ "one-minus-dst-color",
+ "dst-alpha",
+ "one-minus-dst-alpha",
+ "src-alpha-saturated",
+ "blend-color",
+ "one-minus-blend-color",
+ ],
+ );
+
+ // 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.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"],
+ defaultValue: {},
+ },
+ {
+ key: "depthStencil",
+ converter: webidl.converters["GPUDepthStencilState"],
+ },
+ {
+ key: "multisample",
+ converter: webidl.converters["GPUMultisampleState"],
+ defaultValue: {},
+ },
+ { key: "fragment", converter: webidl.converters["GPUFragmentState"] },
+ ];
+ webidl.converters["GPURenderPipelineDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPipelineDescriptor",
+ dictMembersGPUPipelineDescriptorBase,
+ dictMembersGPURenderPipelineDescriptor,
+ );
+
+ // INTERFACE: GPUColorWrite
+ webidl.converters.GPUColorWrite = webidl.createInterfaceConverter(
+ "GPUColorWrite",
+ GPUColorWrite,
+ );
+
+ // INTERFACE: GPUCommandBuffer
+ webidl.converters.GPUCommandBuffer = webidl.createInterfaceConverter(
+ "GPUCommandBuffer",
+ GPUCommandBuffer,
+ );
+
+ // DICTIONARY: GPUCommandBufferDescriptor
+ const dictMembersGPUCommandBufferDescriptor = [];
+ webidl.converters["GPUCommandBufferDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUCommandBufferDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUCommandBufferDescriptor,
+ );
+
+ // INTERFACE: GPUCommandEncoder
+ webidl.converters.GPUCommandEncoder = webidl.createInterfaceConverter(
+ "GPUCommandEncoder",
+ GPUCommandEncoder,
+ );
+
+ // DICTIONARY: GPUCommandEncoderDescriptor
+ const dictMembersGPUCommandEncoderDescriptor = [
+ {
+ key: "measureExecutionTime",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+ ];
+ 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,
+ );
+
+ // TYPEDEF: GPUOrigin3D
+ webidl.converters["GPUOrigin3D"] = webidl.converters.any;
+
+ // 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"],
+ defaultValue: {},
+ },
+ {
+ key: "aspect",
+ converter: webidl.converters["GPUTextureAspect"],
+ defaultValue: "all",
+ },
+ ];
+ webidl.converters["GPUImageCopyTexture"] = webidl.createDictionaryConverter(
+ "GPUImageCopyTexture",
+ dictMembersGPUImageCopyTexture,
+ );
+
+ // INTERFACE: GPUComputePassEncoder
+ webidl.converters.GPUComputePassEncoder = webidl.createInterfaceConverter(
+ "GPUComputePassEncoder",
+ GPUComputePassEncoder,
+ );
+
+ // DICTIONARY: GPUComputePassDescriptor
+ const dictMembersGPUComputePassDescriptor = [];
+ webidl.converters["GPUComputePassDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPUComputePassDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPUComputePassDescriptor,
+ );
+
+ // INTERFACE: GPURenderPassEncoder
+ webidl.converters.GPURenderPassEncoder = webidl.createInterfaceConverter(
+ "GPURenderPassEncoder",
+ GPURenderPassEncoder,
+ );
+
+ // ENUM: GPUStoreOp
+ webidl.converters["GPUStoreOp"] = webidl.createEnumConverter("GPUStoreOp", [
+ "store",
+ "clear",
+ ]);
+
+ // DICTIONARY: GPURenderPassColorAttachment
+ const dictMembersGPURenderPassColorAttachment = [
+ {
+ key: "view",
+ converter: webidl.converters["GPUTextureView"],
+ required: true,
+ },
+ { key: "resolveTarget", converter: webidl.converters["GPUTextureView"] },
+ { key: "loadValue", converter: webidl.converters.any, required: true },
+ {
+ key: "storeOp",
+ converter: webidl.converters["GPUStoreOp"],
+ defaultValue: "store",
+ },
+ ];
+ webidl.converters["GPURenderPassColorAttachment"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassColorAttachment",
+ dictMembersGPURenderPassColorAttachment,
+ );
+
+ // DICTIONARY: GPURenderPassDepthStencilAttachment
+ const dictMembersGPURenderPassDepthStencilAttachment = [
+ {
+ key: "view",
+ converter: webidl.converters["GPUTextureView"],
+ required: true,
+ },
+ { key: "depthLoadValue", converter: webidl.converters.any, required: true },
+ {
+ key: "depthStoreOp",
+ converter: webidl.converters["GPUStoreOp"],
+ required: true,
+ },
+ {
+ key: "depthReadOnly",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+ {
+ key: "stencilLoadValue",
+ converter: webidl.converters.any,
+ required: true,
+ },
+ {
+ key: "stencilStoreOp",
+ converter: webidl.converters["GPUStoreOp"],
+ required: true,
+ },
+ {
+ key: "stencilReadOnly",
+ converter: webidl.converters["boolean"],
+ defaultValue: false,
+ },
+ ];
+ webidl.converters["GPURenderPassDepthStencilAttachment"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassDepthStencilAttachment",
+ dictMembersGPURenderPassDepthStencilAttachment,
+ );
+
+ // INTERFACE: GPUQuerySet
+ webidl.converters.GPUQuerySet = webidl.createInterfaceConverter(
+ "GPUQuerySet",
+ GPUQuerySet,
+ );
+
+ // DICTIONARY: GPURenderPassDescriptor
+ const dictMembersGPURenderPassDescriptor = [
+ {
+ key: "colorAttachments",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPURenderPassColorAttachment"],
+ ),
+ required: true,
+ },
+ {
+ key: "depthStencilAttachment",
+ converter: webidl.converters["GPURenderPassDepthStencilAttachment"],
+ },
+ { key: "occlusionQuerySet", converter: webidl.converters["GPUQuerySet"] },
+ ];
+ webidl.converters["GPURenderPassDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderPassDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPURenderPassDescriptor,
+ );
+
+ // ENUM: GPULoadOp
+ webidl.converters["GPULoadOp"] = webidl.createEnumConverter("GPULoadOp", [
+ "load",
+ ]);
+
+ // INTERFACE: GPURenderBundle
+ webidl.converters.GPURenderBundle = webidl.createInterfaceConverter(
+ "GPURenderBundle",
+ GPURenderBundle,
+ );
+
+ // DICTIONARY: GPURenderBundleDescriptor
+ const dictMembersGPURenderBundleDescriptor = [];
+ webidl.converters["GPURenderBundleDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderBundleDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPURenderBundleDescriptor,
+ );
+
+ // INTERFACE: GPURenderBundleEncoder
+ webidl.converters.GPURenderBundleEncoder = webidl.createInterfaceConverter(
+ "GPURenderBundleEncoder",
+ GPURenderBundleEncoder,
+ );
+
+ // DICTIONARY: GPURenderBundleEncoderDescriptor
+ const dictMembersGPURenderBundleEncoderDescriptor = [
+ {
+ key: "colorFormats",
+ converter: webidl.createSequenceConverter(
+ webidl.converters["GPUTextureFormat"],
+ ),
+ required: true,
+ },
+ {
+ key: "depthStencilFormat",
+ converter: webidl.converters["GPUTextureFormat"],
+ },
+ {
+ key: "sampleCount",
+ converter: webidl.converters["GPUSize32"],
+ defaultValue: 1,
+ },
+ ];
+ webidl.converters["GPURenderBundleEncoderDescriptor"] = webidl
+ .createDictionaryConverter(
+ "GPURenderBundleEncoderDescriptor",
+ dictMembersGPUObjectDescriptorBase,
+ dictMembersGPURenderBundleEncoderDescriptor,
+ );
+
+ // INTERFACE: GPUQueue
+ webidl.converters.GPUQueue = webidl.createInterfaceConverter(
+ "GPUQueue",
+ GPUQueue,
+ );
+
+ // ENUM: GPUQueryType
+ webidl.converters["GPUQueryType"] = webidl.createEnumConverter(
+ "GPUQueryType",
+ [
+ "occlusion",
+ "pipeline-statistics",
+ "timestamp",
+ ],
+ );
+
+ // ENUM: GPUPipelineStatisticName
+ webidl.converters["GPUPipelineStatisticName"] = webidl.createEnumConverter(
+ "GPUPipelineStatisticName",
+ [
+ "vertex-shader-invocations",
+ "clipper-invocations",
+ "clipper-primitives-out",
+ "fragment-shader-invocations",
+ "compute-shader-invocations",
+ ],
+ );
+
+ // 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"],
+ ),
+ defaultValue: [],
+ },
+ ];
+ 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,
+ // );
+
+ // ENUM: GPUErrorFilter
+ webidl.converters["GPUErrorFilter"] = webidl.createEnumConverter(
+ "GPUErrorFilter",
+ [
+ "out-of-memory",
+ "validation",
+ ],
+ );
+
+ // INTERFACE: GPUOutOfMemoryError
+ webidl.converters.GPUOutOfMemoryError = webidl.createInterfaceConverter(
+ "GPUOutOfMemoryError",
+ GPUOutOfMemoryError,
+ );
+
+ // INTERFACE: GPUValidationError
+ webidl.converters.GPUValidationError = webidl.createInterfaceConverter(
+ "GPUValidationError",
+ GPUValidationError,
+ );
+
+ // TYPEDEF: GPUError
+ webidl.converters["GPUError"] = webidl.converters.any;
+
+ // // INTERFACE: GPUUncapturedErrorEvent
+ // webidl.converters.GPUUncapturedErrorEvent = webidl.createInterfaceConverter(
+ // "GPUUncapturedErrorEvent",
+ // GPUUncapturedErrorEvent,
+ // );
+
+ // DICTIONARY: GPUUncapturedErrorEventInit
+ const dictMembersGPUUncapturedErrorEventInit = [
+ { key: "error", converter: webidl.converters["GPUError"], required: true },
+ ];
+ webidl.converters["GPUUncapturedErrorEventInit"] = webidl
+ .createDictionaryConverter(
+ "GPUUncapturedErrorEventInit",
+ // dictMembersEventInit,
+ dictMembersGPUUncapturedErrorEventInit,
+ );
+
+ // TYPEDEF: GPUBufferDynamicOffset
+ webidl.converters["GPUBufferDynamicOffset"] = (V, opts) =>
+ webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+
+ // TYPEDEF: GPUSignedOffset32
+ webidl.converters["GPUSignedOffset32"] = (V, opts) =>
+ webidl.converters["long"](V, { ...opts, enforceRange: true });
+
+ // TYPEDEF: GPUFlagsConstant
+ webidl.converters["GPUFlagsConstant"] = webidl.converters["unsigned long"];
+
+ // 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"] = webidl.converters.any;
+
+ // 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"] = webidl.converters.any;
+
+ // 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,
+ );
+
+ // DICTIONARY: GPUExtent3DDict
+ const dictMembersGPUExtent3DDict = [
+ {
+ key: "width",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 1,
+ },
+ {
+ key: "height",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 1,
+ },
+ {
+ key: "depthOrArrayLayers",
+ converter: webidl.converters["GPUIntegerCoordinate"],
+ defaultValue: 1,
+ },
+ ];
+ webidl.converters["GPUExtent3DDict"] = webidl.createDictionaryConverter(
+ "GPUExtent3DDict",
+ dictMembersGPUExtent3DDict,
+ );
+
+ webidl.converters["sequence<GPURenderBundle>"] = webidl
+ .createSequenceConverter(webidl.converters["GPURenderBundle"]);
+ webidl.converters["sequence<GPUCommandBuffer>"] = webidl
+ .createSequenceConverter(webidl.converters["GPUCommandBuffer"]);
+ webidl.converters["UVString?"] = webidl.createNullableConverter(
+ webidl.converters.USVString,
+ );
+})(this);
diff --git a/op_crates/webgpu/Cargo.toml b/op_crates/webgpu/Cargo.toml
new file mode 100644
index 000000000..92d9e01f3
--- /dev/null
+++ b/op_crates/webgpu/Cargo.toml
@@ -0,0 +1,21 @@
+# Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "deno_webgpu"
+version = "0.1.0"
+edition = "2018"
+description = "provides webgpu Web API to deno_core"
+authors = ["the Deno authors"]
+license = "MIT"
+readme = "README.md"
+repository = "https://github.com/denoland/deno"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+deno_core = { version = "0.79.0", path = "../../core" }
+tokio = { version = "1.1.1", features = ["full"] }
+serde = { version = "1.0.123", features = ["derive"] }
+wgpu-core = { version = "0.7.0", features = ["trace"] }
+wgpu-types = "0.7.0"
diff --git a/op_crates/webgpu/README.md b/op_crates/webgpu/README.md
new file mode 100644
index 000000000..2f915dcbb
--- /dev/null
+++ b/op_crates/webgpu/README.md
@@ -0,0 +1,35 @@
+# deno_webgpu
+
+This op crate implements the WebGPU API as defined in
+https://gpuweb.github.io/gpuweb/ in Deno. The implementation targets the spec
+draft as of February 22, 2021. The spec is still very much in flux. This op
+crate tries to stay up to date with the spec, but is constrained by the features
+implemented in our GPU backend library [wgpu](https://github.com/gfx-rs/wgpu).
+
+The spec is still very bare bones, and is still missing many details. As the
+spec becomes more concrete, we will implement to follow the spec more closely.
+
+In addition, setting the `DENO_WEBGPU_TRACE` environmental variable will output
+a
+[wgpu trace](https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications#tracing-infrastructure)
+to the specified directory.
+
+For testing this op crate will make use of the WebGPU conformance tests suite,
+running through our WPT runner. This will be used to validate implementation
+conformance.
+
+GitHub CI doesn't run with GPUs, so testing relies on software like DX WARP &
+Vulkan lavapipe. Currently only using DX WARP works, so tests are only run on
+Windows.
+
+## Links
+
+Specification: https://gpuweb.github.io/gpuweb/
+
+Design documents: https://github.com/gpuweb/gpuweb/tree/main/design
+
+Conformance tests suite: https://github.com/gpuweb/cts
+
+WebGPU examples for Deno: https://github.com/crowlKats/webgpu-examples
+
+wgpu-users matrix channel: https://matrix.to/#/#wgpu-users:matrix.org
diff --git a/op_crates/webgpu/binding.rs b/op_crates/webgpu/binding.rs
new file mode 100644
index 000000000..b93b223cf
--- /dev/null
+++ b/op_crates/webgpu/binding.rs
@@ -0,0 +1,362 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPUBindGroupLayout(
+ pub(crate) wgpu_core::id::BindGroupLayoutId,
+);
+impl Resource for WebGPUBindGroupLayout {
+ fn name(&self) -> Cow<str> {
+ "webGPUBindGroupLayout".into()
+ }
+}
+
+pub(crate) struct WebGPUBindGroup(pub(crate) wgpu_core::id::BindGroupId);
+impl Resource for WebGPUBindGroup {
+ fn name(&self) -> Cow<str> {
+ "webGPUBindGroup".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUBufferBindingLayout {
+ #[serde(rename = "type")]
+ kind: Option<String>,
+ has_dynamic_offset: Option<bool>,
+ min_binding_size: Option<u64>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUSamplerBindingLayout {
+ #[serde(rename = "type")]
+ kind: Option<String>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUTextureBindingLayout {
+ sample_type: Option<String>,
+ view_dimension: Option<String>,
+ multisampled: Option<bool>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUStorageTextureBindingLayout {
+ access: String,
+ format: String,
+ view_dimension: Option<String>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUBindGroupLayoutEntry {
+ binding: u32,
+ visibility: u32,
+ buffer: Option<GPUBufferBindingLayout>,
+ sampler: Option<GPUSamplerBindingLayout>,
+ texture: Option<GPUTextureBindingLayout>,
+ storage_texture: Option<GPUStorageTextureBindingLayout>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateBindGroupLayoutArgs {
+ device_rid: u32,
+ label: Option<String>,
+ entries: Vec<GPUBindGroupLayoutEntry>,
+}
+
+pub fn op_webgpu_create_bind_group_layout(
+ state: &mut OpState,
+ args: CreateBindGroupLayoutArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let mut entries = vec![];
+
+ for entry in &args.entries {
+ entries.push(wgpu_types::BindGroupLayoutEntry {
+ binding: entry.binding,
+ visibility: wgpu_types::ShaderStage::from_bits(entry.visibility).unwrap(),
+ ty: if let Some(buffer) = &entry.buffer {
+ wgpu_types::BindingType::Buffer {
+ ty: match &buffer.kind {
+ Some(kind) => match kind.as_str() {
+ "uniform" => wgpu_types::BufferBindingType::Uniform,
+ "storage" => {
+ wgpu_types::BufferBindingType::Storage { read_only: false }
+ }
+ "read-only-storage" => {
+ wgpu_types::BufferBindingType::Storage { read_only: true }
+ }
+ _ => unreachable!(),
+ },
+ None => wgpu_types::BufferBindingType::Uniform,
+ },
+ has_dynamic_offset: buffer.has_dynamic_offset.unwrap_or(false),
+ min_binding_size: if let Some(min_binding_size) =
+ buffer.min_binding_size
+ {
+ std::num::NonZeroU64::new(min_binding_size)
+ } else {
+ None
+ },
+ }
+ } else if let Some(sampler) = &entry.sampler {
+ match &sampler.kind {
+ Some(kind) => match kind.as_str() {
+ "filtering" => wgpu_types::BindingType::Sampler {
+ filtering: true,
+ comparison: false,
+ },
+ "non-filtering" => wgpu_types::BindingType::Sampler {
+ filtering: false,
+ comparison: false,
+ },
+ "comparison" => wgpu_types::BindingType::Sampler {
+ filtering: false,
+ comparison: true,
+ },
+ _ => unreachable!(),
+ },
+ None => wgpu_types::BindingType::Sampler {
+ filtering: true,
+ comparison: false,
+ },
+ }
+ } else if let Some(texture) = &entry.texture {
+ wgpu_types::BindingType::Texture {
+ sample_type: match &texture.sample_type {
+ Some(sample_type) => match sample_type.as_str() {
+ "float" => {
+ wgpu_types::TextureSampleType::Float { filterable: true }
+ }
+ "unfilterable-float" => {
+ wgpu_types::TextureSampleType::Float { filterable: false }
+ }
+ "depth" => wgpu_types::TextureSampleType::Depth,
+ "sint" => wgpu_types::TextureSampleType::Sint,
+ "uint" => wgpu_types::TextureSampleType::Uint,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::TextureSampleType::Float { filterable: true },
+ },
+ view_dimension: match &texture.view_dimension {
+ Some(view_dimension) => {
+ super::texture::serialize_dimension(view_dimension)
+ }
+ None => wgpu_types::TextureViewDimension::D2,
+ },
+ multisampled: texture.multisampled.unwrap_or(false),
+ }
+ } else if let Some(storage_texture) = &entry.storage_texture {
+ wgpu_types::BindingType::StorageTexture {
+ access: match storage_texture.access.as_str() {
+ "read-only" => wgpu_types::StorageTextureAccess::ReadOnly,
+ "write-only" => wgpu_types::StorageTextureAccess::WriteOnly,
+ _ => unreachable!(),
+ },
+ format: super::texture::serialize_texture_format(
+ &storage_texture.format,
+ )?,
+ view_dimension: match &storage_texture.view_dimension {
+ Some(view_dimension) => {
+ super::texture::serialize_dimension(view_dimension)
+ }
+ None => wgpu_types::TextureViewDimension::D2,
+ },
+ }
+ } else {
+ unreachable!()
+ },
+ count: None, // native-only
+ });
+ }
+
+ let descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
+ label: args.label.map(Cow::from),
+ entries: Cow::from(entries),
+ };
+
+ let (bind_group_layout, maybe_err) = gfx_select!(device => instance.device_create_bind_group_layout(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state
+ .resource_table
+ .add(WebGPUBindGroupLayout(bind_group_layout));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreatePipelineLayoutArgs {
+ device_rid: u32,
+ label: Option<String>,
+ bind_group_layouts: Vec<u32>,
+}
+
+pub fn op_webgpu_create_pipeline_layout(
+ state: &mut OpState,
+ args: CreatePipelineLayoutArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let mut bind_group_layouts = vec![];
+
+ for rid in &args.bind_group_layouts {
+ let bind_group_layout = state
+ .resource_table
+ .get::<WebGPUBindGroupLayout>(*rid)
+ .ok_or_else(bad_resource_id)?;
+ bind_group_layouts.push(bind_group_layout.0);
+ }
+
+ let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
+ label: args.label.map(Cow::from),
+ bind_group_layouts: Cow::from(bind_group_layouts),
+ push_constant_ranges: Default::default(),
+ };
+
+ let (pipeline_layout, maybe_err) = gfx_select!(device => instance.device_create_pipeline_layout(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state
+ .resource_table
+ .add(super::pipeline::WebGPUPipelineLayout(pipeline_layout));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUBindGroupEntry {
+ binding: u32,
+ kind: String,
+ resource: u32,
+ offset: Option<u64>,
+ size: Option<u64>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateBindGroupArgs {
+ device_rid: u32,
+ label: Option<String>,
+ layout: u32,
+ entries: Vec<GPUBindGroupEntry>,
+}
+
+pub fn op_webgpu_create_bind_group(
+ state: &mut OpState,
+ args: CreateBindGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let mut entries = vec![];
+
+ for entry in &args.entries {
+ let e = wgpu_core::binding_model::BindGroupEntry {
+ binding: entry.binding,
+ resource: match entry.kind.as_str() {
+ "GPUSampler" => {
+ let sampler_resource = state
+ .resource_table
+ .get::<super::sampler::WebGPUSampler>(entry.resource)
+ .ok_or_else(bad_resource_id)?;
+ wgpu_core::binding_model::BindingResource::Sampler(sampler_resource.0)
+ }
+ "GPUTextureView" => {
+ let texture_view_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTextureView>(entry.resource)
+ .ok_or_else(bad_resource_id)?;
+ wgpu_core::binding_model::BindingResource::TextureView(
+ texture_view_resource.0,
+ )
+ }
+ "GPUBufferBinding" => {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(entry.resource)
+ .ok_or_else(bad_resource_id)?;
+ wgpu_core::binding_model::BindingResource::Buffer(
+ wgpu_core::binding_model::BufferBinding {
+ buffer_id: buffer_resource.0,
+ offset: entry.offset.unwrap_or(0),
+ size: std::num::NonZeroU64::new(entry.size.unwrap_or(0)),
+ },
+ )
+ }
+ _ => unreachable!(),
+ },
+ };
+ entries.push(e);
+ }
+
+ let bind_group_layout = state
+ .resource_table
+ .get::<WebGPUBindGroupLayout>(args.layout)
+ .ok_or_else(bad_resource_id)?;
+
+ let descriptor = wgpu_core::binding_model::BindGroupDescriptor {
+ label: args.label.map(Cow::from),
+ layout: bind_group_layout.0,
+ entries: Cow::from(entries),
+ };
+
+ let (bind_group, maybe_err) = gfx_select!(device => instance.device_create_bind_group(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUBindGroup(bind_group));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
diff --git a/op_crates/webgpu/buffer.rs b/op_crates/webgpu/buffer.rs
new file mode 100644
index 000000000..8ff9f3115
--- /dev/null
+++ b/op_crates/webgpu/buffer.rs
@@ -0,0 +1,243 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::futures::channel::oneshot;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::OpState;
+use deno_core::ZeroCopyBuf;
+use deno_core::{BufVec, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::time::Duration;
+
+use super::error::DOMExceptionOperationError;
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPUBuffer(pub(crate) wgpu_core::id::BufferId);
+impl Resource for WebGPUBuffer {
+ fn name(&self) -> Cow<str> {
+ "webGPUBuffer".into()
+ }
+}
+
+struct WebGPUBufferMapped(*mut u8, usize);
+impl Resource for WebGPUBufferMapped {
+ fn name(&self) -> Cow<str> {
+ "webGPUBufferMapped".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateBufferArgs {
+ device_rid: u32,
+ label: Option<String>,
+ size: u64,
+ usage: u32,
+ mapped_at_creation: Option<bool>,
+}
+
+pub fn op_webgpu_create_buffer(
+ state: &mut OpState,
+ args: CreateBufferArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let descriptor = wgpu_core::resource::BufferDescriptor {
+ label: args.label.map(Cow::from),
+ size: args.size,
+ usage: wgpu_types::BufferUsage::from_bits(args.usage).unwrap(),
+ mapped_at_creation: args.mapped_at_creation.unwrap_or(false),
+ };
+
+ let (buffer, maybe_err) = gfx_select!(device => instance.device_create_buffer(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUBuffer(buffer));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BufferGetMapAsyncArgs {
+ buffer_rid: u32,
+ device_rid: u32,
+ mode: u32,
+ offset: u64,
+ size: u64,
+}
+
+pub async fn op_webgpu_buffer_get_map_async(
+ state: Rc<RefCell<OpState>>,
+ args: BufferGetMapAsyncArgs,
+ _bufs: BufVec,
+) -> Result<Value, AnyError> {
+ let (sender, receiver) = oneshot::channel::<Result<(), AnyError>>();
+
+ let device;
+ {
+ let state_ = state.borrow();
+ let instance = state_.borrow::<super::Instance>();
+ let buffer_resource = state_
+ .resource_table
+ .get::<WebGPUBuffer>(args.buffer_rid)
+ .ok_or_else(bad_resource_id)?;
+ let buffer = buffer_resource.0;
+ let device_resource = state_
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ device = device_resource.0;
+
+ let boxed_sender = Box::new(sender);
+ let sender_ptr = Box::into_raw(boxed_sender) as *mut u8;
+
+ extern "C" fn buffer_map_future_wrapper(
+ status: wgpu_core::resource::BufferMapAsyncStatus,
+ user_data: *mut u8,
+ ) {
+ let sender_ptr = user_data as *mut oneshot::Sender<Result<(), AnyError>>;
+ let boxed_sender = unsafe { Box::from_raw(sender_ptr) };
+ boxed_sender
+ .send(match status {
+ wgpu_core::resource::BufferMapAsyncStatus::Success => Ok(()),
+ _ => unreachable!(), // TODO
+ })
+ .unwrap();
+ }
+
+ // TODO(lucacasonato): error handling
+ gfx_select!(buffer => instance.buffer_map_async(
+ buffer,
+ args.offset..(args.offset + args.size),
+ wgpu_core::resource::BufferMapOperation {
+ host: match args.mode {
+ 1 => wgpu_core::device::HostMap::Read,
+ 2 => wgpu_core::device::HostMap::Write,
+ _ => unreachable!(),
+ },
+ callback: buffer_map_future_wrapper,
+ user_data: sender_ptr,
+ }
+ ))?;
+ }
+
+ let done = Rc::new(RefCell::new(false));
+ let done_ = done.clone();
+ let device_poll_fut = async move {
+ while !*done.borrow() {
+ {
+ let state = state.borrow();
+ let instance = state.borrow::<super::Instance>();
+ gfx_select!(device => instance.device_poll(device, false)).unwrap()
+ }
+ tokio::time::sleep(Duration::from_millis(10)).await;
+ }
+ Ok::<(), AnyError>(())
+ };
+
+ let receiver_fut = async move {
+ receiver.await??;
+ let mut done = done_.borrow_mut();
+ *done = true;
+ Ok::<(), AnyError>(())
+ };
+
+ tokio::try_join!(device_poll_fut, receiver_fut)?;
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BufferGetMappedRangeArgs {
+ buffer_rid: u32,
+ offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_buffer_get_mapped_range(
+ state: &mut OpState,
+ args: BufferGetMappedRangeArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource = state
+ .resource_table
+ .get::<WebGPUBuffer>(args.buffer_rid)
+ .ok_or_else(bad_resource_id)?;
+ let buffer = buffer_resource.0;
+
+ let slice_pointer = gfx_select!(buffer => instance.buffer_get_mapped_range(
+ buffer,
+ args.offset,
+ std::num::NonZeroU64::new(args.size)
+ ))
+ .map_err(|e| DOMExceptionOperationError::new(&e.to_string()))?;
+
+ let slice = unsafe {
+ std::slice::from_raw_parts_mut(slice_pointer, args.size as usize)
+ };
+ zero_copy[0].copy_from_slice(slice);
+
+ let rid = state
+ .resource_table
+ .add(WebGPUBufferMapped(slice_pointer, args.size as usize));
+
+ Ok(json!({
+ "rid": rid,
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct BufferUnmapArgs {
+ buffer_rid: u32,
+ mapped_rid: u32,
+}
+
+pub fn op_webgpu_buffer_unmap(
+ state: &mut OpState,
+ args: BufferUnmapArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let mapped_resource = state
+ .resource_table
+ .take::<WebGPUBufferMapped>(args.mapped_rid)
+ .ok_or_else(bad_resource_id)?;
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource = state
+ .resource_table
+ .get::<WebGPUBuffer>(args.buffer_rid)
+ .ok_or_else(bad_resource_id)?;
+ let buffer = buffer_resource.0;
+
+ let slice_pointer = mapped_resource.0;
+ let size = mapped_resource.1;
+
+ if let Some(buffer) = zero_copy.get(0) {
+ let slice = unsafe { std::slice::from_raw_parts_mut(slice_pointer, size) };
+ slice.copy_from_slice(&buffer);
+ }
+
+ let maybe_err = gfx_select!(buffer => instance.buffer_unmap(buffer)).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
diff --git a/op_crates/webgpu/bundle.rs b/op_crates/webgpu/bundle.rs
new file mode 100644
index 000000000..7043b0a9e
--- /dev/null
+++ b/op_crates/webgpu/bundle.rs
@@ -0,0 +1,465 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use super::error::WebGPUError;
+use super::texture::serialize_texture_format;
+
+struct WebGPURenderBundleEncoder(
+ RefCell<wgpu_core::command::RenderBundleEncoder>,
+);
+impl Resource for WebGPURenderBundleEncoder {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderBundleEncoder".into()
+ }
+}
+
+pub(crate) struct WebGPURenderBundle(pub(crate) wgpu_core::id::RenderBundleId);
+impl Resource for WebGPURenderBundle {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderBundle".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateRenderBundleEncoderArgs {
+ device_rid: u32,
+ label: Option<String>,
+ color_formats: Vec<String>,
+ depth_stencil_format: Option<String>,
+ sample_count: Option<u32>,
+}
+
+pub fn op_webgpu_create_render_bundle_encoder(
+ state: &mut OpState,
+ args: CreateRenderBundleEncoderArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let mut color_formats = vec![];
+
+ for format in &args.color_formats {
+ color_formats.push(serialize_texture_format(format)?);
+ }
+
+ let descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
+ label: args.label.map(Cow::from),
+ color_formats: Cow::from(color_formats),
+ depth_stencil_format: args
+ .depth_stencil_format
+ .map(|s| serialize_texture_format(&s))
+ .transpose()?,
+ sample_count: args.sample_count.unwrap_or(1),
+ };
+
+ 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(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from),
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderFinishArgs {
+ render_bundle_encoder_rid: u32,
+ label: Option<String>,
+}
+
+pub fn op_webgpu_render_bundle_encoder_finish(
+ state: &mut OpState,
+ args: RenderBundleEncoderFinishArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .take::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let render_bundle_encoder = Rc::try_unwrap(render_bundle_encoder_resource)
+ .ok()
+ .expect("unwrapping render_bundle_encoder_resource should succeed")
+ .0
+ .into_inner();
+ let instance = state.borrow::<super::Instance>();
+
+ let (render_bundle, maybe_err) = gfx_select!(render_bundle_encoder.parent() => instance.render_bundle_encoder_finish(
+ render_bundle_encoder,
+ &wgpu_core::command::RenderBundleDescriptor {
+ label: args.label.map(Cow::from),
+ },
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPURenderBundle(render_bundle));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetBindGroupArgs {
+ render_bundle_encoder_rid: u32,
+ index: u32,
+ bind_group: u32,
+ dynamic_offsets_data: Option<Vec<u32>>,
+ dynamic_offsets_data_start: usize,
+ dynamic_offsets_data_length: usize,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_bind_group(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetBindGroupArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let bind_group_resource = state
+ .resource_table
+ .get::<super::binding::WebGPUBindGroup>(args.bind_group)
+ .ok_or_else(bad_resource_id)?;
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ // I know this might look like it can be easily deduplicated, but it can not
+ // be due to the lifetime of the args.dynamic_offsets_data slice. Because we
+ // need to use a raw pointer here the slice can be freed before the pointer
+ // is used in wgpu_render_pass_set_bind_group. See
+ // https://matrix.to/#/!XFRnMvAfptAHthwBCx:matrix.org/$HgrlhD-Me1DwsGb8UdMu2Hqubgks8s7ILwWRwigOUAg
+ match args.dynamic_offsets_data {
+ Some(data) => unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ data.as_slice().as_ptr(),
+ args.dynamic_offsets_data_length,
+ );
+ },
+ None => {
+ let (prefix, data, suffix) = unsafe { zero_copy[0].align_to::<u32>() };
+ assert!(prefix.is_empty());
+ assert!(suffix.is_empty());
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ data[args.dynamic_offsets_data_start..].as_ptr(),
+ args.dynamic_offsets_data_length,
+ );
+ }
+ }
+ };
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderPushDebugGroupArgs {
+ render_bundle_encoder_rid: u32,
+ group_label: String,
+}
+
+pub fn op_webgpu_render_bundle_encoder_push_debug_group(
+ state: &mut OpState,
+ args: RenderBundleEncoderPushDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ let label = std::ffi::CString::new(args.group_label).unwrap();
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ label.as_ptr(),
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderPopDebugGroupArgs {
+ render_bundle_encoder_rid: u32,
+}
+
+pub fn op_webgpu_render_bundle_encoder_pop_debug_group(
+ state: &mut OpState,
+ args: RenderBundleEncoderPopDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderInsertDebugMarkerArgs {
+ render_bundle_encoder_rid: u32,
+ marker_label: String,
+}
+
+pub fn op_webgpu_render_bundle_encoder_insert_debug_marker(
+ state: &mut OpState,
+ args: RenderBundleEncoderInsertDebugMarkerArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ let label = std::ffi::CString::new(args.marker_label).unwrap();
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ label.as_ptr(),
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetPipelineArgs {
+ render_bundle_encoder_rid: u32,
+ pipeline: u32,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_pipeline(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetPipelineArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pipeline_resource = state
+ .resource_table
+ .get::<super::pipeline::WebGPURenderPipeline>(args.pipeline)
+ .ok_or_else(bad_resource_id)?;
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ render_pipeline_resource.0,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetIndexBufferArgs {
+ render_bundle_encoder_rid: u32,
+ buffer: u32,
+ index_format: String,
+ offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_index_buffer(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetIndexBufferArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.buffer)
+ .ok_or_else(bad_resource_id)?;
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ render_bundle_encoder_resource
+ .0
+ .borrow_mut()
+ .set_index_buffer(
+ buffer_resource.0,
+ super::pipeline::serialize_index_format(args.index_format),
+ args.offset,
+ std::num::NonZeroU64::new(args.size),
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderSetVertexBufferArgs {
+ render_bundle_encoder_rid: u32,
+ slot: u32,
+ buffer: u32,
+ offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer(
+ state: &mut OpState,
+ args: RenderBundleEncoderSetVertexBufferArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.buffer)
+ .ok_or_else(bad_resource_id)?;
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.slot,
+ buffer_resource.0,
+ args.offset,
+ std::num::NonZeroU64::new(args.size),
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderDrawArgs {
+ render_bundle_encoder_rid: u32,
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_bundle_encoder_draw(
+ state: &mut OpState,
+ args: RenderBundleEncoderDrawArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.vertex_count,
+ args.instance_count,
+ args.first_vertex,
+ args.first_instance,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderDrawIndexedArgs {
+ render_bundle_encoder_rid: u32,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_bundle_encoder_draw_indexed(
+ state: &mut OpState,
+ args: RenderBundleEncoderDrawIndexedArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ args.index_count,
+ args.instance_count,
+ args.first_index,
+ args.base_vertex,
+ args.first_instance,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderBundleEncoderDrawIndirectArgs {
+ render_bundle_encoder_rid: u32,
+ indirect_buffer: u32,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_render_bundle_encoder_draw_indirect(
+ state: &mut OpState,
+ args: RenderBundleEncoderDrawIndirectArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
+ .ok_or_else(bad_resource_id)?;
+ let render_bundle_encoder_resource = state
+ .resource_table
+ .get::<WebGPURenderBundleEncoder>(args.render_bundle_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(
+ &mut render_bundle_encoder_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(json!({}))
+}
diff --git a/op_crates/webgpu/command_encoder.rs b/op_crates/webgpu/command_encoder.rs
new file mode 100644
index 000000000..b91f677ee
--- /dev/null
+++ b/op_crates/webgpu/command_encoder.rs
@@ -0,0 +1,734 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPUCommandEncoder(
+ pub(crate) wgpu_core::id::CommandEncoderId,
+);
+impl Resource for WebGPUCommandEncoder {
+ fn name(&self) -> Cow<str> {
+ "webGPUCommandEncoder".into()
+ }
+}
+
+pub(crate) struct WebGPUCommandBuffer(
+ pub(crate) wgpu_core::id::CommandBufferId,
+);
+impl Resource for WebGPUCommandBuffer {
+ fn name(&self) -> Cow<str> {
+ "webGPUCommandBuffer".into()
+ }
+}
+
+fn serialize_store_op(store_op: String) -> wgpu_core::command::StoreOp {
+ match store_op.as_str() {
+ "store" => wgpu_core::command::StoreOp::Store,
+ "clear" => wgpu_core::command::StoreOp::Clear,
+ _ => unreachable!(),
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateCommandEncoderArgs {
+ device_rid: u32,
+ label: Option<String>,
+ _measure_execution_time: Option<bool>, // not yet implemented
+}
+
+pub fn op_webgpu_create_command_encoder(
+ state: &mut OpState,
+ args: CreateCommandEncoderArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let descriptor = wgpu_types::CommandEncoderDescriptor {
+ label: args.label.map(Cow::from),
+ };
+
+ let (command_encoder, maybe_err) = gfx_select!(device => instance.device_create_command_encoder(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state
+ .resource_table
+ .add(WebGPUCommandEncoder(command_encoder));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from),
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPURenderPassColorAttachment {
+ view: u32,
+ resolve_target: Option<u32>,
+ load_op: String,
+ load_value: Option<super::render_pass::GPUColor>,
+ store_op: Option<String>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPURenderPassDepthStencilAttachment {
+ view: u32,
+ depth_load_op: String,
+ depth_load_value: Option<f32>,
+ depth_store_op: String,
+ depth_read_only: Option<bool>,
+ stencil_load_op: String,
+ stencil_load_value: Option<u32>,
+ stencil_store_op: String,
+ stencil_read_only: Option<bool>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderBeginRenderPassArgs {
+ command_encoder_rid: u32,
+ label: Option<String>,
+ color_attachments: Vec<GPURenderPassColorAttachment>,
+ depth_stencil_attachment: Option<GPURenderPassDepthStencilAttachment>,
+ _occlusion_query_set: Option<u32>, // not yet implemented
+}
+
+pub fn op_webgpu_command_encoder_begin_render_pass(
+ state: &mut OpState,
+ args: CommandEncoderBeginRenderPassArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let mut color_attachments = vec![];
+
+ for color_attachment in args.color_attachments {
+ let texture_view_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTextureView>(color_attachment.view)
+ .ok_or_else(bad_resource_id)?;
+
+ let attachment = wgpu_core::command::ColorAttachmentDescriptor {
+ attachment: texture_view_resource.0,
+ resolve_target: color_attachment
+ .resolve_target
+ .map(|rid| {
+ state
+ .resource_table
+ .get::<super::texture::WebGPUTextureView>(rid)
+ .ok_or_else(bad_resource_id)
+ })
+ .transpose()?
+ .map(|texture| texture.0),
+ channel: match color_attachment.load_op.as_str() {
+ "load" => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Load,
+ store_op: color_attachment
+ .store_op
+ .map_or(wgpu_core::command::StoreOp::Store, serialize_store_op),
+ clear_value: Default::default(),
+ read_only: false,
+ },
+ "clear" => {
+ let color = color_attachment.load_value.unwrap();
+ wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Clear,
+ store_op: color_attachment
+ .store_op
+ .map_or(wgpu_core::command::StoreOp::Store, serialize_store_op),
+ clear_value: wgpu_types::Color {
+ r: color.r,
+ g: color.g,
+ b: color.b,
+ a: color.a,
+ },
+ read_only: false,
+ }
+ }
+ _ => unreachable!(),
+ },
+ };
+
+ color_attachments.push(attachment)
+ }
+
+ let mut depth_stencil_attachment = None;
+
+ if let Some(attachment) = args.depth_stencil_attachment {
+ let texture_view_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTextureView>(attachment.view)
+ .ok_or_else(bad_resource_id)?;
+
+ depth_stencil_attachment =
+ Some(wgpu_core::command::DepthStencilAttachmentDescriptor {
+ attachment: texture_view_resource.0,
+ depth: match attachment.depth_load_op.as_str() {
+ "load" => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Load,
+ store_op: serialize_store_op(attachment.depth_store_op),
+ clear_value: 0.0,
+ read_only: attachment.depth_read_only.unwrap_or(false),
+ },
+ "clear" => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Clear,
+ store_op: serialize_store_op(attachment.depth_store_op),
+ clear_value: attachment.depth_load_value.unwrap(),
+ read_only: attachment.depth_read_only.unwrap_or(false),
+ },
+ _ => unreachable!(),
+ },
+ stencil: match attachment.stencil_load_op.as_str() {
+ "load" => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Load,
+ store_op: serialize_store_op(attachment.stencil_store_op),
+ clear_value: 0,
+ read_only: attachment.stencil_read_only.unwrap_or(false),
+ },
+ "clear" => wgpu_core::command::PassChannel {
+ load_op: wgpu_core::command::LoadOp::Clear,
+ store_op: serialize_store_op(attachment.stencil_store_op),
+ clear_value: attachment.stencil_load_value.unwrap(),
+ read_only: attachment.stencil_read_only.unwrap_or(false),
+ },
+ _ => unreachable!(),
+ },
+ });
+ }
+
+ let descriptor = wgpu_core::command::RenderPassDescriptor {
+ label: args.label.map(Cow::from),
+ color_attachments: Cow::from(color_attachments),
+ depth_stencil_attachment: depth_stencil_attachment.as_ref(),
+ };
+
+ let render_pass = wgpu_core::command::RenderPass::new(
+ command_encoder_resource.0,
+ &descriptor,
+ );
+
+ let rid = state
+ .resource_table
+ .add(super::render_pass::WebGPURenderPass(RefCell::new(
+ render_pass,
+ )));
+
+ Ok(json!({
+ "rid": rid,
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderBeginComputePassArgs {
+ command_encoder_rid: u32,
+ label: Option<String>,
+}
+
+pub fn op_webgpu_command_encoder_begin_compute_pass(
+ state: &mut OpState,
+ args: CommandEncoderBeginComputePassArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ let descriptor = wgpu_core::command::ComputePassDescriptor {
+ label: args.label.map(Cow::from),
+ };
+
+ let compute_pass = wgpu_core::command::ComputePass::new(
+ command_encoder_resource.0,
+ &descriptor,
+ );
+
+ let rid = state
+ .resource_table
+ .add(super::compute_pass::WebGPUComputePass(RefCell::new(
+ compute_pass,
+ )));
+
+ Ok(json!({
+ "rid": rid,
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyBufferToBufferArgs {
+ command_encoder_rid: u32,
+ source: u32,
+ source_offset: u64,
+ destination: u32,
+ destination_offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_command_encoder_copy_buffer_to_buffer(
+ state: &mut OpState,
+ args: CommandEncoderCopyBufferToBufferArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.source)
+ .ok_or_else(bad_resource_id)?;
+ let source_buffer = source_buffer_resource.0;
+ let destination_buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.destination)
+ .ok_or_else(bad_resource_id)?;
+ let destination_buffer = destination_buffer_resource.0;
+
+ let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_buffer_to_buffer(
+ command_encoder,
+ source_buffer,
+ args.source_offset,
+ destination_buffer,
+ args.destination_offset,
+ args.size
+ )).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPUImageCopyBuffer {
+ buffer: u32,
+ offset: Option<u64>,
+ bytes_per_row: Option<u32>,
+ rows_per_image: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPUOrigin3D {
+ pub x: Option<u32>,
+ pub y: Option<u32>,
+ pub z: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPUImageCopyTexture {
+ pub texture: u32,
+ pub mip_level: Option<u32>,
+ pub origin: Option<GPUOrigin3D>,
+ pub _aspect: Option<String>, // not yet implemented
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyBufferToTextureArgs {
+ command_encoder_rid: u32,
+ source: GPUImageCopyBuffer,
+ destination: GPUImageCopyTexture,
+ copy_size: super::texture::GPUExtent3D,
+}
+
+pub fn op_webgpu_command_encoder_copy_buffer_to_texture(
+ state: &mut OpState,
+ args: CommandEncoderCopyBufferToTextureArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.source.buffer)
+ .ok_or_else(bad_resource_id)?;
+ let destination_texture_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTexture>(args.destination.texture)
+ .ok_or_else(bad_resource_id)?;
+
+ let source = wgpu_core::command::BufferCopyView {
+ buffer: source_buffer_resource.0,
+ layout: wgpu_types::TextureDataLayout {
+ offset: args.source.offset.unwrap_or(0),
+ bytes_per_row: args.source.bytes_per_row.unwrap_or(0),
+ rows_per_image: args.source.rows_per_image.unwrap_or(0),
+ },
+ };
+ let destination = wgpu_core::command::TextureCopyView {
+ texture: destination_texture_resource.0,
+ mip_level: args.destination.mip_level.unwrap_or(0),
+ origin: args
+ .destination
+ .origin
+ .map_or(Default::default(), |origin| wgpu_types::Origin3d {
+ x: origin.x.unwrap_or(0),
+ y: origin.y.unwrap_or(0),
+ z: origin.z.unwrap_or(0),
+ }),
+ };
+ let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_buffer_to_texture(
+ command_encoder,
+ &source,
+ &destination,
+ &wgpu_types::Extent3d {
+ width: args.copy_size.width.unwrap_or(1),
+ height: args.copy_size.height.unwrap_or(1),
+ depth: args.copy_size.depth.unwrap_or(1),
+ }
+ )).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyTextureToBufferArgs {
+ command_encoder_rid: u32,
+ source: GPUImageCopyTexture,
+ destination: GPUImageCopyBuffer,
+ copy_size: super::texture::GPUExtent3D,
+}
+
+pub fn op_webgpu_command_encoder_copy_texture_to_buffer(
+ state: &mut OpState,
+ args: CommandEncoderCopyTextureToBufferArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_texture_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTexture>(args.source.texture)
+ .ok_or_else(bad_resource_id)?;
+ let destination_buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.destination.buffer)
+ .ok_or_else(bad_resource_id)?;
+
+ let source = wgpu_core::command::TextureCopyView {
+ texture: source_texture_resource.0,
+ mip_level: args.source.mip_level.unwrap_or(0),
+ origin: args.source.origin.map_or(Default::default(), |origin| {
+ wgpu_types::Origin3d {
+ x: origin.x.unwrap_or(0),
+ y: origin.y.unwrap_or(0),
+ z: origin.z.unwrap_or(0),
+ }
+ }),
+ };
+ let destination = wgpu_core::command::BufferCopyView {
+ buffer: destination_buffer_resource.0,
+ layout: wgpu_types::TextureDataLayout {
+ offset: args.destination.offset.unwrap_or(0),
+ bytes_per_row: args.destination.bytes_per_row.unwrap_or(0),
+ rows_per_image: args.destination.rows_per_image.unwrap_or(0),
+ },
+ };
+ let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_texture_to_buffer(
+ command_encoder,
+ &source,
+ &destination,
+ &wgpu_types::Extent3d {
+ width: args.copy_size.width.unwrap_or(1),
+ height: args.copy_size.height.unwrap_or(1),
+ depth: args.copy_size.depth.unwrap_or(1),
+ }
+ )).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderCopyTextureToTextureArgs {
+ command_encoder_rid: u32,
+ source: GPUImageCopyTexture,
+ destination: GPUImageCopyTexture,
+ copy_size: super::texture::GPUExtent3D,
+}
+
+pub fn op_webgpu_command_encoder_copy_texture_to_texture(
+ state: &mut OpState,
+ args: CommandEncoderCopyTextureToTextureArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let source_texture_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTexture>(args.source.texture)
+ .ok_or_else(bad_resource_id)?;
+ let destination_texture_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTexture>(args.destination.texture)
+ .ok_or_else(bad_resource_id)?;
+
+ let source = wgpu_core::command::TextureCopyView {
+ texture: source_texture_resource.0,
+ mip_level: args.source.mip_level.unwrap_or(0),
+ origin: args.source.origin.map_or(Default::default(), |origin| {
+ wgpu_types::Origin3d {
+ x: origin.x.unwrap_or(0),
+ y: origin.y.unwrap_or(0),
+ z: origin.z.unwrap_or(0),
+ }
+ }),
+ };
+ let destination = wgpu_core::command::TextureCopyView {
+ texture: destination_texture_resource.0,
+ mip_level: args.destination.mip_level.unwrap_or(0),
+ origin: args
+ .destination
+ .origin
+ .map_or(Default::default(), |origin| wgpu_types::Origin3d {
+ x: origin.x.unwrap_or(0),
+ y: origin.y.unwrap_or(0),
+ z: origin.z.unwrap_or(0),
+ }),
+ };
+ let maybe_err = gfx_select!(command_encoder => instance.command_encoder_copy_texture_to_texture(
+ command_encoder,
+ &source,
+ &destination,
+ &wgpu_types::Extent3d {
+ width: args.copy_size.width.unwrap_or(1),
+ height: args.copy_size.height.unwrap_or(1),
+ depth: args.copy_size.depth.unwrap_or(1),
+ }
+ )).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderPushDebugGroupArgs {
+ command_encoder_rid: u32,
+ group_label: String,
+}
+
+pub fn op_webgpu_command_encoder_push_debug_group(
+ state: &mut OpState,
+ args: CommandEncoderPushDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+
+ let maybe_err = gfx_select!(command_encoder => instance
+ .command_encoder_push_debug_group(command_encoder, &args.group_label))
+ .err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderPopDebugGroupArgs {
+ command_encoder_rid: u32,
+}
+
+pub fn op_webgpu_command_encoder_pop_debug_group(
+ state: &mut OpState,
+ args: CommandEncoderPopDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+
+ let maybe_err = gfx_select!(command_encoder => instance.command_encoder_pop_debug_group(command_encoder)).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderInsertDebugMarkerArgs {
+ command_encoder_rid: u32,
+ marker_label: String,
+}
+
+pub fn op_webgpu_command_encoder_insert_debug_marker(
+ state: &mut OpState,
+ args: CommandEncoderInsertDebugMarkerArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+
+ let maybe_err = gfx_select!(command_encoder => instance.command_encoder_insert_debug_marker(
+ command_encoder,
+ &args.marker_label
+ )).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderWriteTimestampArgs {
+ command_encoder_rid: u32,
+ query_set: u32,
+ query_index: u32,
+}
+
+pub fn op_webgpu_command_encoder_write_timestamp(
+ state: &mut OpState,
+ args: CommandEncoderWriteTimestampArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGPUQuerySet>(args.query_set)
+ .ok_or_else(bad_resource_id)?;
+
+ let maybe_err =
+ gfx_select!(command_encoder => instance.command_encoder_write_timestamp(
+ command_encoder,
+ query_set_resource.0,
+ args.query_index
+ ))
+ .err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderResolveQuerySetArgs {
+ command_encoder_rid: u32,
+ query_set: u32,
+ first_query: u32,
+ query_count: u32,
+ destination: u32,
+ destination_offset: u64,
+}
+
+pub fn op_webgpu_command_encoder_resolve_query_set(
+ state: &mut OpState,
+ args: CommandEncoderResolveQuerySetArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let command_encoder_resource = state
+ .resource_table
+ .get::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGPUQuerySet>(args.query_set)
+ .ok_or_else(bad_resource_id)?;
+ let destination_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.destination)
+ .ok_or_else(bad_resource_id)?;
+
+ let maybe_err =
+ gfx_select!(command_encoder => instance.command_encoder_resolve_query_set(
+ command_encoder,
+ query_set_resource.0,
+ args.first_query,
+ args.query_count,
+ destination_resource.0,
+ args.destination_offset
+ ))
+ .err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CommandEncoderFinishArgs {
+ command_encoder_rid: u32,
+ label: Option<String>,
+}
+
+pub fn op_webgpu_command_encoder_finish(
+ state: &mut OpState,
+ args: CommandEncoderFinishArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .take::<WebGPUCommandEncoder>(args.command_encoder_rid)
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let instance = state.borrow::<super::Instance>();
+
+ let descriptor = wgpu_types::CommandBufferDescriptor {
+ label: args.label.map(Cow::from),
+ };
+
+ let (command_buffer, maybe_err) = gfx_select!(command_encoder => instance.command_encoder_finish(
+ command_encoder,
+ &descriptor
+ ));
+
+ let rid = state
+ .resource_table
+ .add(WebGPUCommandBuffer(command_buffer));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
diff --git a/op_crates/webgpu/compute_pass.rs b/op_crates/webgpu/compute_pass.rs
new file mode 100644
index 000000000..711bdea97
--- /dev/null
+++ b/op_crates/webgpu/compute_pass.rs
@@ -0,0 +1,365 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPUComputePass(
+ pub(crate) RefCell<wgpu_core::command::ComputePass>,
+);
+impl Resource for WebGPUComputePass {
+ fn name(&self) -> Cow<str> {
+ "webGPUComputePass".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassSetPipelineArgs {
+ compute_pass_rid: u32,
+ pipeline: u32,
+}
+
+pub fn op_webgpu_compute_pass_set_pipeline(
+ state: &mut OpState,
+ args: ComputePassSetPipelineArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pipeline_resource = state
+ .resource_table
+ .get::<super::pipeline::WebGPUComputePipeline>(args.pipeline)
+ .ok_or_else(bad_resource_id)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_set_pipeline(
+ &mut compute_pass_resource.0.borrow_mut(),
+ compute_pipeline_resource.0,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassDispatchArgs {
+ compute_pass_rid: u32,
+ x: u32,
+ y: u32,
+ z: u32,
+}
+
+pub fn op_webgpu_compute_pass_dispatch(
+ state: &mut OpState,
+ args: ComputePassDispatchArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch(
+ &mut compute_pass_resource.0.borrow_mut(),
+ args.x,
+ args.y,
+ args.z,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassDispatchIndirectArgs {
+ compute_pass_rid: u32,
+ indirect_buffer: u32,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_compute_pass_dispatch_indirect(
+ state: &mut OpState,
+ args: ComputePassDispatchIndirectArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
+ .ok_or_else(bad_resource_id)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_dispatch_indirect(
+ &mut compute_pass_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassBeginPipelineStatisticsQueryArgs {
+ compute_pass_rid: u32,
+ query_set: u32,
+ query_index: u32,
+}
+
+pub fn op_webgpu_compute_pass_begin_pipeline_statistics_query(
+ state: &mut OpState,
+ args: ComputePassBeginPipelineStatisticsQueryArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGPUQuerySet>(args.query_set)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_begin_pipeline_statistics_query(
+ &mut compute_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassEndPipelineStatisticsQueryArgs {
+ compute_pass_rid: u32,
+}
+
+pub fn op_webgpu_compute_pass_end_pipeline_statistics_query(
+ state: &mut OpState,
+ args: ComputePassEndPipelineStatisticsQueryArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_end_pipeline_statistics_query(
+ &mut compute_pass_resource.0.borrow_mut(),
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassWriteTimestampArgs {
+ compute_pass_rid: u32,
+ query_set: u32,
+ query_index: u32,
+}
+
+pub fn op_webgpu_compute_pass_write_timestamp(
+ state: &mut OpState,
+ args: ComputePassWriteTimestampArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGPUQuerySet>(args.query_set)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_write_timestamp(
+ &mut compute_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassEndPassArgs {
+ command_encoder_rid: u32,
+ compute_pass_rid: u32,
+}
+
+pub fn op_webgpu_compute_pass_end_pass(
+ state: &mut OpState,
+ args: ComputePassEndPassArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<super::command_encoder::WebGPUCommandEncoder>(
+ args.command_encoder_rid,
+ )
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let compute_pass_resource = state
+ .resource_table
+ .take::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+ let compute_pass = &compute_pass_resource.0.borrow();
+ let instance = state.borrow::<super::Instance>();
+
+ let maybe_err =
+ gfx_select!(command_encoder => instance.command_encoder_run_compute_pass(
+ command_encoder,
+ compute_pass
+ ))
+ .err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassSetBindGroupArgs {
+ compute_pass_rid: u32,
+ index: u32,
+ bind_group: u32,
+ dynamic_offsets_data: Option<Vec<u32>>,
+ dynamic_offsets_data_start: usize,
+ dynamic_offsets_data_length: usize,
+}
+
+pub fn op_webgpu_compute_pass_set_bind_group(
+ state: &mut OpState,
+ args: ComputePassSetBindGroupArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let bind_group_resource = state
+ .resource_table
+ .get::<super::binding::WebGPUBindGroup>(args.bind_group)
+ .ok_or_else(bad_resource_id)?;
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_set_bind_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ match args.dynamic_offsets_data {
+ Some(data) => data.as_ptr(),
+ None => {
+ let (prefix, data, suffix) = zero_copy[0].align_to::<u32>();
+ assert!(prefix.is_empty());
+ assert!(suffix.is_empty());
+ data[args.dynamic_offsets_data_start..].as_ptr()
+ }
+ },
+ args.dynamic_offsets_data_length,
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassPushDebugGroupArgs {
+ compute_pass_rid: u32,
+ group_label: String,
+}
+
+pub fn op_webgpu_compute_pass_push_debug_group(
+ state: &mut OpState,
+ args: ComputePassPushDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ let label = std::ffi::CString::new(args.group_label).unwrap();
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_push_debug_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassPopDebugGroupArgs {
+ compute_pass_rid: u32,
+}
+
+pub fn op_webgpu_compute_pass_pop_debug_group(
+ state: &mut OpState,
+ args: ComputePassPopDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_pop_debug_group(
+ &mut compute_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePassInsertDebugMarkerArgs {
+ compute_pass_rid: u32,
+ marker_label: String,
+}
+
+pub fn op_webgpu_compute_pass_insert_debug_marker(
+ state: &mut OpState,
+ args: ComputePassInsertDebugMarkerArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let compute_pass_resource = state
+ .resource_table
+ .get::<WebGPUComputePass>(args.compute_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ let label = std::ffi::CString::new(args.marker_label).unwrap();
+ wgpu_core::command::compute_ffi::wgpu_compute_pass_insert_debug_marker(
+ &mut compute_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(json!({}))
+}
diff --git a/op_crates/webgpu/error.rs b/op_crates/webgpu/error.rs
new file mode 100644
index 000000000..e793dabfa
--- /dev/null
+++ b/op_crates/webgpu/error.rs
@@ -0,0 +1,252 @@
+use deno_core::error::AnyError;
+use serde::Serialize;
+use std::fmt;
+use wgpu_core::binding_model::CreateBindGroupError;
+use wgpu_core::binding_model::CreateBindGroupLayoutError;
+use wgpu_core::binding_model::CreatePipelineLayoutError;
+use wgpu_core::binding_model::GetBindGroupLayoutError;
+use wgpu_core::command::CommandAllocatorError;
+use wgpu_core::command::CommandEncoderError;
+use wgpu_core::command::ComputePassError;
+use wgpu_core::command::CopyError;
+use wgpu_core::command::CreateRenderBundleError;
+use wgpu_core::command::QueryError;
+use wgpu_core::command::RenderBundleError;
+use wgpu_core::command::RenderPassError;
+use wgpu_core::device::queue::QueueSubmitError;
+use wgpu_core::device::queue::QueueWriteError;
+use wgpu_core::device::DeviceError;
+use wgpu_core::pipeline::CreateComputePipelineError;
+use wgpu_core::pipeline::CreateRenderPipelineError;
+use wgpu_core::pipeline::CreateShaderModuleError;
+use wgpu_core::resource::BufferAccessError;
+use wgpu_core::resource::CreateBufferError;
+use wgpu_core::resource::CreateQuerySetError;
+use wgpu_core::resource::CreateSamplerError;
+use wgpu_core::resource::CreateTextureError;
+use wgpu_core::resource::CreateTextureViewError;
+
+#[derive(Serialize)]
+#[serde(tag = "type", content = "value")]
+#[serde(rename_all = "kebab-case")]
+pub enum WebGPUError {
+ Lost,
+ OutOfMemory,
+ Validation(String),
+}
+
+impl From<CreateBufferError> for WebGPUError {
+ fn from(err: CreateBufferError) -> Self {
+ match err {
+ CreateBufferError::Device(err) => err.into(),
+ CreateBufferError::AccessError(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<DeviceError> for WebGPUError {
+ fn from(err: DeviceError) -> Self {
+ match err {
+ DeviceError::Lost => WebGPUError::Lost,
+ DeviceError::OutOfMemory => WebGPUError::OutOfMemory,
+ DeviceError::Invalid => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<BufferAccessError> for WebGPUError {
+ fn from(err: BufferAccessError) -> Self {
+ match err {
+ BufferAccessError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateBindGroupLayoutError> for WebGPUError {
+ fn from(err: CreateBindGroupLayoutError) -> Self {
+ match err {
+ CreateBindGroupLayoutError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreatePipelineLayoutError> for WebGPUError {
+ fn from(err: CreatePipelineLayoutError) -> Self {
+ match err {
+ CreatePipelineLayoutError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateBindGroupError> for WebGPUError {
+ fn from(err: CreateBindGroupError) -> Self {
+ match err {
+ CreateBindGroupError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<RenderBundleError> for WebGPUError {
+ fn from(err: RenderBundleError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateRenderBundleError> for WebGPUError {
+ fn from(err: CreateRenderBundleError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<CommandAllocatorError> for WebGPUError {
+ fn from(err: CommandAllocatorError) -> Self {
+ match err {
+ CommandAllocatorError::Device(err) => err.into(),
+ }
+ }
+}
+
+impl From<CopyError> for WebGPUError {
+ fn from(err: CopyError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<CommandEncoderError> for WebGPUError {
+ fn from(err: CommandEncoderError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<QueryError> for WebGPUError {
+ fn from(err: QueryError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<ComputePassError> for WebGPUError {
+ fn from(err: ComputePassError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateComputePipelineError> for WebGPUError {
+ fn from(err: CreateComputePipelineError) -> Self {
+ match err {
+ CreateComputePipelineError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<GetBindGroupLayoutError> for WebGPUError {
+ fn from(err: GetBindGroupLayoutError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateRenderPipelineError> for WebGPUError {
+ fn from(err: CreateRenderPipelineError) -> Self {
+ match err {
+ CreateRenderPipelineError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<RenderPassError> for WebGPUError {
+ fn from(err: RenderPassError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateSamplerError> for WebGPUError {
+ fn from(err: CreateSamplerError) -> Self {
+ match err {
+ CreateSamplerError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateShaderModuleError> for WebGPUError {
+ fn from(err: CreateShaderModuleError) -> Self {
+ match err {
+ CreateShaderModuleError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateTextureError> for WebGPUError {
+ fn from(err: CreateTextureError) -> Self {
+ match err {
+ CreateTextureError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<CreateTextureViewError> for WebGPUError {
+ fn from(err: CreateTextureViewError) -> Self {
+ WebGPUError::Validation(err.to_string())
+ }
+}
+
+impl From<CreateQuerySetError> for WebGPUError {
+ fn from(err: CreateQuerySetError) -> Self {
+ match err {
+ CreateQuerySetError::Device(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<QueueSubmitError> for WebGPUError {
+ fn from(err: QueueSubmitError) -> Self {
+ match err {
+ QueueSubmitError::Queue(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+impl From<QueueWriteError> for WebGPUError {
+ fn from(err: QueueWriteError) -> Self {
+ match err {
+ QueueWriteError::Queue(err) => err.into(),
+ err => WebGPUError::Validation(err.to_string()),
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct DOMExceptionOperationError {
+ pub msg: String,
+}
+
+impl DOMExceptionOperationError {
+ pub fn new(msg: &str) -> Self {
+ DOMExceptionOperationError {
+ msg: msg.to_string(),
+ }
+ }
+}
+
+impl fmt::Display for DOMExceptionOperationError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.pad(&self.msg)
+ }
+}
+
+impl std::error::Error for DOMExceptionOperationError {}
+
+pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
+ e.downcast_ref::<DOMExceptionOperationError>()
+ .map(|_| "DOMExceptionOperationError")
+}
diff --git a/op_crates/webgpu/lib.deno_webgpu.d.ts b/op_crates/webgpu/lib.deno_webgpu.d.ts
new file mode 100644
index 000000000..cd2680b3d
--- /dev/null
+++ b/op_crates/webgpu/lib.deno_webgpu.d.ts
@@ -0,0 +1,1126 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+// deno-lint-ignore-file no-explicit-any no-empty-interface
+
+/// <reference no-default-lib="true" />
+/// <reference lib="esnext" />
+
+// 8cc98b6f10b7f354473a08c3773bb1de839845b9
+
+interface GPUObjectBase {
+ label: string | null;
+}
+
+declare interface GPUObjectDescriptorBase {
+ label?: string;
+}
+
+declare class GPUAdapterLimits {
+ maxTextureDimension1D?: number;
+ maxTextureDimension2D?: number;
+ maxTextureDimension3D?: number;
+ maxTextureArrayLayers?: number;
+ maxBindGroups?: number;
+ maxDynamicUniformBuffersPerPipelineLayout?: number;
+ maxDynamicStorageBuffersPerPipelineLayout?: number;
+ maxSampledTexturesPerShaderStage?: number;
+ maxSamplersPerShaderStage?: number;
+ maxStorageBuffersPerShaderStage?: number;
+ maxStorageTexturesPerShaderStage?: number;
+ maxUniformBuffersPerShaderStage?: number;
+ maxUniformBufferBindingSize?: number;
+ maxStorageBufferBindingSize?: number;
+ maxVertexBuffers?: number;
+ maxVertexAttributes?: number;
+ maxVertexBufferArrayStride?: number;
+}
+
+declare class GPUAdapterFeatures {
+ forEach(
+ callbackfn: (
+ value: GPUFeatureName,
+ value2: GPUFeatureName,
+ set: Set<GPUFeatureName>,
+ ) => void,
+ thisArg?: any,
+ ): void;
+ has(value: GPUFeatureName): boolean;
+ size: number;
+ [
+ Symbol
+ .iterator
+ ](): IterableIterator<GPUFeatureName>;
+ entries(): IterableIterator<[GPUFeatureName, GPUFeatureName]>;
+ keys(): IterableIterator<GPUFeatureName>;
+ values(): IterableIterator<GPUFeatureName>;
+}
+
+declare class GPU {
+ requestAdapter(
+ options?: GPURequestAdapterOptions,
+ ): Promise<GPUAdapter | null>;
+}
+
+declare interface GPURequestAdapterOptions {
+ powerPreference?: GPUPowerPreference;
+}
+
+declare type GPUPowerPreference = "low-power" | "high-performance";
+
+declare class GPUAdapter {
+ readonly name: string;
+ readonly features: GPUAdapterFeatures;
+ readonly limits: GPUAdapterLimits;
+
+ requestDevice(descriptor?: GPUDeviceDescriptor): Promise<GPUDevice | null>;
+}
+
+declare interface GPUDeviceDescriptor extends GPUObjectDescriptorBase {
+ nonGuaranteedFeatures?: GPUFeatureName[];
+ nonGuaranteedLimits?: Record<string, number>;
+}
+
+declare type GPUFeatureName =
+ | "depth-clamping"
+ | "depth24unorm-stencil8"
+ | "depth32float-stencil8"
+ | "pipeline-statistics-query"
+ | "texture-compression-bc"
+ | "timestamp-query"
+ // extended from spec
+ | "mappable-primary-buffers"
+ | "sampled-texture-binding-array"
+ | "sampled-texture-array-dynamic-indexing"
+ | "sampled-texture-array-non-uniform-indexing"
+ | "unsized-binding-array"
+ | "multi-draw-indirect"
+ | "multi-draw-indirect-count"
+ | "push-constants"
+ | "address-mode-clamp-to-border"
+ | "non-fill-polygon-mode"
+ | "texture-compression-etc2"
+ | "texture-compression-astc-ldr"
+ | "texture-adapter-specific-format-features"
+ | "shader-float64"
+ | "vertex-attribute-64bit";
+
+declare class GPUDevice extends EventTarget implements GPUObjectBase {
+ label: string | null;
+
+ readonly lost: Promise<GPUDeviceLostInfo>;
+ pushErrorScope(filter: GPUErrorFilter): undefined;
+ popErrorScope(): Promise<GPUError | null>;
+ onuncapturederror:
+ | ((this: GPUDevice, ev: GPUUncapturedErrorEvent) => any)
+ | null;
+
+ readonly adapter: GPUAdapter;
+ readonly features: ReadonlyArray<GPUFeatureName>;
+ readonly limits: Record<string, number>;
+ readonly queue: GPUQueue;
+
+ destroy(): undefined;
+
+ createBuffer(descriptor: GPUBufferDescriptor): GPUBuffer;
+ createTexture(descriptor: GPUTextureDescriptor): GPUTexture;
+ createSampler(descriptor?: GPUSamplerDescriptor): GPUSampler;
+
+ createBindGroupLayout(
+ descriptor: GPUBindGroupLayoutDescriptor,
+ ): GPUBindGroupLayout;
+ createPipelineLayout(
+ descriptor: GPUPipelineLayoutDescriptor,
+ ): GPUPipelineLayout;
+ createBindGroup(descriptor: GPUBindGroupDescriptor): GPUBindGroup;
+
+ createShaderModule(descriptor: GPUShaderModuleDescriptor): GPUShaderModule;
+ createComputePipeline(
+ descriptor: GPUComputePipelineDescriptor,
+ ): GPUComputePipeline;
+ createRenderPipeline(
+ descriptor: GPURenderPipelineDescriptor,
+ ): GPURenderPipeline;
+ createComputePipelineAsync(
+ descriptor: GPUComputePipelineDescriptor,
+ ): Promise<GPUComputePipeline>;
+ createRenderPipelineAsync(
+ descriptor: GPURenderPipelineDescriptor,
+ ): Promise<GPURenderPipeline>;
+
+ createCommandEncoder(
+ descriptor?: GPUCommandEncoderDescriptor,
+ ): GPUCommandEncoder;
+ createRenderBundleEncoder(
+ descriptor: GPURenderBundleEncoderDescriptor,
+ ): GPURenderBundleEncoder;
+
+ createQuerySet(descriptor: GPUQuerySetDescriptor): GPUQuerySet;
+}
+
+declare class GPUBuffer implements GPUObjectBase {
+ label: string | null;
+
+ mapAsync(
+ mode: GPUMapModeFlags,
+ offset?: number,
+ size?: number,
+ ): Promise<undefined>;
+ getMappedRange(offset?: number, size?: number): ArrayBuffer;
+ unmap(): undefined;
+
+ destroy(): undefined;
+}
+
+declare interface GPUBufferDescriptor extends GPUObjectDescriptorBase {
+ size: number;
+ usage: GPUBufferUsageFlags;
+ mappedAtCreation?: boolean;
+}
+
+declare type GPUBufferUsageFlags = number;
+declare class GPUBufferUsage {
+ static MAP_READ: 0x0001;
+ static MAP_WRITE: 0x0002;
+ static COPY_SRC: 0x0004;
+ static COPY_DST: 0x0008;
+ static INDEX: 0x0010;
+ static VERTEX: 0x0020;
+ static UNIFORM: 0x0040;
+ static STORAGE: 0x0080;
+ static INDIRECT: 0x0100;
+ static QUERY_RESOLVE: 0x0200;
+}
+
+declare type GPUMapModeFlags = number;
+declare class GPUMapMode {
+ static READ: 0x0001;
+ static WRITE: 0x0002;
+}
+
+declare class GPUTexture implements GPUObjectBase {
+ label: string | null;
+
+ createView(descriptor?: GPUTextureViewDescriptor): GPUTextureView;
+ destroy(): undefined;
+}
+
+declare interface GPUTextureDescriptor extends GPUObjectDescriptorBase {
+ size: GPUExtent3D;
+ mipLevelCount?: number;
+ sampleCount?: number;
+ dimension?: GPUTextureDimension;
+ format: GPUTextureFormat;
+ usage: GPUTextureUsageFlags;
+}
+
+declare type GPUTextureDimension = "1d" | "2d" | "3d";
+
+declare type GPUTextureUsageFlags = number;
+declare class GPUTextureUsage {
+ static COPY_SRC: 0x01;
+ static COPY_DST: 0x02;
+ static SAMPLED: 0x04;
+ static STORAGE: 0x08;
+ static RENDER_ATTACHMENT: 0x10;
+}
+
+declare class GPUTextureView implements GPUObjectBase {
+ label: string | null;
+}
+
+declare interface GPUTextureViewDescriptor extends GPUObjectDescriptorBase {
+ format?: GPUTextureFormat;
+ dimension?: GPUTextureViewDimension;
+ aspect?: GPUTextureAspect;
+ baseMipLevel?: number;
+ mipLevelCount?: number;
+ baseArrayLayer?: number;
+ arrayLayerCount?: number;
+}
+
+declare type GPUTextureViewDimension =
+ | "1d"
+ | "2d"
+ | "2d-array"
+ | "cube"
+ | "cube-array"
+ | "3d";
+
+declare type GPUTextureAspect = "all" | "stencil-only" | "depth-only";
+
+declare type GPUTextureFormat =
+ | "r8unorm"
+ | "r8snorm"
+ | "r8uint"
+ | "r8sint"
+ | "r16uint"
+ | "r16sint"
+ | "r16float"
+ | "rg8unorm"
+ | "rg8snorm"
+ | "rg8uint"
+ | "rg8sint"
+ | "r32uint"
+ | "r32sint"
+ | "r32float"
+ | "rg16uint"
+ | "rg16sint"
+ | "rg16float"
+ | "rgba8unorm"
+ | "rgba8unorm-srgb"
+ | "rgba8snorm"
+ | "rgba8uint"
+ | "rgba8sint"
+ | "bgra8unorm"
+ | "bgra8unorm-srgb"
+ | "rgb9e5ufloat"
+ | "rgb10a2unorm"
+ | "rg11b10ufloat"
+ | "rg32uint"
+ | "rg32sint"
+ | "rg32float"
+ | "rgba16uint"
+ | "rgba16sint"
+ | "rgba16float"
+ | "rgba32uint"
+ | "rgba32sint"
+ | "rgba32float"
+ | "stencil8"
+ | "depth16unorm"
+ | "depth24plus"
+ | "depth24plus-stencil8"
+ | "depth32float"
+ | "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"
+ | "depth24unorm-stencil8"
+ | "depth32float-stencil8";
+
+declare class GPUSampler implements GPUObjectBase {
+ label: string | null;
+}
+
+declare interface GPUSamplerDescriptor extends GPUObjectDescriptorBase {
+ addressModeU?: GPUAddressMode;
+ addressModeV?: GPUAddressMode;
+ addressModeW?: GPUAddressMode;
+ magFilter?: GPUFilterMode;
+ minFilter?: GPUFilterMode;
+ mipmapFilter?: GPUFilterMode;
+ lodMinClamp?: number;
+ lodMaxClamp?: number;
+ compare?: GPUCompareFunction;
+ maxAnisotropy?: number;
+}
+
+declare type GPUAddressMode = "clamp-to-edge" | "repeat" | "mirror-repeat";
+
+declare type GPUFilterMode = "nearest" | "linear";
+
+declare type GPUCompareFunction =
+ | "never"
+ | "less"
+ | "equal"
+ | "less-equal"
+ | "greater"
+ | "not-equal"
+ | "greater-equal"
+ | "always";
+
+declare class GPUBindGroupLayout implements GPUObjectBase {
+ label: string | null;
+}
+
+declare interface GPUBindGroupLayoutDescriptor extends GPUObjectDescriptorBase {
+ entries: GPUBindGroupLayoutEntry[];
+}
+
+declare interface GPUBindGroupLayoutEntry {
+ binding: number;
+ visibility: GPUShaderStageFlags;
+
+ buffer?: GPUBufferBindingLayout;
+ sampler?: GPUSamplerBindingLayout;
+ texture?: GPUTextureBindingLayout;
+ storageTexture?: GPUStorageTextureBindingLayout;
+}
+
+declare type GPUShaderStageFlags = number;
+declare class GPUShaderStage {
+ static VERTEX: 0x1;
+ static FRAGMENT: 0x2;
+ static COMPUTE: 0x4;
+}
+
+declare interface GPUBufferBindingLayout {
+ type?: GPUBufferBindingType;
+ hasDynamicOffset?: boolean;
+ minBindingSize?: number;
+}
+
+declare type GPUBufferBindingType = "uniform" | "storage" | "read-only-storage";
+
+declare interface GPUSamplerBindingLayout {
+ type?: GPUSamplerBindingType;
+}
+
+declare type GPUSamplerBindingType =
+ | "filtering"
+ | "non-filtering"
+ | "comparison";
+
+declare interface GPUTextureBindingLayout {
+ sampleType?: GPUTextureSampleType;
+ viewDimension?: GPUTextureViewDimension;
+ multisampled?: boolean;
+}
+
+declare type GPUTextureSampleType =
+ | "float"
+ | "unfilterable-float"
+ | "depth"
+ | "sint"
+ | "uint";
+
+declare interface GPUTextureBindingLayout {
+ sampleType?: GPUTextureSampleType;
+ viewDimension?: GPUTextureViewDimension;
+ multisampled?: boolean;
+}
+
+declare type GPUStorageTextureAccess = "read-only" | "write-only";
+
+declare interface GPUStorageTextureBindingLayout {
+ access: GPUStorageTextureAccess;
+ format: GPUTextureFormat;
+ viewDimension?: GPUTextureViewDimension;
+}
+
+declare class GPUBindGroup implements GPUObjectBase {
+ label: string | null;
+}
+
+declare interface GPUBindGroupDescriptor extends GPUObjectDescriptorBase {
+ layout: GPUBindGroupLayout;
+ entries: GPUBindGroupEntry[];
+}
+
+declare type GPUBindingResource =
+ | GPUSampler
+ | GPUTextureView
+ | GPUBufferBinding;
+
+declare interface GPUBindGroupEntry {
+ binding: number;
+ resource: GPUBindingResource;
+}
+
+declare interface GPUBufferBinding {
+ buffer: GPUBuffer;
+ offset?: number;
+ size?: number;
+}
+
+declare class GPUPipelineLayout implements GPUObjectBase {
+ label: string | null;
+}
+
+declare interface GPUPipelineLayoutDescriptor extends GPUObjectDescriptorBase {
+ bindGroupLayouts: GPUBindGroupLayout[];
+}
+
+declare type GPUCompilationMessageType = "error" | "warning" | "info";
+
+declare interface GPUCompilationMessage {
+ readonly message: string;
+ readonly type: GPUCompilationMessageType;
+ readonly lineNum: number;
+ readonly linePos: number;
+}
+
+declare interface GPUCompilationInfo {
+ readonly messages: ReadonlyArray<GPUCompilationMessage>;
+}
+
+declare class GPUShaderModule implements GPUObjectBase {
+ label: string | null;
+
+ compilationInfo(): Promise<GPUCompilationInfo>;
+}
+
+declare interface GPUShaderModuleDescriptor extends GPUObjectDescriptorBase {
+ code: string;
+ sourceMap?: any;
+}
+
+declare interface GPUPipelineDescriptorBase extends GPUObjectDescriptorBase {
+ layout?: GPUPipelineLayout;
+}
+
+declare interface GPUPipelineBase {
+ getBindGroupLayout(index: number): GPUBindGroupLayout;
+}
+
+declare interface GPUProgrammableStage {
+ module: GPUShaderModule;
+ entryPoint: string;
+}
+
+declare class GPUComputePipeline implements GPUObjectBase, GPUPipelineBase {
+ label: string | null;
+
+ getBindGroupLayout(index: number): GPUBindGroupLayout;
+}
+
+declare interface GPUComputePipelineDescriptor
+ extends GPUPipelineDescriptorBase {
+ compute: GPUProgrammableStage;
+}
+
+declare class GPURenderPipeline implements GPUObjectBase, GPUPipelineBase {
+ label: string | null;
+
+ getBindGroupLayout(index: number): GPUBindGroupLayout;
+}
+
+declare interface GPURenderPipelineDescriptor
+ extends GPUPipelineDescriptorBase {
+ vertex: GPUVertexState;
+ primitive?: GPUPrimitiveState;
+ depthStencil?: GPUDepthStencilState;
+ multisample?: GPUMultisampleState;
+ fragment?: GPUFragmentState;
+}
+
+declare type GPUPrimitiveTopology =
+ | "point-list"
+ | "line-list"
+ | "line-strip"
+ | "triangle-list"
+ | "triangle-strip";
+
+declare interface GPUPrimitiveState {
+ topology?: GPUPrimitiveTopology;
+ stripIndexFormat?: GPUIndexFormat;
+ frontFace?: GPUFrontFace;
+ cullMode?: GPUCullMode;
+}
+
+declare type GPUFrontFace = "ccw" | "cw";
+
+declare type GPUCullMode = "none" | "front" | "back";
+
+declare interface GPUMultisampleState {
+ count?: number;
+ mask?: number;
+ alphaToCoverageEnabled?: boolean;
+}
+
+declare interface GPUFragmentState extends GPUProgrammableStage {
+ targets: GPUColorTargetState[];
+}
+
+declare interface GPUColorTargetState {
+ format: GPUTextureFormat;
+
+ blend?: GPUBlendState;
+ writeMask?: GPUColorWriteFlags;
+}
+
+declare interface GPUBlendState {
+ color: GPUBlendComponent;
+ alpha: GPUBlendComponent;
+}
+
+declare type GPUColorWriteFlags = number;
+declare class GPUColorWrite {
+ static RED: 0x1;
+ static GREEN: 0x2;
+ static BLUE: 0x4;
+ static ALPHA: 0x8;
+ static ALL: 0xF;
+}
+
+declare interface GPUBlendComponent {
+ srcFactor: GPUBlendFactor;
+ dstFactor: GPUBlendFactor;
+ operation: GPUBlendOperation;
+}
+
+declare type GPUBlendFactor =
+ | "zero"
+ | "one"
+ | "src-color"
+ | "one-minus-src-color"
+ | "src-alpha"
+ | "one-minus-src-alpha"
+ | "dst-color"
+ | "one-minus-dst-color"
+ | "dst-alpha"
+ | "one-minus-dst-alpha"
+ | "src-alpha-saturated"
+ | "blend-color"
+ | "one-minus-blend-color";
+
+declare type GPUBlendOperation =
+ | "add"
+ | "subtract"
+ | "reverse-subtract"
+ | "min"
+ | "max";
+
+declare interface GPUDepthStencilState {
+ format: GPUTextureFormat;
+
+ depthWriteEnabled?: boolean;
+ depthCompare?: GPUCompareFunction;
+
+ stencilFront?: GPUStencilFaceState;
+ stencilBack?: GPUStencilFaceState;
+
+ stencilReadMask?: number;
+ stencilWriteMask?: number;
+
+ depthBias?: number;
+ depthBiasSlopeScale?: number;
+ depthBiasClamp?: number;
+
+ clampDepth?: boolean;
+}
+
+declare interface GPUStencilFaceState {
+ compare?: GPUCompareFunction;
+ failOp?: GPUStencilOperation;
+ depthFailOp?: GPUStencilOperation;
+ passOp?: GPUStencilOperation;
+}
+
+declare type GPUStencilOperation =
+ | "keep"
+ | "zero"
+ | "replace"
+ | "invert"
+ | "increment-clamp"
+ | "decrement-clamp"
+ | "increment-wrap"
+ | "decrement-wrap";
+
+declare type GPUIndexFormat = "uint16" | "uint32";
+
+declare type GPUVertexFormat =
+ | "uchar2"
+ | "uchar4"
+ | "char2"
+ | "char4"
+ | "uchar2norm"
+ | "uchar4norm"
+ | "char2norm"
+ | "char4norm"
+ | "ushort2"
+ | "ushort4"
+ | "short2"
+ | "short4"
+ | "ushort2norm"
+ | "ushort4norm"
+ | "short2norm"
+ | "short4norm"
+ | "half2"
+ | "half4"
+ | "float"
+ | "float2"
+ | "float3"
+ | "float4"
+ | "uint"
+ | "uint2"
+ | "uint3"
+ | "uint4"
+ | "int"
+ | "int2"
+ | "int3"
+ | "int4";
+
+declare type GPUInputStepMode = "vertex" | "instance";
+
+declare interface GPUVertexState extends GPUProgrammableStage {
+ buffers?: (GPUVertexBufferLayout | null)[];
+}
+
+declare interface GPUVertexBufferLayout {
+ arrayStride: number;
+ stepMode?: GPUInputStepMode;
+ attributes: GPUVertexAttribute[];
+}
+
+declare interface GPUVertexAttribute {
+ format: GPUVertexFormat;
+ offset: number;
+
+ shaderLocation: number;
+}
+
+declare class GPUCommandBuffer implements GPUObjectBase {
+ label: string | null;
+
+ readonly executionTime: Promise<number>;
+}
+
+declare interface GPUCommandBufferDescriptor extends GPUObjectDescriptorBase {}
+
+declare class GPUCommandEncoder implements GPUObjectBase {
+ label: string | null;
+
+ beginRenderPass(descriptor: GPURenderPassDescriptor): GPURenderPassEncoder;
+ beginComputePass(
+ descriptor?: GPUComputePassDescriptor,
+ ): GPUComputePassEncoder;
+
+ copyBufferToBuffer(
+ source: GPUBuffer,
+ sourceOffset: number,
+ destination: GPUBuffer,
+ destinationOffset: number,
+ size: number,
+ ): undefined;
+
+ copyBufferToTexture(
+ source: GPUImageCopyBuffer,
+ destination: GPUImageCopyTexture,
+ copySize: GPUExtent3D,
+ ): undefined;
+
+ copyTextureToBuffer(
+ source: GPUImageCopyTexture,
+ destination: GPUImageCopyBuffer,
+ copySize: GPUExtent3D,
+ ): undefined;
+
+ copyTextureToTexture(
+ source: GPUImageCopyTexture,
+ destination: GPUImageCopyTexture,
+ copySize: GPUExtent3D,
+ ): undefined;
+
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+
+ writeTimestamp(querySet: GPUQuerySet, queryIndex: number): undefined;
+
+ resolveQuerySet(
+ querySet: GPUQuerySet,
+ firstQuery: number,
+ queryCount: number,
+ destination: GPUBuffer,
+ destinationOffset: number,
+ ): undefined;
+
+ finish(descriptor?: GPUCommandBufferDescriptor): GPUCommandBuffer;
+}
+
+declare interface GPUCommandEncoderDescriptor extends GPUObjectDescriptorBase {
+ measureExecutionTime?: boolean;
+}
+
+declare interface GPUImageDataLayout {
+ offset?: number;
+ bytesPerRow?: number;
+ rowsPerImage?: number;
+}
+
+declare interface GPUImageCopyBuffer extends GPUImageDataLayout {
+ buffer: GPUBuffer;
+}
+
+declare interface GPUImageCopyTexture {
+ texture: GPUTexture;
+ mipLevel?: number;
+ origin?: GPUOrigin3D;
+ aspect?: GPUTextureAspect;
+}
+
+interface GPUProgrammablePassEncoder {
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+}
+
+declare class GPUComputePassEncoder
+ implements GPUObjectBase, GPUProgrammablePassEncoder {
+ label: string | null;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+ setPipeline(pipeline: GPUComputePipeline): undefined;
+ dispatch(x: number, y?: number, z?: number): undefined;
+ dispatchIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+
+ beginPipelineStatisticsQuery(
+ querySet: GPUQuerySet,
+ queryIndex: number,
+ ): undefined;
+ endPipelineStatisticsQuery(): undefined;
+
+ writeTimestamp(querySet: GPUQuerySet, queryIndex: number): undefined;
+
+ endPass(): undefined;
+}
+
+declare interface GPUComputePassDescriptor extends GPUObjectDescriptorBase {}
+
+interface GPURenderEncoderBase {
+ setPipeline(pipeline: GPURenderPipeline): undefined;
+
+ setIndexBuffer(
+ buffer: GPUBuffer,
+ indexFormat: GPUIndexFormat,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ setVertexBuffer(
+ slot: number,
+ buffer: GPUBuffer,
+ offset?: number,
+ size?: number,
+ ): undefined;
+
+ draw(
+ vertexCount: number,
+ instanceCount?: number,
+ firstVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexed(
+ indexCount: number,
+ instanceCount?: number,
+ firstIndex?: number,
+ baseVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+
+ drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: number): undefined;
+ drawIndexedIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+}
+
+declare class GPURenderPassEncoder
+ implements GPUObjectBase, GPUProgrammablePassEncoder, GPURenderEncoderBase {
+ label: string | null;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+ pushDebugGroup(groupLabel: string): undefined;
+ popDebugGroup(): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+ setPipeline(pipeline: GPURenderPipeline): undefined;
+ setIndexBuffer(
+ buffer: GPUBuffer,
+ indexFormat: GPUIndexFormat,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ setVertexBuffer(
+ slot: number,
+ buffer: GPUBuffer,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ draw(
+ vertexCount: number,
+ instanceCount?: number,
+ firstVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexed(
+ indexCount: number,
+ instanceCount?: number,
+ firstIndex?: number,
+ baseVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: number): undefined;
+ drawIndexedIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+
+ setViewport(
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ minDepth: number,
+ maxDepth: number,
+ ): undefined;
+
+ setScissorRect(
+ x: number,
+ y: number,
+ width: number,
+ height: number,
+ ): undefined;
+
+ setBlendColor(color: GPUColor): undefined;
+ setStencilReference(reference: number): undefined;
+
+ beginOcclusionQuery(queryIndex: number): undefined;
+ endOcclusionQuery(): undefined;
+
+ beginPipelineStatisticsQuery(
+ querySet: GPUQuerySet,
+ queryIndex: number,
+ ): undefined;
+ endPipelineStatisticsQuery(): undefined;
+
+ writeTimestamp(querySet: GPUQuerySet, queryIndex: number): undefined;
+
+ executeBundles(bundles: GPURenderBundle[]): undefined;
+ endPass(): undefined;
+}
+
+declare interface GPURenderPassDescriptor extends GPUObjectDescriptorBase {
+ colorAttachments: GPURenderPassColorAttachment[];
+ depthStencilAttachment?: GPURenderPassDepthStencilAttachment;
+ occlusionQuerySet?: GPUQuerySet;
+}
+
+declare interface GPURenderPassColorAttachment {
+ view: GPUTextureView;
+ resolveTarget?: GPUTextureView;
+
+ loadValue: GPULoadOp | GPUColor;
+ storeOp?: GPUStoreOp;
+}
+
+declare interface GPURenderPassDepthStencilAttachment {
+ view: GPUTextureView;
+
+ depthLoadValue: GPULoadOp | number;
+ depthStoreOp: GPUStoreOp;
+ depthReadOnly?: boolean;
+
+ stencilLoadValue: GPULoadOp | number;
+ stencilStoreOp: GPUStoreOp;
+ stencilReadOnly?: boolean;
+}
+
+declare type GPULoadOp = "load";
+
+declare type GPUStoreOp = "store" | "clear";
+
+declare class GPURenderBundle implements GPUObjectBase {
+ label: string | null;
+}
+
+declare interface GPURenderBundleDescriptor extends GPUObjectDescriptorBase {}
+
+declare class GPURenderBundleEncoder
+ implements GPUObjectBase, GPUProgrammablePassEncoder, GPURenderEncoderBase {
+ label: string | null;
+ draw(
+ vertexCount: number,
+ instanceCount?: number,
+ firstVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexed(
+ indexCount: number,
+ instanceCount?: number,
+ firstIndex?: number,
+ baseVertex?: number,
+ firstInstance?: number,
+ ): undefined;
+ drawIndexedIndirect(
+ indirectBuffer: GPUBuffer,
+ indirectOffset: number,
+ ): undefined;
+ drawIndirect(indirectBuffer: GPUBuffer, indirectOffset: number): undefined;
+ insertDebugMarker(markerLabel: string): undefined;
+ popDebugGroup(): undefined;
+ pushDebugGroup(groupLabel: string): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsets?: number[],
+ ): undefined;
+ setBindGroup(
+ index: number,
+ bindGroup: GPUBindGroup,
+ dynamicOffsetsData: Uint32Array,
+ dynamicOffsetsDataStart: number,
+ dynamicOffsetsDataLength: number,
+ ): undefined;
+ setIndexBuffer(
+ buffer: GPUBuffer,
+ indexFormat: GPUIndexFormat,
+ offset?: number,
+ size?: number,
+ ): undefined;
+ setPipeline(pipeline: GPURenderPipeline): undefined;
+ setVertexBuffer(
+ slot: number,
+ buffer: GPUBuffer,
+ offset?: number,
+ size?: number,
+ ): undefined;
+
+ finish(descriptor?: GPURenderBundleDescriptor): GPURenderBundle;
+}
+
+declare interface GPURenderBundleEncoderDescriptor
+ extends GPUObjectDescriptorBase {
+ colorFormats: GPUTextureFormat[];
+ depthStencilFormat?: GPUTextureFormat;
+ sampleCount?: number;
+}
+
+declare class GPUQueue implements GPUObjectBase {
+ label: string | null;
+
+ submit(commandBuffers: GPUCommandBuffer[]): undefined;
+
+ onSubmittedWorkDone(): Promise<undefined>;
+
+ writeBuffer(
+ buffer: GPUBuffer,
+ bufferOffset: number,
+ data: BufferSource,
+ dataOffset?: number,
+ size?: number,
+ ): undefined;
+
+ writeTexture(
+ destination: GPUImageCopyTexture,
+ data: BufferSource,
+ dataLayout: GPUImageDataLayout,
+ size: GPUExtent3D,
+ ): undefined;
+}
+
+declare class GPUQuerySet implements GPUObjectBase {
+ label: string | null;
+
+ destroy(): undefined;
+}
+
+declare interface GPUQuerySetDescriptor extends GPUObjectDescriptorBase {
+ type: GPUQueryType;
+ count: number;
+ pipelineStatistics?: GPUPipelineStatisticName[];
+}
+
+declare type GPUQueryType = "occlusion" | "pipeline-statistics" | "timestamp";
+
+declare type GPUPipelineStatisticName =
+ | "vertex-shader-invocations"
+ | "clipper-invocations"
+ | "clipper-primitives-out"
+ | "fragment-shader-invocations"
+ | "compute-shader-invocations";
+
+declare type GPUDeviceLostReason = "destroyed";
+
+declare interface GPUDeviceLostInfo {
+ readonly reason: GPUDeviceLostReason | undefined;
+ readonly message: string;
+}
+
+declare type GPUErrorFilter = "out-of-memory" | "validation";
+
+declare class GPUOutOfMemoryError {
+ constructor();
+}
+
+declare class GPUValidationError {
+ constructor(message: string);
+ readonly message: string;
+}
+
+declare type GPUError = GPUOutOfMemoryError | GPUValidationError;
+
+declare class GPUUncapturedErrorEvent extends Event {
+ constructor(
+ type: string,
+ gpuUncapturedErrorEventInitDict: GPUUncapturedErrorEventInit,
+ );
+ readonly error: GPUError;
+}
+
+declare interface GPUUncapturedErrorEventInit extends EventInit {
+ error?: GPUError;
+}
+
+declare interface GPUColorDict {
+ r: number;
+ g: number;
+ b: number;
+ a: number;
+}
+
+declare type GPUColor = number[] | GPUColorDict;
+
+declare interface GPUOrigin3DDict {
+ x?: number;
+ y?: number;
+ z?: number;
+}
+
+declare type GPUOrigin3D = number[] | GPUOrigin3DDict;
+
+declare interface GPUExtent3DDict {
+ width?: number;
+ height?: number;
+ depth?: number;
+}
+
+declare type GPUExtent3D = number[] | GPUExtent3DDict;
diff --git a/op_crates/webgpu/lib.rs b/op_crates/webgpu/lib.rs
new file mode 100644
index 000000000..2f1769daf
--- /dev/null
+++ b/op_crates/webgpu/lib.rs
@@ -0,0 +1,541 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+#![deny(warnings)]
+
+use deno_core::error::AnyError;
+use deno_core::error::{bad_resource_id, not_supported};
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::OpState;
+use deno_core::ZeroCopyBuf;
+use deno_core::{BufVec, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::path::PathBuf;
+use std::rc::Rc;
+pub use wgpu_core;
+pub use wgpu_types;
+
+use error::DOMExceptionOperationError;
+use error::WebGPUError;
+
+#[macro_use]
+mod macros {
+ macro_rules! gfx_select {
+ ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
+ match $id.backend() {
+ #[cfg(all(not(target_arch = "wasm32"), not(any(target_os = "ios", target_os = "macos"))))]
+ wgpu_types::Backend::Vulkan => $global.$method::<wgpu_core::backend::Vulkan>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))]
+ wgpu_types::Backend::Metal => $global.$method::<wgpu_core::backend::Metal>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
+ wgpu_types::Backend::Dx12 => $global.$method::<wgpu_core::backend::Dx12>( $($param),* ),
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
+ wgpu_types::Backend::Dx11 => $global.$method::<wgpu_core::backend::Dx11>( $($param),* ),
+ #[cfg(any(target_arch = "wasm32", all(unix, not(any(target_os = "ios", target_os = "macos")))))]
+ wgpu_types::Backend::Gl => $global.$method::<wgpu_core::backend::Gl>( $($param),+ ),
+ other => panic!("Unexpected backend {:?}", other),
+ }
+ };
+ }
+}
+
+pub mod binding;
+pub mod buffer;
+pub mod bundle;
+pub mod command_encoder;
+pub mod compute_pass;
+pub mod error;
+pub mod pipeline;
+pub mod queue;
+pub mod render_pass;
+pub mod sampler;
+pub mod shader;
+pub mod texture;
+
+pub struct Unstable(pub bool);
+
+fn check_unstable(state: &OpState, api_name: &str) {
+ let unstable = state.borrow::<Unstable>();
+
+ if !unstable.0 {
+ eprintln!(
+ "Unstable API '{}'. The --unstable flag must be provided.",
+ api_name
+ );
+ std::process::exit(70);
+ }
+}
+
+type Instance = wgpu_core::hub::Global<wgpu_core::hub::IdentityManagerFactory>;
+
+struct WebGPUAdapter(wgpu_core::id::AdapterId);
+impl Resource for WebGPUAdapter {
+ fn name(&self) -> Cow<str> {
+ "webGPUAdapter".into()
+ }
+}
+
+struct WebGPUDevice(wgpu_core::id::DeviceId);
+impl Resource for WebGPUDevice {
+ fn name(&self) -> Cow<str> {
+ "webGPUDevice".into()
+ }
+}
+
+struct WebGPUQuerySet(wgpu_core::id::QuerySetId);
+impl Resource for WebGPUQuerySet {
+ fn name(&self) -> Cow<str> {
+ "webGPUQuerySet".into()
+ }
+}
+
+/// Execute this crates' JS source files.
+pub fn init(isolate: &mut deno_core::JsRuntime) {
+ let files = vec![
+ (
+ "deno:op_crates/webgpu/01_webgpu.js",
+ include_str!("01_webgpu.js"),
+ ),
+ (
+ "deno:op_crates/webgpu/02_idl_types.js",
+ include_str!("02_idl_types.js"),
+ ),
+ ];
+ for (url, source_code) in files {
+ isolate.execute(url, source_code).unwrap();
+ }
+}
+
+pub fn get_declaration() -> PathBuf {
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_webgpu.d.ts")
+}
+
+fn deserialize_features(features: &wgpu_types::Features) -> Vec<&str> {
+ let mut return_features: Vec<&str> = vec![];
+
+ if features.contains(wgpu_types::Features::DEPTH_CLAMPING) {
+ return_features.push("depth-clamping");
+ }
+ if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
+ return_features.push("pipeline-statistics-query");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
+ return_features.push("texture-compression-bc");
+ }
+ if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
+ return_features.push("timestamp-query");
+ }
+
+ // extended from spec
+ if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
+ return_features.push("mappable-primary-buffers");
+ }
+ if features.contains(wgpu_types::Features::SAMPLED_TEXTURE_BINDING_ARRAY) {
+ return_features.push("sampled-texture-binding-array");
+ }
+ if features
+ .contains(wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING)
+ {
+ return_features.push("sampled-texture-array-dynamic-indexing");
+ }
+ if features
+ .contains(wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING)
+ {
+ return_features.push("sampled-texture-array-non-uniform-indexing");
+ }
+ if features.contains(wgpu_types::Features::UNSIZED_BINDING_ARRAY) {
+ return_features.push("unsized-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_BORDER) {
+ return_features.push("address-mode-clamp-to-border");
+ }
+ if features.contains(wgpu_types::Features::NON_FILL_POLYGON_MODE) {
+ return_features.push("non-fill-polygon-mode");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
+ return_features.push("texture-compression-etc2");
+ }
+ if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR) {
+ return_features.push("texture-compression-astc-ldr");
+ }
+ if features
+ .contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES)
+ {
+ return_features.push("texture-adapter-specific-format-features");
+ }
+ if features.contains(wgpu_types::Features::SHADER_FLOAT64) {
+ return_features.push("shader-float64");
+ }
+ if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
+ return_features.push("vertex-attribute-64bit");
+ }
+
+ return_features
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RequestAdapterArgs {
+ power_preference: Option<String>,
+}
+
+pub async fn op_webgpu_request_adapter(
+ state: Rc<RefCell<OpState>>,
+ args: RequestAdapterArgs,
+ _bufs: BufVec,
+) -> Result<Value, AnyError> {
+ let mut state = state.borrow_mut();
+ check_unstable(&state, "navigator.gpu.requestAdapter");
+ let instance = state.borrow::<Instance>();
+
+ let descriptor = wgpu_core::instance::RequestAdapterOptions {
+ power_preference: match args.power_preference {
+ Some(power_preference) => match power_preference.as_str() {
+ "low-power" => wgpu_types::PowerPreference::LowPower,
+ "high-performance" => wgpu_types::PowerPreference::HighPerformance,
+ _ => unreachable!(),
+ },
+ None => Default::default(),
+ },
+ compatible_surface: None, // windowless
+ };
+ let res = instance.request_adapter(
+ &descriptor,
+ wgpu_core::instance::AdapterInputs::Mask(
+ wgpu_types::BackendBit::PRIMARY,
+ |_| std::marker::PhantomData,
+ ),
+ );
+
+ let adapter = match res {
+ Ok(adapter) => adapter,
+ Err(err) => {
+ return Ok(json!({
+ "err": err.to_string()
+ }))
+ }
+ };
+ let name = gfx_select!(adapter => instance.adapter_get_info(adapter))?.name;
+ let adapter_features =
+ gfx_select!(adapter => instance.adapter_features(adapter))?;
+ let features = deserialize_features(&adapter_features);
+ let adapter_limits =
+ gfx_select!(adapter => instance.adapter_limits(adapter))?;
+
+ let limits = json!({
+ "maxBindGroups": adapter_limits.max_bind_groups,
+ "maxDynamicUniformBuffersPerPipelineLayout": adapter_limits.max_dynamic_uniform_buffers_per_pipeline_layout,
+ "maxDynamicStorageBuffersPerPipelineLayout": adapter_limits.max_dynamic_storage_buffers_per_pipeline_layout,
+ "maxSampledTexturesPerShaderStage": adapter_limits.max_sampled_textures_per_shader_stage,
+ "maxSamplersPerShaderStage": adapter_limits.max_samplers_per_shader_stage,
+ "maxStorageBuffersPerShaderStage": adapter_limits.max_storage_buffers_per_shader_stage,
+ "maxStorageTexturesPerShaderStage": adapter_limits.max_storage_textures_per_shader_stage,
+ "maxUniformBuffersPerShaderStage": adapter_limits.max_uniform_buffers_per_shader_stage,
+ "maxUniformBufferBindingSize": adapter_limits.max_uniform_buffer_binding_size
+ });
+
+ let rid = state.resource_table.add(WebGPUAdapter(adapter));
+
+ Ok(json!({
+ "rid": rid,
+ "name": name,
+ "features": features,
+ "limits": limits
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPULimits {
+ _max_texture_dimension1d: Option<u32>,
+ _max_texture_dimension2d: Option<u32>,
+ _max_texture_dimension3d: Option<u32>,
+ _max_texture_array_layers: Option<u32>,
+ max_bind_groups: Option<u32>,
+ max_dynamic_uniform_buffers_per_pipeline_layout: Option<u32>,
+ max_dynamic_storage_buffers_per_pipeline_layout: Option<u32>,
+ max_sampled_textures_per_shader_stage: Option<u32>,
+ max_samplers_per_shader_stage: Option<u32>,
+ max_storage_buffers_per_shader_stage: Option<u32>,
+ max_storage_textures_per_shader_stage: Option<u32>,
+ max_uniform_buffers_per_shader_stage: Option<u32>,
+ max_uniform_buffer_binding_size: Option<u32>,
+ _max_storage_buffer_binding_size: Option<u32>,
+ _max_vertex_buffers: Option<u32>,
+ _max_vertex_attributes: Option<u32>,
+ _max_vertex_buffer_array_stride: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RequestDeviceArgs {
+ adapter_rid: u32,
+ label: Option<String>,
+ non_guaranteed_features: Option<Vec<String>>,
+ non_guaranteed_limits: Option<GPULimits>,
+}
+
+pub async fn op_webgpu_request_device(
+ state: Rc<RefCell<OpState>>,
+ args: RequestDeviceArgs,
+ _bufs: BufVec,
+) -> Result<Value, AnyError> {
+ let mut state = state.borrow_mut();
+ let adapter_resource = state
+ .resource_table
+ .get::<WebGPUAdapter>(args.adapter_rid)
+ .ok_or_else(bad_resource_id)?;
+ let adapter = adapter_resource.0;
+ let instance = state.borrow::<Instance>();
+
+ let mut features: wgpu_types::Features = wgpu_types::Features::empty();
+
+ if let Some(passed_features) = args.non_guaranteed_features {
+ if passed_features.contains(&"depth-clamping".to_string()) {
+ features.set(wgpu_types::Features::DEPTH_CLAMPING, true);
+ }
+ if passed_features.contains(&"pipeline-statistics-query".to_string()) {
+ features.set(wgpu_types::Features::PIPELINE_STATISTICS_QUERY, true);
+ }
+ if passed_features.contains(&"texture-compression-bc".to_string()) {
+ features.set(wgpu_types::Features::TEXTURE_COMPRESSION_BC, true);
+ }
+ if passed_features.contains(&"timestamp-query".to_string()) {
+ features.set(wgpu_types::Features::TIMESTAMP_QUERY, true);
+ }
+
+ // extended from spec
+ if passed_features.contains(&"mappable-primary-buffers".to_string()) {
+ features.set(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS, true);
+ }
+ if passed_features.contains(&"sampled-texture-binding-array".to_string()) {
+ features.set(wgpu_types::Features::SAMPLED_TEXTURE_BINDING_ARRAY, true);
+ }
+ if passed_features
+ .contains(&"sampled-texture-array-dynamic-indexing".to_string())
+ {
+ features.set(
+ wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
+ true,
+ );
+ }
+ if passed_features
+ .contains(&"sampled-texture-array-non-uniform-indexing".to_string())
+ {
+ features.set(
+ wgpu_types::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
+ true,
+ );
+ }
+ if passed_features.contains(&"unsized-binding-array".to_string()) {
+ features.set(wgpu_types::Features::UNSIZED_BINDING_ARRAY, true);
+ }
+ if passed_features.contains(&"multi-draw-indirect".to_string()) {
+ features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT, true);
+ }
+ if passed_features.contains(&"multi-draw-indirect-count".to_string()) {
+ features.set(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT, true);
+ }
+ if passed_features.contains(&"push-constants".to_string()) {
+ features.set(wgpu_types::Features::PUSH_CONSTANTS, true);
+ }
+ if passed_features.contains(&"address-mode-clamp-to-border".to_string()) {
+ features.set(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true);
+ }
+ if passed_features.contains(&"non-fill-polygon-mode".to_string()) {
+ features.set(wgpu_types::Features::NON_FILL_POLYGON_MODE, true);
+ }
+ if passed_features.contains(&"texture-compression-etc2".to_string()) {
+ features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2, true);
+ }
+ if passed_features.contains(&"texture-compression-astc-ldr".to_string()) {
+ features.set(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_LDR, true);
+ }
+ if passed_features
+ .contains(&"texture-adapter-specific-format-features".to_string())
+ {
+ features.set(
+ wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
+ true,
+ );
+ }
+ if passed_features.contains(&"shader-float64".to_string()) {
+ features.set(wgpu_types::Features::SHADER_FLOAT64, true);
+ }
+ if passed_features.contains(&"vertex-attribute-64bit".to_string()) {
+ features.set(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT, true);
+ }
+ }
+
+ let descriptor = wgpu_types::DeviceDescriptor {
+ label: args.label.map(Cow::from),
+ features,
+ limits: args
+ .non_guaranteed_limits
+ .map_or(Default::default(), |limits| wgpu_types::Limits {
+ max_bind_groups: limits.max_bind_groups.unwrap_or(4),
+ max_dynamic_uniform_buffers_per_pipeline_layout: limits
+ .max_dynamic_uniform_buffers_per_pipeline_layout
+ .unwrap_or(8),
+ max_dynamic_storage_buffers_per_pipeline_layout: limits
+ .max_dynamic_storage_buffers_per_pipeline_layout
+ .unwrap_or(4),
+ max_sampled_textures_per_shader_stage: limits
+ .max_sampled_textures_per_shader_stage
+ .unwrap_or(16),
+ max_samplers_per_shader_stage: limits
+ .max_samplers_per_shader_stage
+ .unwrap_or(16),
+ max_storage_buffers_per_shader_stage: limits
+ .max_storage_buffers_per_shader_stage
+ .unwrap_or(4),
+ max_storage_textures_per_shader_stage: limits
+ .max_storage_textures_per_shader_stage
+ .unwrap_or(4),
+ max_uniform_buffers_per_shader_stage: limits
+ .max_uniform_buffers_per_shader_stage
+ .unwrap_or(12),
+ max_uniform_buffer_binding_size: limits
+ .max_uniform_buffer_binding_size
+ .unwrap_or(16384),
+ max_push_constant_size: 0,
+ }),
+ };
+
+ let (device, maybe_err) = gfx_select!(adapter => instance.adapter_request_device(
+ adapter,
+ &descriptor,
+ std::env::var("DENO_WEBGPU_TRACE").ok().as_ref().map(std::path::Path::new),
+ std::marker::PhantomData
+ ));
+ if let Some(err) = maybe_err {
+ return Err(DOMExceptionOperationError::new(&err.to_string()).into());
+ }
+
+ let device_features =
+ gfx_select!(device => instance.device_features(device))?;
+ let features = deserialize_features(&device_features);
+ let limits = gfx_select!(device => instance.device_limits(device))?;
+ let json_limits = json!({
+ "maxBindGroups": limits.max_bind_groups,
+ "maxDynamicUniformBuffersPerPipelineLayout": limits.max_dynamic_uniform_buffers_per_pipeline_layout,
+ "maxDynamicStorageBuffersPerPipelineLayout": limits.max_dynamic_storage_buffers_per_pipeline_layout,
+ "maxSampledTexturesPerShaderStage": limits.max_sampled_textures_per_shader_stage,
+ "maxSamplersPerShaderStage": limits.max_samplers_per_shader_stage,
+ "maxStorageBuffersPerShaderStage": limits.max_storage_buffers_per_shader_stage,
+ "maxStorageTexturesPerShaderStage": limits.max_storage_textures_per_shader_stage,
+ "maxUniformBuffersPerShaderStage": limits.max_uniform_buffers_per_shader_stage,
+ "maxUniformBufferBindingSize": limits.max_uniform_buffer_binding_size,
+ });
+
+ let rid = state.resource_table.add(WebGPUDevice(device));
+
+ Ok(json!({
+ "rid": rid,
+ "features": features,
+ "limits": json_limits,
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateQuerySetArgs {
+ device_rid: u32,
+ _label: Option<String>, // not yet implemented
+ #[serde(rename = "type")]
+ kind: String,
+ count: u32,
+ pipeline_statistics: Option<Vec<String>>,
+}
+
+pub fn op_webgpu_create_query_set(
+ state: &mut OpState,
+ args: CreateQuerySetArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let device_resource = state
+ .resource_table
+ .get::<WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+ let instance = &state.borrow::<Instance>();
+
+ let descriptor = wgpu_types::QuerySetDescriptor {
+ ty: match args.kind.as_str() {
+ "pipeline-statistics" => {
+ let mut pipeline_statistics_names =
+ wgpu_types::PipelineStatisticsTypes::empty();
+
+ if let Some(pipeline_statistics) = args.pipeline_statistics {
+ if pipeline_statistics
+ .contains(&"vertex-shader-invocations".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS,
+ true,
+ );
+ }
+ if pipeline_statistics.contains(&"clipper-invocations".to_string()) {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::CLIPPER_INVOCATIONS,
+ true,
+ );
+ }
+ if pipeline_statistics.contains(&"clipper-primitives-out".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::CLIPPER_PRIMITIVES_OUT,
+ true,
+ );
+ }
+ if pipeline_statistics
+ .contains(&"fragment-shader-invocations".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS,
+ true,
+ );
+ }
+ if pipeline_statistics
+ .contains(&"compute-shader-invocations".to_string())
+ {
+ pipeline_statistics_names.set(
+ wgpu_types::PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS,
+ true,
+ );
+ }
+ };
+
+ wgpu_types::QueryType::PipelineStatistics(pipeline_statistics_names)
+ }
+ "occlusion" => return Err(not_supported()),
+ "timestamp" => wgpu_types::QueryType::Timestamp,
+ _ => unreachable!(),
+ },
+ count: args.count,
+ };
+
+ let (query_set, maybe_err) = gfx_select!(device => instance.device_create_query_set(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUQuerySet(query_set));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from),
+ }))
+}
diff --git a/op_crates/webgpu/pipeline.rs b/op_crates/webgpu/pipeline.rs
new file mode 100644
index 000000000..04493b18a
--- /dev/null
+++ b/op_crates/webgpu/pipeline.rs
@@ -0,0 +1,643 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPUPipelineLayout(
+ pub(crate) wgpu_core::id::PipelineLayoutId,
+);
+impl Resource for WebGPUPipelineLayout {
+ fn name(&self) -> Cow<str> {
+ "webGPUPipelineLayout".into()
+ }
+}
+
+pub(crate) struct WebGPUComputePipeline(
+ pub(crate) wgpu_core::id::ComputePipelineId,
+);
+impl Resource for WebGPUComputePipeline {
+ fn name(&self) -> Cow<str> {
+ "webGPUComputePipeline".into()
+ }
+}
+
+pub(crate) struct WebGPURenderPipeline(
+ pub(crate) wgpu_core::id::RenderPipelineId,
+);
+impl Resource for WebGPURenderPipeline {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderPipeline".into()
+ }
+}
+
+pub fn serialize_index_format(format: String) -> wgpu_types::IndexFormat {
+ match format.as_str() {
+ "uint16" => wgpu_types::IndexFormat::Uint16,
+ "uint32" => wgpu_types::IndexFormat::Uint32,
+ _ => unreachable!(),
+ }
+}
+
+fn serialize_stencil_operation(
+ operation: &str,
+) -> wgpu_types::StencilOperation {
+ match operation {
+ "keep" => wgpu_types::StencilOperation::Keep,
+ "zero" => wgpu_types::StencilOperation::Zero,
+ "replace" => wgpu_types::StencilOperation::Replace,
+ "invert" => wgpu_types::StencilOperation::Invert,
+ "increment-clamp" => wgpu_types::StencilOperation::IncrementClamp,
+ "decrement-clamp" => wgpu_types::StencilOperation::DecrementClamp,
+ "increment-wrap" => wgpu_types::StencilOperation::IncrementWrap,
+ "decrement-wrap" => wgpu_types::StencilOperation::DecrementWrap,
+ _ => unreachable!(),
+ }
+}
+
+fn serialize_stencil_face_state(
+ state: GPUStencilFaceState,
+) -> wgpu_types::StencilFaceState {
+ wgpu_types::StencilFaceState {
+ compare: state
+ .compare
+ .as_ref()
+ .map_or(wgpu_types::CompareFunction::Always, |op| {
+ super::sampler::serialize_compare_function(op)
+ }),
+ fail_op: state
+ .fail_op
+ .as_ref()
+ .map_or(wgpu_types::StencilOperation::Keep, |op| {
+ serialize_stencil_operation(op)
+ }),
+ depth_fail_op: state
+ .depth_fail_op
+ .as_ref()
+ .map_or(wgpu_types::StencilOperation::Keep, |op| {
+ serialize_stencil_operation(op)
+ }),
+ pass_op: state
+ .pass_op
+ .as_ref()
+ .map_or(wgpu_types::StencilOperation::Keep, |op| {
+ serialize_stencil_operation(op)
+ }),
+ }
+}
+
+fn serialize_blend_factor(blend_factor: &str) -> wgpu_types::BlendFactor {
+ match blend_factor {
+ "zero" => wgpu_types::BlendFactor::Zero,
+ "one" => wgpu_types::BlendFactor::One,
+ "src-color" => wgpu_types::BlendFactor::SrcColor,
+ "one-minus-src-color" => wgpu_types::BlendFactor::OneMinusSrcColor,
+ "src-alpha" => wgpu_types::BlendFactor::SrcAlpha,
+ "one-minus-src-alpha" => wgpu_types::BlendFactor::OneMinusSrcAlpha,
+ "dst-color" => wgpu_types::BlendFactor::DstColor,
+ "one-minus-dst-color" => wgpu_types::BlendFactor::OneMinusDstColor,
+ "dst-alpha" => wgpu_types::BlendFactor::DstAlpha,
+ "one-minus-dst-alpha" => wgpu_types::BlendFactor::OneMinusDstAlpha,
+ "src-alpha-saturated" => wgpu_types::BlendFactor::SrcAlphaSaturated,
+ "blend-color" => wgpu_types::BlendFactor::BlendColor,
+ "one-minus-blend-color" => wgpu_types::BlendFactor::OneMinusBlendColor,
+ _ => unreachable!(),
+ }
+}
+
+fn serialize_blend_component(
+ blend: GPUBlendComponent,
+) -> wgpu_types::BlendState {
+ wgpu_types::BlendState {
+ src_factor: blend
+ .src_factor
+ .as_ref()
+ .map_or(wgpu_types::BlendFactor::One, |factor| {
+ serialize_blend_factor(factor)
+ }),
+ dst_factor: blend
+ .dst_factor
+ .as_ref()
+ .map_or(wgpu_types::BlendFactor::Zero, |factor| {
+ serialize_blend_factor(factor)
+ }),
+ operation: match &blend.operation {
+ Some(operation) => match operation.as_str() {
+ "add" => wgpu_types::BlendOperation::Add,
+ "subtract" => wgpu_types::BlendOperation::Subtract,
+ "reverse-subtract" => wgpu_types::BlendOperation::ReverseSubtract,
+ "min" => wgpu_types::BlendOperation::Min,
+ "max" => wgpu_types::BlendOperation::Max,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::BlendOperation::Add,
+ },
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUProgrammableStage {
+ module: u32,
+ entry_point: String,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateComputePipelineArgs {
+ device_rid: u32,
+ label: Option<String>,
+ layout: Option<u32>,
+ compute: GPUProgrammableStage,
+}
+
+pub fn op_webgpu_create_compute_pipeline(
+ state: &mut OpState,
+ args: CreateComputePipelineArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let pipeline_layout = if let Some(rid) = args.layout {
+ let id = state
+ .resource_table
+ .get::<WebGPUPipelineLayout>(rid)
+ .ok_or_else(bad_resource_id)?;
+ Some(id.0)
+ } else {
+ None
+ };
+
+ let compute_shader_module_resource = state
+ .resource_table
+ .get::<super::shader::WebGPUShaderModule>(args.compute.module)
+ .ok_or_else(bad_resource_id)?;
+
+ let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
+ label: args.label.map(Cow::from),
+ layout: pipeline_layout,
+ stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
+ module: compute_shader_module_resource.0,
+ entry_point: Cow::from(args.compute.entry_point),
+ },
+ };
+ let implicit_pipelines = match args.layout {
+ Some(_) => None,
+ None => Some(wgpu_core::device::ImplicitPipelineIds {
+ root_id: std::marker::PhantomData,
+ group_ids: &[std::marker::PhantomData; wgpu_core::MAX_BIND_GROUPS],
+ }),
+ };
+
+ let (compute_pipeline, _, maybe_err) = gfx_select!(device => instance.device_create_compute_pipeline(
+ device,
+ &descriptor,
+ std::marker::PhantomData,
+ implicit_pipelines
+ ));
+
+ let rid = state
+ .resource_table
+ .add(WebGPUComputePipeline(compute_pipeline));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from),
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ComputePipelineGetBindGroupLayoutArgs {
+ compute_pipeline_rid: u32,
+ index: u32,
+}
+
+pub fn op_webgpu_compute_pipeline_get_bind_group_layout(
+ state: &mut OpState,
+ args: ComputePipelineGetBindGroupLayoutArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let compute_pipeline_resource = state
+ .resource_table
+ .get::<WebGPUComputePipeline>(args.compute_pipeline_rid)
+ .ok_or_else(bad_resource_id)?;
+ let compute_pipeline = compute_pipeline_resource.0;
+
+ let (bind_group_layout, maybe_err) = gfx_select!(compute_pipeline => instance.compute_pipeline_get_bind_group_layout(compute_pipeline, args.index, std::marker::PhantomData));
+
+ let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
+
+ let rid = state
+ .resource_table
+ .add(super::binding::WebGPUBindGroupLayout(bind_group_layout));
+
+ Ok(json!({
+ "rid": rid,
+ "label": label,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUPrimitiveState {
+ topology: Option<String>,
+ strip_index_format: Option<String>,
+ front_face: Option<String>,
+ cull_mode: Option<String>,
+}
+
+#[derive(Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+struct GPUBlendComponent {
+ src_factor: Option<String>,
+ dst_factor: Option<String>,
+ operation: Option<String>,
+}
+
+#[derive(Deserialize, Clone)]
+#[serde(rename_all = "camelCase")]
+struct GPUBlendState {
+ color: GPUBlendComponent,
+ alpha: GPUBlendComponent,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUColorTargetState {
+ format: String,
+ blend: Option<GPUBlendState>,
+ write_mask: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUStencilFaceState {
+ compare: Option<String>,
+ fail_op: Option<String>,
+ depth_fail_op: Option<String>,
+ pass_op: Option<String>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUDepthStencilState {
+ format: String,
+ depth_write_enabled: Option<bool>,
+ depth_compare: Option<String>,
+ stencil_front: Option<GPUStencilFaceState>,
+ stencil_back: Option<GPUStencilFaceState>,
+ stencil_read_mask: Option<u32>,
+ stencil_write_mask: Option<u32>,
+ depth_bias: Option<i32>,
+ depth_bias_slope_scale: Option<f32>,
+ depth_bias_clamp: Option<f32>,
+ clamp_depth: Option<bool>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUVertexAttribute {
+ format: String,
+ offset: u64,
+ shader_location: u32,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUVertexBufferLayout {
+ array_stride: u64,
+ step_mode: Option<String>,
+ attributes: Vec<GPUVertexAttribute>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUVertexState {
+ module: u32,
+ entry_point: String,
+ buffers: Option<Vec<Option<GPUVertexBufferLayout>>>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUMultisampleState {
+ count: Option<u32>,
+ mask: Option<u64>, // against spec, but future proof
+ alpha_to_coverage_enabled: Option<bool>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUFragmentState {
+ targets: Vec<GPUColorTargetState>,
+ module: u32,
+ entry_point: String,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateRenderPipelineArgs {
+ device_rid: u32,
+ label: Option<String>,
+ layout: Option<u32>,
+ vertex: GPUVertexState,
+ primitive: Option<GPUPrimitiveState>,
+ depth_stencil: Option<GPUDepthStencilState>,
+ multisample: Option<GPUMultisampleState>,
+ fragment: Option<GPUFragmentState>,
+}
+
+pub fn op_webgpu_create_render_pipeline(
+ state: &mut OpState,
+ args: CreateRenderPipelineArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let layout = if let Some(rid) = args.layout {
+ let pipeline_layout_resource = state
+ .resource_table
+ .get::<WebGPUPipelineLayout>(rid)
+ .ok_or_else(bad_resource_id)?;
+ Some(pipeline_layout_resource.0)
+ } else {
+ None
+ };
+
+ let vertex_shader_module_resource = state
+ .resource_table
+ .get::<super::shader::WebGPUShaderModule>(args.vertex.module)
+ .ok_or_else(bad_resource_id)?;
+
+ let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
+ label: args.label.map(Cow::from),
+ layout,
+ vertex: wgpu_core::pipeline::VertexState {
+ stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
+ module: vertex_shader_module_resource.0,
+ entry_point: Cow::from(args.vertex.entry_point),
+ },
+ buffers: Cow::from(if let Some(buffers) = args.vertex.buffers {
+ let mut return_buffers = vec![];
+ for buffer in buffers {
+ if let Some(buffer) = buffer {
+ return_buffers.push(wgpu_core::pipeline::VertexBufferLayout {
+ array_stride: buffer.array_stride,
+ step_mode: match buffer.step_mode {
+ Some(step_mode) => match step_mode.as_str() {
+ "vertex" => wgpu_types::InputStepMode::Vertex,
+ "instance" => wgpu_types::InputStepMode::Instance,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::InputStepMode::Vertex,
+ },
+ attributes: Cow::from(
+ buffer
+ .attributes
+ .iter()
+ .map(|attribute| wgpu_types::VertexAttribute {
+ format: match attribute.format.as_str() {
+ "uchar2" => wgpu_types::VertexFormat::Uchar2,
+ "uchar4" => wgpu_types::VertexFormat::Uchar4,
+ "char2" => wgpu_types::VertexFormat::Char2,
+ "char4" => wgpu_types::VertexFormat::Char4,
+ "uchar2norm" => wgpu_types::VertexFormat::Uchar2Norm,
+ "uchar4norm" => wgpu_types::VertexFormat::Uchar4,
+ "char2norm" => wgpu_types::VertexFormat::Char2Norm,
+ "char4norm" => wgpu_types::VertexFormat::Char4Norm,
+ "ushort2" => wgpu_types::VertexFormat::Ushort2,
+ "ushort4" => wgpu_types::VertexFormat::Ushort4,
+ "short2" => wgpu_types::VertexFormat::Short2,
+ "short4" => wgpu_types::VertexFormat::Short4,
+ "ushort2norm" => wgpu_types::VertexFormat::Ushort2Norm,
+ "ushort4norm" => wgpu_types::VertexFormat::Ushort4Norm,
+ "short2norm" => wgpu_types::VertexFormat::Short2Norm,
+ "short4norm" => wgpu_types::VertexFormat::Short4Norm,
+ "half2" => wgpu_types::VertexFormat::Half2,
+ "half4" => wgpu_types::VertexFormat::Half4,
+ "float" => wgpu_types::VertexFormat::Float,
+ "float2" => wgpu_types::VertexFormat::Float2,
+ "float3" => wgpu_types::VertexFormat::Float3,
+ "float4" => wgpu_types::VertexFormat::Float4,
+ "uint" => wgpu_types::VertexFormat::Uint,
+ "uint2" => wgpu_types::VertexFormat::Uint2,
+ "uint3" => wgpu_types::VertexFormat::Uint3,
+ "uint4" => wgpu_types::VertexFormat::Uint4,
+ "int" => wgpu_types::VertexFormat::Int,
+ "int2" => wgpu_types::VertexFormat::Int2,
+ "int3" => wgpu_types::VertexFormat::Int3,
+ "int4" => wgpu_types::VertexFormat::Int4,
+ _ => unreachable!(),
+ },
+ offset: attribute.offset,
+ shader_location: attribute.shader_location,
+ })
+ .collect::<Vec<wgpu_types::VertexAttribute>>(),
+ ),
+ })
+ }
+ }
+ return_buffers
+ } else {
+ vec![]
+ }),
+ },
+ primitive: args.primitive.map_or(Default::default(), |primitive| {
+ wgpu_types::PrimitiveState {
+ topology: match primitive.topology {
+ Some(topology) => match topology.as_str() {
+ "point-list" => wgpu_types::PrimitiveTopology::PointList,
+ "line-list" => wgpu_types::PrimitiveTopology::LineList,
+ "line-strip" => wgpu_types::PrimitiveTopology::LineStrip,
+ "triangle-list" => wgpu_types::PrimitiveTopology::TriangleList,
+ "triangle-strip" => wgpu_types::PrimitiveTopology::TriangleStrip,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::PrimitiveTopology::TriangleList,
+ },
+ strip_index_format: primitive
+ .strip_index_format
+ .map(serialize_index_format),
+ front_face: match primitive.front_face {
+ Some(front_face) => match front_face.as_str() {
+ "ccw" => wgpu_types::FrontFace::Ccw,
+ "cw" => wgpu_types::FrontFace::Cw,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::FrontFace::Ccw,
+ },
+ cull_mode: match primitive.cull_mode {
+ Some(cull_mode) => match cull_mode.as_str() {
+ "none" => wgpu_types::CullMode::None,
+ "front" => wgpu_types::CullMode::Front,
+ "back" => wgpu_types::CullMode::Back,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::CullMode::None,
+ },
+ polygon_mode: Default::default(), // native-only
+ }
+ }),
+ depth_stencil: args.depth_stencil.map(|depth_stencil| {
+ wgpu_types::DepthStencilState {
+ format: super::texture::serialize_texture_format(&depth_stencil.format)
+ .unwrap(),
+ depth_write_enabled: depth_stencil.depth_write_enabled.unwrap_or(false),
+ depth_compare: match depth_stencil.depth_compare {
+ Some(depth_compare) => {
+ super::sampler::serialize_compare_function(&depth_compare)
+ }
+ None => wgpu_types::CompareFunction::Always,
+ },
+ stencil: wgpu_types::StencilState {
+ front: depth_stencil
+ .stencil_front
+ .map_or(Default::default(), serialize_stencil_face_state),
+ back: depth_stencil
+ .stencil_back
+ .map_or(Default::default(), serialize_stencil_face_state),
+ read_mask: depth_stencil.stencil_read_mask.unwrap_or(0xFFFFFFFF),
+ write_mask: depth_stencil.stencil_write_mask.unwrap_or(0xFFFFFFFF),
+ },
+ bias: wgpu_types::DepthBiasState {
+ constant: depth_stencil.depth_bias.unwrap_or(0),
+ slope_scale: depth_stencil.depth_bias_slope_scale.unwrap_or(0.0),
+ clamp: depth_stencil.depth_bias_clamp.unwrap_or(0.0),
+ },
+ clamp_depth: depth_stencil.clamp_depth.unwrap_or(false),
+ }
+ }),
+ multisample: args.multisample.map_or(Default::default(), |multisample| {
+ wgpu_types::MultisampleState {
+ count: multisample.count.unwrap_or(1),
+ mask: multisample.mask.unwrap_or(0xFFFFFFFF),
+ alpha_to_coverage_enabled: multisample
+ .alpha_to_coverage_enabled
+ .unwrap_or(false),
+ }
+ }),
+ fragment: args.fragment.map(|fragment| {
+ let fragment_shader_module_resource = state
+ .resource_table
+ .get::<super::shader::WebGPUShaderModule>(fragment.module)
+ .ok_or_else(bad_resource_id)
+ .unwrap();
+
+ wgpu_core::pipeline::FragmentState {
+ stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
+ module: fragment_shader_module_resource.0,
+ entry_point: Cow::from(fragment.entry_point),
+ },
+ targets: Cow::from(
+ fragment
+ .targets
+ .iter()
+ .map(|target| {
+ let blends = target.blend.clone().map(|blend| {
+ (
+ serialize_blend_component(blend.alpha),
+ serialize_blend_component(blend.color),
+ )
+ });
+
+ wgpu_types::ColorTargetState {
+ format: super::texture::serialize_texture_format(
+ &target.format,
+ )
+ .unwrap(),
+ alpha_blend: blends
+ .clone()
+ .map_or(Default::default(), |states| states.0),
+ color_blend: blends
+ .map_or(Default::default(), |states| states.1),
+ write_mask: target
+ .write_mask
+ .map_or(Default::default(), |mask| {
+ wgpu_types::ColorWrite::from_bits(mask).unwrap()
+ }),
+ }
+ })
+ .collect::<Vec<wgpu_types::ColorTargetState>>(),
+ ),
+ }
+ }),
+ };
+
+ let implicit_pipelines = match args.layout {
+ Some(_) => None,
+ None => Some(wgpu_core::device::ImplicitPipelineIds {
+ root_id: std::marker::PhantomData,
+ group_ids: &[std::marker::PhantomData; wgpu_core::MAX_BIND_GROUPS],
+ }),
+ };
+
+ let (render_pipeline, _, maybe_err) = gfx_select!(device => instance.device_create_render_pipeline(
+ device,
+ &descriptor,
+ std::marker::PhantomData,
+ implicit_pipelines
+ ));
+
+ let rid = state
+ .resource_table
+ .add(WebGPURenderPipeline(render_pipeline));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPipelineGetBindGroupLayoutArgs {
+ render_pipeline_rid: u32,
+ index: u32,
+}
+
+pub fn op_webgpu_render_pipeline_get_bind_group_layout(
+ state: &mut OpState,
+ args: RenderPipelineGetBindGroupLayoutArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let render_pipeline_resource = state
+ .resource_table
+ .get::<WebGPURenderPipeline>(args.render_pipeline_rid)
+ .ok_or_else(bad_resource_id)?;
+ let render_pipeline = render_pipeline_resource.0;
+
+ let (bind_group_layout, maybe_err) = gfx_select!(render_pipeline => instance.render_pipeline_get_bind_group_layout(render_pipeline, args.index, std::marker::PhantomData));
+
+ let label = gfx_select!(bind_group_layout => instance.bind_group_layout_label(bind_group_layout));
+
+ let rid = state
+ .resource_table
+ .add(super::binding::WebGPUBindGroupLayout(bind_group_layout));
+
+ Ok(json!({
+ "rid": rid,
+ "label": label,
+ "err": maybe_err.map(WebGPUError::from),
+ }))
+}
diff --git a/op_crates/webgpu/queue.rs b/op_crates/webgpu/queue.rs
new file mode 100644
index 000000000..c141a3c45
--- /dev/null
+++ b/op_crates/webgpu/queue.rs
@@ -0,0 +1,157 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::OpState;
+use deno_core::ZeroCopyBuf;
+use serde::Deserialize;
+
+use super::error::WebGPUError;
+
+type WebGPUQueue = super::WebGPUDevice;
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct QueueSubmitArgs {
+ queue_rid: u32,
+ command_buffers: Vec<u32>,
+}
+
+pub fn op_webgpu_queue_submit(
+ state: &mut OpState,
+ args: QueueSubmitArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let queue_resource = state
+ .resource_table
+ .get::<WebGPUQueue>(args.queue_rid)
+ .ok_or_else(bad_resource_id)?;
+ let queue = queue_resource.0;
+
+ let mut ids = vec![];
+
+ for rid in args.command_buffers {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::command_encoder::WebGPUCommandBuffer>(rid)
+ .ok_or_else(bad_resource_id)?;
+ ids.push(buffer_resource.0);
+ }
+
+ let maybe_err =
+ gfx_select!(queue => instance.queue_submit(queue, &ids)).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+struct GPUImageDataLayout {
+ offset: Option<u64>,
+ bytes_per_row: Option<u32>,
+ rows_per_image: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct QueueWriteBufferArgs {
+ queue_rid: u32,
+ buffer: u32,
+ buffer_offset: u64,
+ data_offset: usize,
+ size: Option<usize>,
+}
+
+pub fn op_webgpu_write_buffer(
+ state: &mut OpState,
+ args: QueueWriteBufferArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.buffer)
+ .ok_or_else(bad_resource_id)?;
+ let buffer = buffer_resource.0;
+ let queue_resource = state
+ .resource_table
+ .get::<WebGPUQueue>(args.queue_rid)
+ .ok_or_else(bad_resource_id)?;
+ let queue = queue_resource.0;
+
+ let data = match args.size {
+ Some(size) => &zero_copy[0][args.data_offset..(args.data_offset + size)],
+ None => &zero_copy[0][args.data_offset..],
+ };
+ let maybe_err = gfx_select!(queue => instance.queue_write_buffer(
+ queue,
+ buffer,
+ args.buffer_offset,
+ data
+ ))
+ .err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct QueueWriteTextureArgs {
+ queue_rid: u32,
+ destination: super::command_encoder::GPUImageCopyTexture,
+ data_layout: GPUImageDataLayout,
+ size: super::texture::GPUExtent3D,
+}
+
+pub fn op_webgpu_write_texture(
+ state: &mut OpState,
+ args: QueueWriteTextureArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let texture_resource = state
+ .resource_table
+ .get::<super::texture::WebGPUTexture>(args.destination.texture)
+ .ok_or_else(bad_resource_id)?;
+ let queue_resource = state
+ .resource_table
+ .get::<WebGPUQueue>(args.queue_rid)
+ .ok_or_else(bad_resource_id)?;
+ let queue = queue_resource.0;
+
+ let destination = wgpu_core::command::TextureCopyView {
+ texture: texture_resource.0,
+ mip_level: args.destination.mip_level.unwrap_or(0),
+ origin: args
+ .destination
+ .origin
+ .map_or(Default::default(), |origin| wgpu_types::Origin3d {
+ x: origin.x.unwrap_or(0),
+ y: origin.y.unwrap_or(0),
+ z: origin.z.unwrap_or(0),
+ }),
+ };
+ let data_layout = wgpu_types::TextureDataLayout {
+ offset: args.data_layout.offset.unwrap_or(0),
+ bytes_per_row: args.data_layout.bytes_per_row.unwrap_or(0),
+ rows_per_image: args.data_layout.rows_per_image.unwrap_or(0),
+ };
+
+ let maybe_err = gfx_select!(queue => instance.queue_write_texture(
+ queue,
+ &destination,
+ &*zero_copy[0],
+ &data_layout,
+ &wgpu_types::Extent3d {
+ width: args.size.width.unwrap_or(1),
+ height: args.size.height.unwrap_or(1),
+ depth: args.size.depth.unwrap_or(1),
+ }
+ ))
+ .err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
diff --git a/op_crates/webgpu/render_pass.rs b/op_crates/webgpu/render_pass.rs
new file mode 100644
index 000000000..c9018726b
--- /dev/null
+++ b/op_crates/webgpu/render_pass.rs
@@ -0,0 +1,676 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+use std::cell::RefCell;
+
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPURenderPass(
+ pub(crate) RefCell<wgpu_core::command::RenderPass>,
+);
+impl Resource for WebGPURenderPass {
+ fn name(&self) -> Cow<str> {
+ "webGPURenderPass".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetViewportArgs {
+ render_pass_rid: u32,
+ x: f32,
+ y: f32,
+ width: f32,
+ height: f32,
+ min_depth: f32,
+ max_depth: f32,
+}
+
+pub fn op_webgpu_render_pass_set_viewport(
+ state: &mut OpState,
+ args: RenderPassSetViewportArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_viewport(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.x,
+ args.y,
+ args.width,
+ args.height,
+ args.min_depth,
+ args.max_depth,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetScissorRectArgs {
+ render_pass_rid: u32,
+ x: u32,
+ y: u32,
+ width: u32,
+ height: u32,
+}
+
+pub fn op_webgpu_render_pass_set_scissor_rect(
+ state: &mut OpState,
+ args: RenderPassSetScissorRectArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_scissor_rect(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.x,
+ args.y,
+ args.width,
+ args.height,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPUColor {
+ pub r: f64,
+ pub g: f64,
+ pub b: f64,
+ pub a: f64,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetBlendColorArgs {
+ render_pass_rid: u32,
+ color: GPUColor,
+}
+
+pub fn op_webgpu_render_pass_set_blend_color(
+ state: &mut OpState,
+ args: RenderPassSetBlendColorArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_blend_color(
+ &mut render_pass_resource.0.borrow_mut(),
+ &wgpu_types::Color {
+ r: args.color.r,
+ g: args.color.g,
+ b: args.color.b,
+ a: args.color.a,
+ },
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetStencilReferenceArgs {
+ render_pass_rid: u32,
+ reference: u32,
+}
+
+pub fn op_webgpu_render_pass_set_stencil_reference(
+ state: &mut OpState,
+ args: RenderPassSetStencilReferenceArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_stencil_reference(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.reference,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassBeginPipelineStatisticsQueryArgs {
+ render_pass_rid: u32,
+ query_set: u32,
+ query_index: u32,
+}
+
+pub fn op_webgpu_render_pass_begin_pipeline_statistics_query(
+ state: &mut OpState,
+ args: RenderPassBeginPipelineStatisticsQueryArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGPUQuerySet>(args.query_set)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_begin_pipeline_statistics_query(
+ &mut render_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassEndPipelineStatisticsQueryArgs {
+ render_pass_rid: u32,
+}
+
+pub fn op_webgpu_render_pass_end_pipeline_statistics_query(
+ state: &mut OpState,
+ args: RenderPassEndPipelineStatisticsQueryArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_end_pipeline_statistics_query(
+ &mut render_pass_resource.0.borrow_mut(),
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassWriteTimestampArgs {
+ render_pass_rid: u32,
+ query_set: u32,
+ query_index: u32,
+}
+
+pub fn op_webgpu_render_pass_write_timestamp(
+ state: &mut OpState,
+ args: RenderPassWriteTimestampArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+ let query_set_resource = state
+ .resource_table
+ .get::<super::WebGPUQuerySet>(args.query_set)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_write_timestamp(
+ &mut render_pass_resource.0.borrow_mut(),
+ query_set_resource.0,
+ args.query_index,
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassExecuteBundlesArgs {
+ render_pass_rid: u32,
+ bundles: Vec<u32>,
+}
+
+pub fn op_webgpu_render_pass_execute_bundles(
+ state: &mut OpState,
+ args: RenderPassExecuteBundlesArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let mut render_bundle_ids = vec![];
+
+ for rid in &args.bundles {
+ let render_bundle_resource = state
+ .resource_table
+ .get::<super::bundle::WebGPURenderBundle>(*rid)
+ .ok_or_else(bad_resource_id)?;
+ render_bundle_ids.push(render_bundle_resource.0);
+ }
+
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_execute_bundles(
+ &mut render_pass_resource.0.borrow_mut(),
+ render_bundle_ids.as_ptr(),
+ args.bundles.len(),
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassEndPassArgs {
+ command_encoder_rid: u32,
+ render_pass_rid: u32,
+}
+
+pub fn op_webgpu_render_pass_end_pass(
+ state: &mut OpState,
+ args: RenderPassEndPassArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let command_encoder_resource = state
+ .resource_table
+ .get::<super::command_encoder::WebGPUCommandEncoder>(
+ args.command_encoder_rid,
+ )
+ .ok_or_else(bad_resource_id)?;
+ let command_encoder = command_encoder_resource.0;
+ let render_pass_resource = state
+ .resource_table
+ .take::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+ let render_pass = &render_pass_resource.0.borrow();
+ let instance = state.borrow::<super::Instance>();
+
+ let maybe_err = gfx_select!(command_encoder => instance.command_encoder_run_render_pass(command_encoder, render_pass)).err();
+
+ Ok(json!({ "err": maybe_err.map(WebGPUError::from) }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetBindGroupArgs {
+ render_pass_rid: u32,
+ index: u32,
+ bind_group: u32,
+ dynamic_offsets_data: Option<Vec<u32>>,
+ dynamic_offsets_data_start: usize,
+ dynamic_offsets_data_length: usize,
+}
+
+pub fn op_webgpu_render_pass_set_bind_group(
+ state: &mut OpState,
+ args: RenderPassSetBindGroupArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let bind_group_resource = state
+ .resource_table
+ .get::<super::binding::WebGPUBindGroup>(args.bind_group)
+ .ok_or_else(bad_resource_id)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ // I know this might look like it can be easily deduplicated, but it can not
+ // be due to the lifetime of the args.dynamic_offsets_data slice. Because we
+ // need to use a raw pointer here the slice can be freed before the pointer
+ // is used in wgpu_render_pass_set_bind_group. See
+ // https://matrix.to/#/!XFRnMvAfptAHthwBCx:matrix.org/$HgrlhD-Me1DwsGb8UdMu2Hqubgks8s7ILwWRwigOUAg
+ match args.dynamic_offsets_data {
+ Some(data) => unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_bind_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ data.as_slice().as_ptr(),
+ args.dynamic_offsets_data_length,
+ );
+ },
+ None => {
+ let (prefix, data, suffix) = unsafe { zero_copy[0].align_to::<u32>() };
+ assert!(prefix.is_empty());
+ assert!(suffix.is_empty());
+ unsafe {
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_bind_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.index,
+ bind_group_resource.0,
+ data[args.dynamic_offsets_data_start..].as_ptr(),
+ args.dynamic_offsets_data_length,
+ );
+ }
+ }
+ };
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassPushDebugGroupArgs {
+ render_pass_rid: u32,
+ group_label: String,
+}
+
+pub fn op_webgpu_render_pass_push_debug_group(
+ state: &mut OpState,
+ args: RenderPassPushDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ let label = std::ffi::CString::new(args.group_label).unwrap();
+ wgpu_core::command::render_ffi::wgpu_render_pass_push_debug_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassPopDebugGroupArgs {
+ render_pass_rid: u32,
+}
+
+pub fn op_webgpu_render_pass_pop_debug_group(
+ state: &mut OpState,
+ args: RenderPassPopDebugGroupArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_pop_debug_group(
+ &mut render_pass_resource.0.borrow_mut(),
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassInsertDebugMarkerArgs {
+ render_pass_rid: u32,
+ marker_label: String,
+}
+
+pub fn op_webgpu_render_pass_insert_debug_marker(
+ state: &mut OpState,
+ args: RenderPassInsertDebugMarkerArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ unsafe {
+ let label = std::ffi::CString::new(args.marker_label).unwrap();
+ wgpu_core::command::render_ffi::wgpu_render_pass_insert_debug_marker(
+ &mut render_pass_resource.0.borrow_mut(),
+ label.as_ptr(),
+ 0, // wgpu#975
+ );
+ }
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetPipelineArgs {
+ render_pass_rid: u32,
+ pipeline: u32,
+}
+
+pub fn op_webgpu_render_pass_set_pipeline(
+ state: &mut OpState,
+ args: RenderPassSetPipelineArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pipeline_resource = state
+ .resource_table
+ .get::<super::pipeline::WebGPURenderPipeline>(args.pipeline)
+ .ok_or_else(bad_resource_id)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_pipeline(
+ &mut render_pass_resource.0.borrow_mut(),
+ render_pipeline_resource.0,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetIndexBufferArgs {
+ render_pass_rid: u32,
+ buffer: u32,
+ index_format: String,
+ offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_render_pass_set_index_buffer(
+ state: &mut OpState,
+ args: RenderPassSetIndexBufferArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.buffer)
+ .ok_or_else(bad_resource_id)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ render_pass_resource.0.borrow_mut().set_index_buffer(
+ buffer_resource.0,
+ super::pipeline::serialize_index_format(args.index_format),
+ args.offset,
+ std::num::NonZeroU64::new(args.size),
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassSetVertexBufferArgs {
+ render_pass_rid: u32,
+ slot: u32,
+ buffer: u32,
+ offset: u64,
+ size: u64,
+}
+
+pub fn op_webgpu_render_pass_set_vertex_buffer(
+ state: &mut OpState,
+ args: RenderPassSetVertexBufferArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.buffer)
+ .ok_or_else(bad_resource_id)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_set_vertex_buffer(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.slot,
+ buffer_resource.0,
+ args.offset,
+ std::num::NonZeroU64::new(args.size),
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawArgs {
+ render_pass_rid: u32,
+ vertex_count: u32,
+ instance_count: u32,
+ first_vertex: u32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_pass_draw(
+ state: &mut OpState,
+ args: RenderPassDrawArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.vertex_count,
+ args.instance_count,
+ args.first_vertex,
+ args.first_instance,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawIndexedArgs {
+ render_pass_rid: u32,
+ index_count: u32,
+ instance_count: u32,
+ first_index: u32,
+ base_vertex: i32,
+ first_instance: u32,
+}
+
+pub fn op_webgpu_render_pass_draw_indexed(
+ state: &mut OpState,
+ args: RenderPassDrawIndexedArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed(
+ &mut render_pass_resource.0.borrow_mut(),
+ args.index_count,
+ args.instance_count,
+ args.first_index,
+ args.base_vertex,
+ args.first_instance,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawIndirectArgs {
+ render_pass_rid: u32,
+ indirect_buffer: u32,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_render_pass_draw_indirect(
+ state: &mut OpState,
+ args: RenderPassDrawIndirectArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
+ .ok_or_else(bad_resource_id)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indirect(
+ &mut render_pass_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(json!({}))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct RenderPassDrawIndexedIndirectArgs {
+ render_pass_rid: u32,
+ indirect_buffer: u32,
+ indirect_offset: u64,
+}
+
+pub fn op_webgpu_render_pass_draw_indexed_indirect(
+ state: &mut OpState,
+ args: RenderPassDrawIndexedIndirectArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let buffer_resource = state
+ .resource_table
+ .get::<super::buffer::WebGPUBuffer>(args.indirect_buffer)
+ .ok_or_else(bad_resource_id)?;
+ let render_pass_resource = state
+ .resource_table
+ .get::<WebGPURenderPass>(args.render_pass_rid)
+ .ok_or_else(bad_resource_id)?;
+
+ wgpu_core::command::render_ffi::wgpu_render_pass_draw_indexed_indirect(
+ &mut render_pass_resource.0.borrow_mut(),
+ buffer_resource.0,
+ args.indirect_offset,
+ );
+
+ Ok(json!({}))
+}
diff --git a/op_crates/webgpu/sampler.rs b/op_crates/webgpu/sampler.rs
new file mode 100644
index 000000000..48974a328
--- /dev/null
+++ b/op_crates/webgpu/sampler.rs
@@ -0,0 +1,129 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPUSampler(pub(crate) wgpu_core::id::SamplerId);
+impl Resource for WebGPUSampler {
+ fn name(&self) -> Cow<str> {
+ "webGPUSampler".into()
+ }
+}
+
+fn serialize_address_mode(
+ address_mode: Option<String>,
+) -> wgpu_types::AddressMode {
+ match address_mode {
+ Some(address_mode) => match address_mode.as_str() {
+ "clamp-to-edge" => wgpu_types::AddressMode::ClampToEdge,
+ "repeat" => wgpu_types::AddressMode::Repeat,
+ "mirror-repeat" => wgpu_types::AddressMode::MirrorRepeat,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::AddressMode::ClampToEdge,
+ }
+}
+
+fn serialize_filter_mode(
+ filter_mode: Option<String>,
+) -> wgpu_types::FilterMode {
+ match filter_mode {
+ Some(filter_mode) => match filter_mode.as_str() {
+ "nearest" => wgpu_types::FilterMode::Nearest,
+ "linear" => wgpu_types::FilterMode::Linear,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::FilterMode::Nearest,
+ }
+}
+
+pub fn serialize_compare_function(
+ compare: &str,
+) -> wgpu_types::CompareFunction {
+ match compare {
+ "never" => wgpu_types::CompareFunction::Never,
+ "less" => wgpu_types::CompareFunction::Less,
+ "equal" => wgpu_types::CompareFunction::Equal,
+ "less-equal" => wgpu_types::CompareFunction::LessEqual,
+ "greater" => wgpu_types::CompareFunction::Greater,
+ "not-equal" => wgpu_types::CompareFunction::NotEqual,
+ "greater-equal" => wgpu_types::CompareFunction::GreaterEqual,
+ "always" => wgpu_types::CompareFunction::Always,
+ _ => unreachable!(),
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateSamplerArgs {
+ device_rid: u32,
+ label: Option<String>,
+ address_mode_u: Option<String>,
+ address_mode_v: Option<String>,
+ address_mode_w: Option<String>,
+ mag_filter: Option<String>,
+ min_filter: Option<String>,
+ mipmap_filter: Option<String>,
+ lod_min_clamp: Option<f32>,
+ lod_max_clamp: Option<f32>,
+ compare: Option<String>,
+ max_anisotropy: Option<u8>,
+}
+
+pub fn op_webgpu_create_sampler(
+ state: &mut OpState,
+ args: CreateSamplerArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let descriptor = wgpu_core::resource::SamplerDescriptor {
+ label: args.label.map(Cow::from),
+ address_modes: [
+ serialize_address_mode(args.address_mode_u),
+ serialize_address_mode(args.address_mode_v),
+ serialize_address_mode(args.address_mode_w),
+ ],
+ mag_filter: serialize_filter_mode(args.mag_filter),
+ min_filter: serialize_filter_mode(args.min_filter),
+ mipmap_filter: serialize_filter_mode(args.mipmap_filter),
+ lod_min_clamp: args.lod_min_clamp.unwrap_or(0.0),
+ lod_max_clamp: args.lod_max_clamp.unwrap_or(
+ wgpu_core::resource::SamplerDescriptor::default().lod_max_clamp,
+ ),
+ compare: args
+ .compare
+ .as_ref()
+ .map(|compare| serialize_compare_function(compare)),
+ anisotropy_clamp: std::num::NonZeroU8::new(
+ args.max_anisotropy.unwrap_or(0),
+ ),
+ border_color: None, // native-only
+ };
+
+ let (sampler, maybe_err) = gfx_select!(device => instance.device_create_sampler(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUSampler(sampler));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
diff --git a/op_crates/webgpu/shader.rs b/op_crates/webgpu/shader.rs
new file mode 100644
index 000000000..25adf9b95
--- /dev/null
+++ b/op_crates/webgpu/shader.rs
@@ -0,0 +1,77 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::bad_resource_id;
+use deno_core::error::AnyError;
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+
+use super::error::WebGPUError;
+
+pub(crate) struct WebGPUShaderModule(pub(crate) wgpu_core::id::ShaderModuleId);
+impl Resource for WebGPUShaderModule {
+ fn name(&self) -> Cow<str> {
+ "webGPUShaderModule".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateShaderModuleArgs {
+ device_rid: u32,
+ label: Option<String>,
+ code: Option<String>,
+ _source_map: Option<()>, // not yet implemented
+}
+
+pub fn op_webgpu_create_shader_module(
+ state: &mut OpState,
+ args: CreateShaderModuleArgs,
+ zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let source = match args.code {
+ Some(code) => {
+ wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::from(code))
+ }
+ None => wgpu_core::pipeline::ShaderModuleSource::SpirV(Cow::from(unsafe {
+ let (prefix, data, suffix) = zero_copy[0].align_to::<u32>();
+ assert!(prefix.is_empty());
+ assert!(suffix.is_empty());
+ data
+ })),
+ };
+
+ let mut flags = wgpu_types::ShaderFlags::default();
+ flags.set(wgpu_types::ShaderFlags::VALIDATION, true);
+ #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
+ flags.set(wgpu_types::ShaderFlags::EXPERIMENTAL_TRANSLATION, true);
+
+ let descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
+ label: args.label.map(Cow::from),
+ flags,
+ };
+
+ let (shader_module, maybe_err) = gfx_select!(device => instance.device_create_shader_module(
+ device,
+ &descriptor,
+ source,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUShaderModule(shader_module));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
diff --git a/op_crates/webgpu/texture.rs b/op_crates/webgpu/texture.rs
new file mode 100644
index 000000000..94de28fed
--- /dev/null
+++ b/op_crates/webgpu/texture.rs
@@ -0,0 +1,256 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::error::{bad_resource_id, not_supported};
+use deno_core::serde_json::json;
+use deno_core::serde_json::Value;
+use deno_core::ZeroCopyBuf;
+use deno_core::{OpState, Resource};
+use serde::Deserialize;
+use std::borrow::Cow;
+
+use super::error::WebGPUError;
+pub(crate) struct WebGPUTexture(pub(crate) wgpu_core::id::TextureId);
+impl Resource for WebGPUTexture {
+ fn name(&self) -> Cow<str> {
+ "webGPUTexture".into()
+ }
+}
+
+pub(crate) struct WebGPUTextureView(pub(crate) wgpu_core::id::TextureViewId);
+impl Resource for WebGPUTextureView {
+ fn name(&self) -> Cow<str> {
+ "webGPUTextureView".into()
+ }
+}
+
+pub fn serialize_texture_format(
+ format: &str,
+) -> Result<wgpu_types::TextureFormat, AnyError> {
+ Ok(match format {
+ // 8-bit formats
+ "r8unorm" => wgpu_types::TextureFormat::R8Unorm,
+ "r8snorm" => wgpu_types::TextureFormat::R8Snorm,
+ "r8uint" => wgpu_types::TextureFormat::R8Uint,
+ "r8sint" => wgpu_types::TextureFormat::R8Sint,
+
+ // 16-bit formats
+ "r16uint" => wgpu_types::TextureFormat::R16Uint,
+ "r16sint" => wgpu_types::TextureFormat::R16Sint,
+ "r16float" => wgpu_types::TextureFormat::R16Float,
+ "rg8unorm" => wgpu_types::TextureFormat::Rg8Unorm,
+ "rg8snorm" => wgpu_types::TextureFormat::Rg8Snorm,
+ "rg8uint" => wgpu_types::TextureFormat::Rg8Uint,
+ "rg8sint" => wgpu_types::TextureFormat::Rg8Sint,
+
+ // 32-bit formats
+ "r32uint" => wgpu_types::TextureFormat::R32Uint,
+ "r32sint" => wgpu_types::TextureFormat::R32Sint,
+ "r32float" => wgpu_types::TextureFormat::R32Float,
+ "rg16uint" => wgpu_types::TextureFormat::Rg16Uint,
+ "rg16sint" => wgpu_types::TextureFormat::Rg16Sint,
+ "rg16float" => wgpu_types::TextureFormat::Rg16Float,
+ "rgba8unorm" => wgpu_types::TextureFormat::Rgba8Unorm,
+ "rgba8unorm-srgb" => wgpu_types::TextureFormat::Rgba8UnormSrgb,
+ "rgba8snorm" => wgpu_types::TextureFormat::Rgba8Snorm,
+ "rgba8uint" => wgpu_types::TextureFormat::Rgba8Uint,
+ "rgba8sint" => wgpu_types::TextureFormat::Rgba8Sint,
+ "bgra8unorm" => wgpu_types::TextureFormat::Bgra8Unorm,
+ "bgra8unorm-srgb" => wgpu_types::TextureFormat::Bgra8UnormSrgb,
+ // Packed 32-bit formats
+ "rgb9e5ufloat" => return Err(not_supported()), // wgpu#967
+ "rgb10a2unorm" => wgpu_types::TextureFormat::Rgb10a2Unorm,
+ "rg11b10ufloat" => wgpu_types::TextureFormat::Rg11b10Float,
+
+ // 64-bit formats
+ "rg32uint" => wgpu_types::TextureFormat::Rg32Uint,
+ "rg32sint" => wgpu_types::TextureFormat::Rg32Sint,
+ "rg32float" => wgpu_types::TextureFormat::Rg32Float,
+ "rgba16uint" => wgpu_types::TextureFormat::Rgba16Uint,
+ "rgba16sint" => wgpu_types::TextureFormat::Rgba16Sint,
+ "rgba16float" => wgpu_types::TextureFormat::Rgba16Float,
+
+ // 128-bit formats
+ "rgba32uint" => wgpu_types::TextureFormat::Rgba32Uint,
+ "rgba32sint" => wgpu_types::TextureFormat::Rgba32Sint,
+ "rgba32float" => wgpu_types::TextureFormat::Rgba32Float,
+
+ // Depth and stencil formats
+ "stencil8" => return Err(not_supported()), // wgpu#967
+ "depth16unorm" => return Err(not_supported()), // wgpu#967
+ "depth24plus" => wgpu_types::TextureFormat::Depth24Plus,
+ "depth24plus-stencil8" => wgpu_types::TextureFormat::Depth24PlusStencil8,
+ "depth32float" => wgpu_types::TextureFormat::Depth32Float,
+
+ // BC compressed formats usable if "texture-compression-bc" is both
+ // supported by the device/user agent and enabled in requestDevice.
+ "bc1-rgba-unorm" => wgpu_types::TextureFormat::Bc1RgbaUnorm,
+ "bc1-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc1RgbaUnormSrgb,
+ "bc2-rgba-unorm" => wgpu_types::TextureFormat::Bc2RgbaUnorm,
+ "bc2-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc2RgbaUnormSrgb,
+ "bc3-rgba-unorm" => wgpu_types::TextureFormat::Bc3RgbaUnorm,
+ "bc3-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc3RgbaUnormSrgb,
+ "bc4-r-unorm" => wgpu_types::TextureFormat::Bc4RUnorm,
+ "bc4-r-snorm" => wgpu_types::TextureFormat::Bc4RSnorm,
+ "bc5-rg-unorm" => wgpu_types::TextureFormat::Bc5RgUnorm,
+ "bc5-rg-snorm" => wgpu_types::TextureFormat::Bc5RgSnorm,
+ "bc6h-rgb-ufloat" => wgpu_types::TextureFormat::Bc6hRgbUfloat,
+ "bc6h-rgb-float" => wgpu_types::TextureFormat::Bc6hRgbSfloat, // wgpu#967
+ "bc7-rgba-unorm" => wgpu_types::TextureFormat::Bc7RgbaUnorm,
+ "bc7-rgba-unorm-srgb" => wgpu_types::TextureFormat::Bc7RgbaUnormSrgb,
+
+ // "depth24unorm-stencil8" extension
+ "depth24unorm-stencil8" => return Err(not_supported()), // wgpu#967
+
+ // "depth32float-stencil8" extension
+ "depth32float-stencil8" => return Err(not_supported()), // wgpu#967
+ _ => unreachable!(),
+ })
+}
+
+pub fn serialize_dimension(
+ dimension: &str,
+) -> wgpu_types::TextureViewDimension {
+ match dimension {
+ "1d" => wgpu_types::TextureViewDimension::D1,
+ "2d" => wgpu_types::TextureViewDimension::D2,
+ "2d-array" => wgpu_types::TextureViewDimension::D2Array,
+ "cube" => wgpu_types::TextureViewDimension::Cube,
+ "cube-array" => wgpu_types::TextureViewDimension::CubeArray,
+ "3d" => wgpu_types::TextureViewDimension::D3,
+ _ => unreachable!(),
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct GPUExtent3D {
+ pub width: Option<u32>,
+ pub height: Option<u32>,
+ pub depth: Option<u32>,
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateTextureArgs {
+ device_rid: u32,
+ label: Option<String>,
+ size: GPUExtent3D,
+ mip_level_count: Option<u32>,
+ sample_count: Option<u32>,
+ dimension: Option<String>,
+ format: String,
+ usage: u32,
+}
+
+pub fn op_webgpu_create_texture(
+ state: &mut OpState,
+ args: CreateTextureArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let device_resource = state
+ .resource_table
+ .get::<super::WebGPUDevice>(args.device_rid)
+ .ok_or_else(bad_resource_id)?;
+ let device = device_resource.0;
+
+ let descriptor = wgpu_core::resource::TextureDescriptor {
+ label: args.label.map(Cow::from),
+ size: wgpu_types::Extent3d {
+ width: args.size.width.unwrap_or(1),
+ height: args.size.height.unwrap_or(1),
+ depth: args.size.depth.unwrap_or(1),
+ },
+ mip_level_count: args.mip_level_count.unwrap_or(1),
+ sample_count: args.sample_count.unwrap_or(1),
+ dimension: match args.dimension {
+ Some(dimension) => match dimension.as_str() {
+ "1d" => wgpu_types::TextureDimension::D1,
+ "2d" => wgpu_types::TextureDimension::D2,
+ "3d" => wgpu_types::TextureDimension::D3,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::TextureDimension::D2,
+ },
+ format: serialize_texture_format(&args.format)?,
+ usage: wgpu_types::TextureUsage::from_bits(args.usage).unwrap(),
+ };
+
+ let (texture, maybe_err) = gfx_select!(device => instance.device_create_texture(
+ device,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUTexture(texture));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CreateTextureViewArgs {
+ texture_rid: u32,
+ label: Option<String>,
+ format: Option<String>,
+ dimension: Option<String>,
+ aspect: Option<String>,
+ base_mip_level: Option<u32>,
+ mip_level_count: Option<u32>,
+ base_array_layer: Option<u32>,
+ array_layer_count: Option<u32>,
+}
+
+pub fn op_webgpu_create_texture_view(
+ state: &mut OpState,
+ args: CreateTextureViewArgs,
+ _zero_copy: &mut [ZeroCopyBuf],
+) -> Result<Value, AnyError> {
+ let instance = state.borrow::<super::Instance>();
+ let texture_resource = state
+ .resource_table
+ .get::<WebGPUTexture>(args.texture_rid)
+ .ok_or_else(bad_resource_id)?;
+ let texture = texture_resource.0;
+
+ let descriptor = wgpu_core::resource::TextureViewDescriptor {
+ label: args.label.map(Cow::from),
+ format: args
+ .format
+ .map(|s| serialize_texture_format(&s))
+ .transpose()?,
+ dimension: args.dimension.map(|s| serialize_dimension(&s)),
+ aspect: match args.aspect {
+ Some(aspect) => match aspect.as_str() {
+ "all" => wgpu_types::TextureAspect::All,
+ "stencil-only" => wgpu_types::TextureAspect::StencilOnly,
+ "depth-only" => wgpu_types::TextureAspect::DepthOnly,
+ _ => unreachable!(),
+ },
+ None => wgpu_types::TextureAspect::All,
+ },
+ base_mip_level: args.base_mip_level.unwrap_or(0),
+ level_count: std::num::NonZeroU32::new(args.mip_level_count.unwrap_or(0)),
+ base_array_layer: args.base_array_layer.unwrap_or(0),
+ array_layer_count: std::num::NonZeroU32::new(
+ args.array_layer_count.unwrap_or(0),
+ ),
+ };
+
+ let (texture_view, maybe_err) = gfx_select!(texture => instance.texture_create_view(
+ texture,
+ &descriptor,
+ std::marker::PhantomData
+ ));
+
+ let rid = state.resource_table.add(WebGPUTextureView(texture_view));
+
+ Ok(json!({
+ "rid": rid,
+ "err": maybe_err.map(WebGPUError::from)
+ }))
+}
diff --git a/op_crates/webgpu/webgpu.idl b/op_crates/webgpu/webgpu.idl
new file mode 100644
index 000000000..598df7efb
--- /dev/null
+++ b/op_crates/webgpu/webgpu.idl
@@ -0,0 +1,1023 @@
+interface mixin GPUObjectBase {
+ attribute USVString? label;
+};
+
+dictionary GPUObjectDescriptorBase {
+ USVString label;
+};
+
+interface GPUAdapterLimits {
+ readonly attribute GPUSize32 maxTextureDimension1D;
+ readonly attribute GPUSize32 maxTextureDimension2D;
+ readonly attribute GPUSize32 maxTextureDimension3D;
+ readonly attribute GPUSize32 maxTextureArrayLayers;
+ readonly attribute GPUSize32 maxBindGroups;
+ readonly attribute GPUSize32 maxDynamicUniformBuffersPerPipelineLayout;
+ readonly attribute GPUSize32 maxDynamicStorageBuffersPerPipelineLayout;
+ readonly attribute GPUSize32 maxSampledTexturesPerShaderStage;
+ readonly attribute GPUSize32 maxSamplersPerShaderStage;
+ readonly attribute GPUSize32 maxStorageBuffersPerShaderStage;
+ readonly attribute GPUSize32 maxStorageTexturesPerShaderStage;
+ readonly attribute GPUSize32 maxUniformBuffersPerShaderStage;
+ readonly attribute GPUSize32 maxUniformBufferBindingSize;
+ readonly attribute GPUSize32 maxStorageBufferBindingSize;
+ readonly attribute GPUSize32 maxVertexBuffers;
+ readonly attribute GPUSize32 maxVertexAttributes;
+ readonly attribute GPUSize32 maxVertexBufferArrayStride;
+};
+
+interface GPUAdapterFeatures {
+ readonly setlike<GPUFeatureName>;
+};
+
+[Exposed=Window]
+partial interface Navigator {
+ [SameObject] readonly attribute GPU gpu;
+};
+
+[Exposed=DedicatedWorker]
+partial interface WorkerNavigator {
+ [SameObject] readonly attribute GPU gpu;
+};
+
+[Exposed=(Window, DedicatedWorker)]
+interface GPU {
+ Promise<GPUAdapter?> requestAdapter(optional GPURequestAdapterOptions options = {});
+};
+
+dictionary GPURequestAdapterOptions {
+ GPUPowerPreference powerPreference;
+};
+
+enum GPUPowerPreference {
+ "low-power",
+ "high-performance"
+};
+
+interface GPUAdapter {
+ readonly attribute DOMString name;
+ [SameObject] readonly attribute GPUAdapterFeatures features;
+ [SameObject] readonly attribute GPUAdapterLimits limits;
+
+ Promise<GPUDevice?> requestDevice(optional GPUDeviceDescriptor descriptor = {});
+};
+
+dictionary GPUDeviceDescriptor : GPUObjectDescriptorBase {
+ sequence<GPUFeatureName> nonGuaranteedFeatures = [];
+ record<DOMString, GPUSize32> nonGuaranteedLimits = {};
+};
+
+enum GPUFeatureName {
+ "depth-clamping",
+ "depth24unorm-stencil8",
+ "depth32float-stencil8",
+ "pipeline-statistics-query",
+ "texture-compression-bc",
+ "timestamp-query",
+};
+
+[Exposed=(Window, DedicatedWorker), Serializable]
+interface GPUDevice : EventTarget {
+ [SameObject] readonly attribute GPUAdapter adapter;
+ readonly attribute FrozenArray<GPUFeatureName> features;
+ readonly attribute object limits;
+
+ [SameObject] readonly attribute GPUQueue queue;
+
+ undefined destroy();
+
+ GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
+ GPUTexture createTexture(GPUTextureDescriptor descriptor);
+ GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {});
+
+ GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
+ GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
+ GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);
+
+ GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
+ GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
+ GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);
+ Promise<GPUComputePipeline> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor);
+ Promise<GPURenderPipeline> createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor);
+
+ GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {});
+ GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor);
+
+ GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor);
+};
+GPUDevice includes GPUObjectBase;
+
+[Serializable]
+interface GPUBuffer {
+ Promise<undefined> mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size);
+ ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size);
+ undefined unmap();
+
+ undefined destroy();
+};
+GPUBuffer includes GPUObjectBase;
+
+dictionary GPUBufferDescriptor : GPUObjectDescriptorBase {
+ required GPUSize64 size;
+ required GPUBufferUsageFlags usage;
+ boolean mappedAtCreation = false;
+};
+
+typedef [EnforceRange] unsigned long GPUBufferUsageFlags;
+interface GPUBufferUsage {
+ const GPUFlagsConstant MAP_READ = 0x0001;
+ const GPUFlagsConstant MAP_WRITE = 0x0002;
+ const GPUFlagsConstant COPY_SRC = 0x0004;
+ const GPUFlagsConstant COPY_DST = 0x0008;
+ const GPUFlagsConstant INDEX = 0x0010;
+ const GPUFlagsConstant VERTEX = 0x0020;
+ const GPUFlagsConstant UNIFORM = 0x0040;
+ const GPUFlagsConstant STORAGE = 0x0080;
+ const GPUFlagsConstant INDIRECT = 0x0100;
+ const GPUFlagsConstant QUERY_RESOLVE = 0x0200;
+};
+
+typedef [EnforceRange] unsigned long GPUMapModeFlags;
+interface GPUMapMode {
+ const GPUFlagsConstant READ = 0x0001;
+ const GPUFlagsConstant WRITE = 0x0002;
+};
+
+[Serializable]
+interface GPUTexture {
+ GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {});
+
+ undefined destroy();
+};
+GPUTexture includes GPUObjectBase;
+
+dictionary GPUTextureDescriptor : GPUObjectDescriptorBase {
+ required GPUExtent3D size;
+ GPUIntegerCoordinate mipLevelCount = 1;
+ GPUSize32 sampleCount = 1;
+ GPUTextureDimension dimension = "2d";
+ required GPUTextureFormat format;
+ required GPUTextureUsageFlags usage;
+};
+
+enum GPUTextureDimension {
+ "1d",
+ "2d",
+ "3d",
+};
+
+typedef [EnforceRange] unsigned long GPUTextureUsageFlags;
+interface GPUTextureUsage {
+ const GPUFlagsConstant COPY_SRC = 0x01;
+ const GPUFlagsConstant COPY_DST = 0x02;
+ const GPUFlagsConstant SAMPLED = 0x04;
+ const GPUFlagsConstant STORAGE = 0x08;
+ const GPUFlagsConstant RENDER_ATTACHMENT = 0x10;
+};
+
+interface GPUTextureView {
+};
+GPUTextureView includes GPUObjectBase;
+
+dictionary GPUTextureViewDescriptor : GPUObjectDescriptorBase {
+ GPUTextureFormat format;
+ GPUTextureViewDimension dimension;
+ GPUTextureAspect aspect = "all";
+ GPUIntegerCoordinate baseMipLevel = 0;
+ GPUIntegerCoordinate mipLevelCount;
+ GPUIntegerCoordinate baseArrayLayer = 0;
+ GPUIntegerCoordinate arrayLayerCount;
+};
+
+enum GPUTextureViewDimension {
+ "1d",
+ "2d",
+ "2d-array",
+ "cube",
+ "cube-array",
+ "3d"
+};
+
+enum GPUTextureAspect {
+ "all",
+ "stencil-only",
+ "depth-only"
+};
+
+enum GPUTextureFormat {
+ // 8-bit formats
+ "r8unorm",
+ "r8snorm",
+ "r8uint",
+ "r8sint",
+
+ // 16-bit formats
+ "r16uint",
+ "r16sint",
+ "r16float",
+ "rg8unorm",
+ "rg8snorm",
+ "rg8uint",
+ "rg8sint",
+
+ // 32-bit formats
+ "r32uint",
+ "r32sint",
+ "r32float",
+ "rg16uint",
+ "rg16sint",
+ "rg16float",
+ "rgba8unorm",
+ "rgba8unorm-srgb",
+ "rgba8snorm",
+ "rgba8uint",
+ "rgba8sint",
+ "bgra8unorm",
+ "bgra8unorm-srgb",
+ // Packed 32-bit formats
+ "rgb9e5ufloat",
+ "rgb10a2unorm",
+ "rg11b10ufloat",
+
+ // 64-bit formats
+ "rg32uint",
+ "rg32sint",
+ "rg32float",
+ "rgba16uint",
+ "rgba16sint",
+ "rgba16float",
+
+ // 128-bit formats
+ "rgba32uint",
+ "rgba32sint",
+ "rgba32float",
+
+ // Depth and stencil formats
+ "stencil8",
+ "depth16unorm",
+ "depth24plus",
+ "depth24plus-stencil8",
+ "depth32float",
+
+ // BC compressed formats usable if "texture-compression-bc" is both
+ // supported by the device/user agent and enabled in requestDevice.
+ "bc1-rgba-unorm",
+ "bc1-rgba-unorm-srgb",
+ "bc2-rgba-unorm",
+ "bc2-rgba-unorm-srgb",
+ "bc3-rgba-unorm",
+ "bc3-rgba-unorm-srgb",
+ "bc4-r-unorm",
+ "bc4-r-snorm",
+ "bc5-rg-unorm",
+ "bc5-rg-snorm",
+ "bc6h-rgb-ufloat",
+ "bc6h-rgb-float",
+ "bc7-rgba-unorm",
+ "bc7-rgba-unorm-srgb",
+
+ // "depth24unorm-stencil8" feature
+ "depth24unorm-stencil8",
+
+ // "depth32float-stencil8" feature
+ "depth32float-stencil8",
+};
+
+interface GPUSampler {
+};
+GPUSampler includes GPUObjectBase;
+
+dictionary GPUSamplerDescriptor : GPUObjectDescriptorBase {
+ GPUAddressMode addressModeU = "clamp-to-edge";
+ GPUAddressMode addressModeV = "clamp-to-edge";
+ GPUAddressMode addressModeW = "clamp-to-edge";
+ GPUFilterMode magFilter = "nearest";
+ GPUFilterMode minFilter = "nearest";
+ GPUFilterMode mipmapFilter = "nearest";
+ float lodMinClamp = 0;
+ float lodMaxClamp = 0xffffffff; // TODO: What should this be? Was Number.MAX_VALUE.
+ GPUCompareFunction compare;
+ [Clamp] unsigned short maxAnisotropy = 1;
+};
+
+enum GPUAddressMode {
+ "clamp-to-edge",
+ "repeat",
+ "mirror-repeat"
+};
+
+enum GPUFilterMode {
+ "nearest",
+ "linear"
+};
+
+enum GPUCompareFunction {
+ "never",
+ "less",
+ "equal",
+ "less-equal",
+ "greater",
+ "not-equal",
+ "greater-equal",
+ "always"
+};
+
+[Serializable]
+interface GPUBindGroupLayout {
+};
+GPUBindGroupLayout includes GPUObjectBase;
+
+dictionary GPUBindGroupLayoutDescriptor : GPUObjectDescriptorBase {
+ required sequence<GPUBindGroupLayoutEntry> entries;
+};
+
+typedef [EnforceRange] unsigned long GPUShaderStageFlags;
+interface GPUShaderStage {
+ const GPUFlagsConstant VERTEX = 0x1;
+ const GPUFlagsConstant FRAGMENT = 0x2;
+ const GPUFlagsConstant COMPUTE = 0x4;
+};
+
+dictionary GPUBindGroupLayoutEntry {
+ required GPUIndex32 binding;
+ required GPUShaderStageFlags visibility;
+
+ GPUBufferBindingLayout buffer;
+ GPUSamplerBindingLayout sampler;
+ GPUTextureBindingLayout texture;
+ GPUStorageTextureBindingLayout storageTexture;
+};
+
+enum GPUBufferBindingType {
+ "uniform",
+ "storage",
+ "read-only-storage",
+};
+
+dictionary GPUBufferBindingLayout {
+ GPUBufferBindingType type = "uniform";
+ boolean hasDynamicOffset = false;
+ GPUSize64 minBindingSize = 0;
+};
+
+enum GPUSamplerBindingType {
+ "filtering",
+ "non-filtering",
+ "comparison",
+};
+
+dictionary GPUSamplerBindingLayout {
+ GPUSamplerBindingType type = "filtering";
+};
+
+enum GPUTextureSampleType {
+ "float",
+ "unfilterable-float",
+ "depth",
+ "sint",
+ "uint",
+};
+
+dictionary GPUTextureBindingLayout {
+ GPUTextureSampleType sampleType = "float";
+ GPUTextureViewDimension viewDimension = "2d";
+ boolean multisampled = false;
+};
+
+enum GPUStorageTextureAccess {
+ "read-only",
+ "write-only",
+};
+
+dictionary GPUStorageTextureBindingLayout {
+ required GPUStorageTextureAccess access;
+ required GPUTextureFormat format;
+ GPUTextureViewDimension viewDimension = "2d";
+};
+
+interface GPUBindGroup {
+};
+GPUBindGroup includes GPUObjectBase;
+
+dictionary GPUBindGroupDescriptor : GPUObjectDescriptorBase {
+ required GPUBindGroupLayout layout;
+ required sequence<GPUBindGroupEntry> entries;
+};
+
+typedef (GPUSampler or GPUTextureView or GPUBufferBinding) GPUBindingResource;
+
+dictionary GPUBindGroupEntry {
+ required GPUIndex32 binding;
+ required GPUBindingResource resource;
+};
+
+dictionary GPUBufferBinding {
+ required GPUBuffer buffer;
+ GPUSize64 offset = 0;
+ GPUSize64 size;
+};
+
+[Serializable]
+interface GPUPipelineLayout {
+};
+GPUPipelineLayout includes GPUObjectBase;
+
+dictionary GPUPipelineLayoutDescriptor : GPUObjectDescriptorBase {
+ required sequence<GPUBindGroupLayout> bindGroupLayouts;
+};
+
+enum GPUCompilationMessageType {
+ "error",
+ "warning",
+ "info"
+};
+
+[Serializable]
+interface GPUCompilationMessage {
+ readonly attribute DOMString message;
+ readonly attribute GPUCompilationMessageType type;
+ readonly attribute unsigned long long lineNum;
+ readonly attribute unsigned long long linePos;
+};
+
+[Serializable]
+interface GPUCompilationInfo {
+ readonly attribute FrozenArray<GPUCompilationMessage> messages;
+};
+
+[Serializable]
+interface GPUShaderModule {
+ Promise<GPUCompilationInfo> compilationInfo();
+};
+GPUShaderModule includes GPUObjectBase;
+
+dictionary GPUShaderModuleDescriptor : GPUObjectDescriptorBase {
+ required USVString code;
+ object sourceMap;
+};
+
+dictionary GPUPipelineDescriptorBase : GPUObjectDescriptorBase {
+ GPUPipelineLayout layout;
+};
+
+interface mixin GPUPipelineBase {
+ GPUBindGroupLayout getBindGroupLayout(unsigned long index);
+};
+
+dictionary GPUProgrammableStage {
+ required GPUShaderModule module;
+ required USVString entryPoint;
+};
+
+[Serializable]
+interface GPUComputePipeline {
+};
+GPUComputePipeline includes GPUObjectBase;
+GPUComputePipeline includes GPUPipelineBase;
+
+dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
+ required GPUProgrammableStage compute;
+};
+
+[Serializable]
+interface GPURenderPipeline {
+};
+GPURenderPipeline includes GPUObjectBase;
+GPURenderPipeline includes GPUPipelineBase;
+
+dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
+ required GPUVertexState vertex;
+ GPUPrimitiveState primitive = {};
+ GPUDepthStencilState depthStencil;
+ GPUMultisampleState multisample = {};
+ GPUFragmentState fragment;
+};
+
+enum GPUPrimitiveTopology {
+ "point-list",
+ "line-list",
+ "line-strip",
+ "triangle-list",
+ "triangle-strip"
+};
+
+dictionary GPUPrimitiveState {
+ GPUPrimitiveTopology topology = "triangle-list";
+ GPUIndexFormat stripIndexFormat;
+ GPUFrontFace frontFace = "ccw";
+ GPUCullMode cullMode = "none";
+};
+
+enum GPUFrontFace {
+ "ccw",
+ "cw"
+};
+
+enum GPUCullMode {
+ "none",
+ "front",
+ "back"
+};
+
+dictionary GPUMultisampleState {
+ GPUSize32 count = 1;
+ GPUSampleMask mask = 0xFFFFFFFF;
+ boolean alphaToCoverageEnabled = false;
+};
+
+dictionary GPUFragmentState: GPUProgrammableStage {
+ required sequence<GPUColorTargetState> targets;
+};
+
+dictionary GPUColorTargetState {
+ required GPUTextureFormat format;
+
+ GPUBlendState blend;
+ GPUColorWriteFlags writeMask = 0xF; // GPUColorWrite.ALL
+};
+
+dictionary GPUBlendState {
+ required GPUBlendComponent color;
+ required GPUBlendComponent alpha;
+};
+
+typedef [EnforceRange] unsigned long GPUColorWriteFlags;
+interface GPUColorWrite {
+ const GPUFlagsConstant RED = 0x1;
+ const GPUFlagsConstant GREEN = 0x2;
+ const GPUFlagsConstant BLUE = 0x4;
+ const GPUFlagsConstant ALPHA = 0x8;
+ const GPUFlagsConstant ALL = 0xF;
+};
+
+dictionary GPUBlendComponent {
+ GPUBlendFactor srcFactor = "one";
+ GPUBlendFactor dstFactor = "zero";
+ GPUBlendOperation operation = "add";
+};
+
+enum GPUBlendFactor {
+ "zero",
+ "one",
+ "src-color",
+ "one-minus-src-color",
+ "src-alpha",
+ "one-minus-src-alpha",
+ "dst-color",
+ "one-minus-dst-color",
+ "dst-alpha",
+ "one-minus-dst-alpha",
+ "src-alpha-saturated",
+ "blend-color",
+ "one-minus-blend-color"
+};
+
+enum GPUBlendOperation {
+ "add",
+ "subtract",
+ "reverse-subtract",
+ "min",
+ "max"
+};
+
+dictionary GPUDepthStencilState {
+ required GPUTextureFormat format;
+
+ boolean depthWriteEnabled = false;
+ GPUCompareFunction depthCompare = "always";
+
+ GPUStencilFaceState stencilFront = {};
+ GPUStencilFaceState stencilBack = {};
+
+ GPUStencilValue stencilReadMask = 0xFFFFFFFF;
+ GPUStencilValue stencilWriteMask = 0xFFFFFFFF;
+
+ GPUDepthBias depthBias = 0;
+ float depthBiasSlopeScale = 0;
+ float depthBiasClamp = 0;
+
+ // Enable depth clamping (requires "depth-clamping" feature)
+ boolean clampDepth = false;
+};
+
+dictionary GPUStencilFaceState {
+ GPUCompareFunction compare = "always";
+ GPUStencilOperation failOp = "keep";
+ GPUStencilOperation depthFailOp = "keep";
+ GPUStencilOperation passOp = "keep";
+};
+
+enum GPUStencilOperation {
+ "keep",
+ "zero",
+ "replace",
+ "invert",
+ "increment-clamp",
+ "decrement-clamp",
+ "increment-wrap",
+ "decrement-wrap"
+};
+
+enum GPUIndexFormat {
+ "uint16",
+ "uint32"
+};
+
+enum GPUVertexFormat {
+ "uchar2",
+ "uchar4",
+ "char2",
+ "char4",
+ "uchar2norm",
+ "uchar4norm",
+ "char2norm",
+ "char4norm",
+ "ushort2",
+ "ushort4",
+ "short2",
+ "short4",
+ "ushort2norm",
+ "ushort4norm",
+ "short2norm",
+ "short4norm",
+ "half2",
+ "half4",
+ "float",
+ "float2",
+ "float3",
+ "float4",
+ "uint",
+ "uint2",
+ "uint3",
+ "uint4",
+ "int",
+ "int2",
+ "int3",
+ "int4"
+};
+
+enum GPUInputStepMode {
+ "vertex",
+ "instance"
+};
+
+dictionary GPUVertexState: GPUProgrammableStage {
+ sequence<GPUVertexBufferLayout?> buffers = [];
+};
+
+dictionary GPUVertexBufferLayout {
+ required GPUSize64 arrayStride;
+ GPUInputStepMode stepMode = "vertex";
+ required sequence<GPUVertexAttribute> attributes;
+};
+
+dictionary GPUVertexAttribute {
+ required GPUVertexFormat format;
+ required GPUSize64 offset;
+
+ required GPUIndex32 shaderLocation;
+};
+
+interface GPUCommandBuffer {
+ readonly attribute Promise<double> executionTime;
+};
+GPUCommandBuffer includes GPUObjectBase;
+
+dictionary GPUCommandBufferDescriptor : GPUObjectDescriptorBase {
+};
+
+interface GPUCommandEncoder {
+ GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
+ GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {});
+
+ undefined copyBufferToBuffer(
+ GPUBuffer source,
+ GPUSize64 sourceOffset,
+ GPUBuffer destination,
+ GPUSize64 destinationOffset,
+ GPUSize64 size);
+
+ undefined copyBufferToTexture(
+ GPUImageCopyBuffer source,
+ GPUImageCopyTexture destination,
+ GPUExtent3D copySize);
+
+ undefined copyTextureToBuffer(
+ GPUImageCopyTexture source,
+ GPUImageCopyBuffer destination,
+ GPUExtent3D copySize);
+
+ undefined copyTextureToTexture(
+ GPUImageCopyTexture source,
+ GPUImageCopyTexture destination,
+ GPUExtent3D copySize);
+
+ undefined pushDebugGroup(USVString groupLabel);
+ undefined popDebugGroup();
+ undefined insertDebugMarker(USVString markerLabel);
+
+ undefined writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);
+
+ undefined resolveQuerySet(
+ GPUQuerySet querySet,
+ GPUSize32 firstQuery,
+ GPUSize32 queryCount,
+ GPUBuffer destination,
+ GPUSize64 destinationOffset);
+
+ GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {});
+};
+GPUCommandEncoder includes GPUObjectBase;
+
+dictionary GPUCommandEncoderDescriptor : GPUObjectDescriptorBase {
+ boolean measureExecutionTime = false;
+
+ // TODO: reusability flag?
+};
+
+dictionary GPUImageDataLayout {
+ GPUSize64 offset = 0;
+ GPUSize32 bytesPerRow;
+ GPUSize32 rowsPerImage;
+};
+
+dictionary GPUImageCopyBuffer : GPUImageDataLayout {
+ required GPUBuffer buffer;
+};
+
+dictionary GPUImageCopyTexture {
+ required GPUTexture texture;
+ GPUIntegerCoordinate mipLevel = 0;
+ GPUOrigin3D origin = {};
+ GPUTextureAspect aspect = "all";
+};
+
+interface mixin GPUProgrammablePassEncoder {
+ undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
+ optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);
+
+ undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
+ Uint32Array dynamicOffsetsData,
+ GPUSize64 dynamicOffsetsDataStart,
+ GPUSize32 dynamicOffsetsDataLength);
+
+ undefined pushDebugGroup(USVString groupLabel);
+ undefined popDebugGroup();
+ undefined insertDebugMarker(USVString markerLabel);
+};
+
+interface GPUComputePassEncoder {
+ undefined setPipeline(GPUComputePipeline pipeline);
+ undefined dispatch(GPUSize32 x, optional GPUSize32 y = 1, optional GPUSize32 z = 1);
+ undefined dispatchIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
+
+ undefined beginPipelineStatisticsQuery(GPUQuerySet querySet, GPUSize32 queryIndex);
+ undefined endPipelineStatisticsQuery();
+
+ undefined writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);
+
+ undefined endPass();
+};
+GPUComputePassEncoder includes GPUObjectBase;
+GPUComputePassEncoder includes GPUProgrammablePassEncoder;
+
+dictionary GPUComputePassDescriptor : GPUObjectDescriptorBase {
+};
+
+interface mixin GPURenderEncoderBase {
+ undefined setPipeline(GPURenderPipeline pipeline);
+
+ undefined setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size = 0);
+ undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer buffer, optional GPUSize64 offset = 0, optional GPUSize64 size = 0);
+
+ undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1,
+ optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0);
+ undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1,
+ optional GPUSize32 firstIndex = 0,
+ optional GPUSignedOffset32 baseVertex = 0,
+ optional GPUSize32 firstInstance = 0);
+
+ undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
+ undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
+};
+
+interface GPURenderPassEncoder {
+ undefined setViewport(float x, float y,
+ float width, float height,
+ float minDepth, float maxDepth);
+
+ undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y,
+ GPUIntegerCoordinate width, GPUIntegerCoordinate height);
+
+ undefined setBlendColor(GPUColor color);
+ undefined setStencilReference(GPUStencilValue reference);
+
+ undefined beginOcclusionQuery(GPUSize32 queryIndex);
+ undefined endOcclusionQuery();
+
+ undefined beginPipelineStatisticsQuery(GPUQuerySet querySet, GPUSize32 queryIndex);
+ undefined endPipelineStatisticsQuery();
+
+ undefined writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);
+
+ undefined executeBundles(sequence<GPURenderBundle> bundles);
+ undefined endPass();
+};
+GPURenderPassEncoder includes GPUObjectBase;
+GPURenderPassEncoder includes GPUProgrammablePassEncoder;
+GPURenderPassEncoder includes GPURenderEncoderBase;
+
+dictionary GPURenderPassDescriptor : GPUObjectDescriptorBase {
+ required sequence<GPURenderPassColorAttachment> colorAttachments;
+ GPURenderPassDepthStencilAttachment depthStencilAttachment;
+ GPUQuerySet occlusionQuerySet;
+};
+
+dictionary GPURenderPassColorAttachment {
+ required GPUTextureView view;
+ GPUTextureView resolveTarget;
+
+ required (GPULoadOp or GPUColor) loadValue;
+ GPUStoreOp storeOp = "store";
+};
+
+dictionary GPURenderPassDepthStencilAttachment {
+ required GPUTextureView view;
+
+ required (GPULoadOp or float) depthLoadValue;
+ required GPUStoreOp depthStoreOp;
+ boolean depthReadOnly = false;
+
+ required (GPULoadOp or GPUStencilValue) stencilLoadValue;
+ required GPUStoreOp stencilStoreOp;
+ boolean stencilReadOnly = false;
+};
+
+enum GPULoadOp {
+ "load"
+};
+
+enum GPUStoreOp {
+ "store",
+ "clear"
+};
+
+interface GPURenderBundle {
+};
+GPURenderBundle includes GPUObjectBase;
+
+dictionary GPURenderBundleDescriptor : GPUObjectDescriptorBase {
+};
+
+interface GPURenderBundleEncoder {
+ GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {});
+};
+GPURenderBundleEncoder includes GPUObjectBase;
+GPURenderBundleEncoder includes GPUProgrammablePassEncoder;
+GPURenderBundleEncoder includes GPURenderEncoderBase;
+
+dictionary GPURenderBundleEncoderDescriptor : GPUObjectDescriptorBase {
+ required sequence<GPUTextureFormat> colorFormats;
+ GPUTextureFormat depthStencilFormat;
+ GPUSize32 sampleCount = 1;
+};
+
+interface GPUQueue {
+ undefined submit(sequence<GPUCommandBuffer> commandBuffers);
+
+ Promise<undefined> onSubmittedWorkDone();
+
+ undefined writeBuffer(
+ GPUBuffer buffer,
+ GPUSize64 bufferOffset,
+ [AllowShared] BufferSource data,
+ optional GPUSize64 dataOffset = 0,
+ optional GPUSize64 size);
+
+ undefined writeTexture(
+ GPUImageCopyTexture destination,
+ [AllowShared] BufferSource data,
+ GPUImageDataLayout dataLayout,
+ GPUExtent3D size);
+};
+GPUQueue includes GPUObjectBase;
+
+interface GPUQuerySet {
+ undefined destroy();
+};
+GPUQuerySet includes GPUObjectBase;
+
+dictionary GPUQuerySetDescriptor : GPUObjectDescriptorBase {
+ required GPUQueryType type;
+ required GPUSize32 count;
+ sequence<GPUPipelineStatisticName> pipelineStatistics = [];
+};
+
+enum GPUQueryType {
+ "occlusion",
+ "pipeline-statistics",
+ "timestamp"
+};
+
+enum GPUPipelineStatisticName {
+ "vertex-shader-invocations",
+ "clipper-invocations",
+ "clipper-primitives-out",
+ "fragment-shader-invocations",
+ "compute-shader-invocations"
+};
+
+enum GPUDeviceLostReason {
+ "destroyed",
+};
+
+interface GPUDeviceLostInfo {
+ readonly attribute (GPUDeviceLostReason or undefined) reason;
+ readonly attribute DOMString message;
+};
+
+partial interface GPUDevice {
+ readonly attribute Promise<GPUDeviceLostInfo> lost;
+};
+
+enum GPUErrorFilter {
+ "out-of-memory",
+ "validation"
+};
+
+interface GPUOutOfMemoryError {
+ constructor();
+};
+
+interface GPUValidationError {
+ constructor(DOMString message);
+ readonly attribute DOMString message;
+};
+
+typedef (GPUOutOfMemoryError or GPUValidationError) GPUError;
+
+partial interface GPUDevice {
+ undefined pushErrorScope(GPUErrorFilter filter);
+ Promise<GPUError?> popErrorScope();
+};
+
+[
+ Exposed=(Window, DedicatedWorker)
+]
+interface GPUUncapturedErrorEvent : Event {
+ constructor(
+ DOMString type,
+ GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict
+ );
+ [SameObject] readonly attribute GPUError error;
+};
+
+dictionary GPUUncapturedErrorEventInit : EventInit {
+ required GPUError error;
+};
+
+partial interface GPUDevice {
+ [Exposed=(Window, DedicatedWorker)]
+ attribute EventHandler onuncapturederror;
+};
+
+typedef [EnforceRange] unsigned long GPUBufferDynamicOffset;
+typedef [EnforceRange] unsigned long GPUStencilValue;
+typedef [EnforceRange] unsigned long GPUSampleMask;
+typedef [EnforceRange] long GPUDepthBias;
+
+typedef [EnforceRange] unsigned long long GPUSize64;
+typedef [EnforceRange] unsigned long GPUIntegerCoordinate;
+typedef [EnforceRange] unsigned long GPUIndex32;
+typedef [EnforceRange] unsigned long GPUSize32;
+typedef [EnforceRange] long GPUSignedOffset32;
+
+typedef unsigned long GPUFlagsConstant;
+
+dictionary GPUColorDict {
+ required double r;
+ required double g;
+ required double b;
+ required double a;
+};
+typedef (sequence<double> or GPUColorDict) GPUColor;
+
+dictionary GPUOrigin2DDict {
+ GPUIntegerCoordinate x = 0;
+ GPUIntegerCoordinate y = 0;
+};
+typedef (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict) GPUOrigin2D;
+
+dictionary GPUOrigin3DDict {
+ GPUIntegerCoordinate x = 0;
+ GPUIntegerCoordinate y = 0;
+ GPUIntegerCoordinate z = 0;
+};
+typedef (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict) GPUOrigin3D;
+
+dictionary GPUExtent3DDict {
+ GPUIntegerCoordinate width = 1;
+ GPUIntegerCoordinate height = 1;
+ GPUIntegerCoordinate depthOrArrayLayers = 1;
+};
+typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index cdae5a40c..30912c078 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -23,6 +23,7 @@ deno_crypto = { path = "../op_crates/crypto", version = "0.13.0" }
deno_fetch = { path = "../op_crates/fetch", version = "0.22.0" }
deno_web = { path = "../op_crates/web", version = "0.30.0" }
deno_websocket = { path = "../op_crates/websocket", version = "0.5.0" }
+deno_webgpu = { path = "../op_crates/webgpu", version = "0.1.0" }
[target.'cfg(windows)'.build-dependencies]
winres = "0.1.11"
@@ -34,6 +35,7 @@ deno_crypto = { path = "../op_crates/crypto", version = "0.13.0" }
deno_fetch = { path = "../op_crates/fetch", version = "0.22.0" }
deno_web = { path = "../op_crates/web", version = "0.30.0" }
deno_websocket = { path = "../op_crates/websocket", version = "0.5.0" }
+deno_webgpu = { path = "../op_crates/webgpu", version = "0.1.0" }
atty = "0.2.14"
dlopen = "0.1.8"
diff --git a/runtime/build.rs b/runtime/build.rs
index 5a99b86c8..248148568 100644
--- a/runtime/build.rs
+++ b/runtime/build.rs
@@ -17,6 +17,7 @@ fn create_snapshot(
deno_fetch::init(&mut js_runtime);
deno_websocket::init(&mut js_runtime);
deno_crypto::init(&mut js_runtime);
+ deno_webgpu::init(&mut js_runtime);
// TODO(nayeemrmn): https://github.com/rust-lang/cargo/issues/3946 to get the
// workspace root.
let display_root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
diff --git a/runtime/errors.rs b/runtime/errors.rs
index b3d474504..a8152b075 100644
--- a/runtime/errors.rs
+++ b/runtime/errors.rs
@@ -151,6 +151,7 @@ fn get_nix_error_class(error: &nix::Error) -> &'static str {
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
+ .or_else(|| deno_webgpu::error::get_error_class_name(e))
.or_else(|| {
e.downcast_ref::<dlopen::Error>()
.map(get_dlopen_error_class)
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index 485080812..811412049 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -27,6 +27,7 @@ delete Object.prototype.__proto__;
const headers = window.__bootstrap.headers;
const streams = window.__bootstrap.streams;
const fileReader = window.__bootstrap.fileReader;
+ const webgpu = window.__bootstrap.webgpu;
const webSocket = window.__bootstrap.webSocket;
const file = window.__bootstrap.file;
const fetch = window.__bootstrap.fetch;
@@ -195,6 +196,11 @@ delete Object.prototype.__proto__;
core.registerErrorClass("SyntaxError", SyntaxError);
core.registerErrorClass("TypeError", TypeError);
core.registerErrorClass("URIError", URIError);
+ core.registerErrorClass(
+ "DOMExceptionOperationError",
+ DOMException,
+ "OperationError",
+ );
}
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
@@ -249,6 +255,37 @@ delete Object.prototype.__proto__;
performance: util.writable(performance.performance),
setInterval: util.writable(timers.setInterval),
setTimeout: util.writable(timers.setTimeout),
+
+ GPU: util.nonEnumerable(webgpu.GPU),
+ GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter),
+ GPUAdapterLimits: util.nonEnumerable(webgpu.GPUAdapterLimits),
+ GPUAdapterFeatures: util.nonEnumerable(webgpu.GPUAdapterFeatures),
+ GPUDevice: util.nonEnumerable(webgpu.GPUDevice),
+ GPUQueue: util.nonEnumerable(webgpu.GPUQueue),
+ GPUBuffer: util.nonEnumerable(webgpu.GPUBuffer),
+ GPUBufferUsage: util.nonEnumerable(webgpu.GPUBufferUsage),
+ GPUMapMode: util.nonEnumerable(webgpu.GPUMapMode),
+ GPUTexture: util.nonEnumerable(webgpu.GPUTexture),
+ GPUTextureUsage: util.nonEnumerable(webgpu.GPUTextureUsage),
+ GPUTextureView: util.nonEnumerable(webgpu.GPUTextureView),
+ GPUSampler: util.nonEnumerable(webgpu.GPUSampler),
+ GPUBindGroupLayout: util.nonEnumerable(webgpu.GPUBindGroupLayout),
+ GPUPipelineLayout: util.nonEnumerable(webgpu.GPUPipelineLayout),
+ GPUBindGroup: util.nonEnumerable(webgpu.GPUBindGroup),
+ GPUShaderModule: util.nonEnumerable(webgpu.GPUShaderModule),
+ GPUShaderStage: util.nonEnumerable(webgpu.GPUShaderStage),
+ GPUComputePipeline: util.nonEnumerable(webgpu.GPUComputePipeline),
+ GPURenderPipeline: util.nonEnumerable(webgpu.GPURenderPipeline),
+ GPUColorWrite: util.nonEnumerable(webgpu.GPUColorWrite),
+ GPUCommandEncoder: util.nonEnumerable(webgpu.GPUCommandEncoder),
+ GPURenderPassEncoder: util.nonEnumerable(webgpu.GPURenderPassEncoder),
+ GPUComputePassEncoder: util.nonEnumerable(webgpu.GPUComputePassEncoder),
+ GPUCommandBuffer: util.nonEnumerable(webgpu.GPUCommandBuffer),
+ GPURenderBundleEncoder: util.nonEnumerable(webgpu.GPURenderBundleEncoder),
+ GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle),
+ GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet),
+ GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError),
+ GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError),
};
// The console seems to be the only one that should be writable and non-enumerable
@@ -256,12 +293,17 @@ delete Object.prototype.__proto__;
// structure, it might be worth it to define a helper in `util`
windowOrWorkerGlobalScope.console.enumerable = false;
+ const windowNavigatorProperties = {
+ gpu: webgpu.gpu,
+ };
+
const mainRuntimeGlobalProperties = {
Location: location.locationConstructorDescriptor,
location: location.locationDescriptor,
Window: globalInterfaces.windowConstructorDescriptor,
window: util.readOnly(globalThis),
self: util.readOnly(globalThis),
+ navigator: util.readOnly(windowNavigatorProperties),
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
// it seems those two properties should be available to workers as well
onload: util.writable(null),
@@ -273,12 +315,17 @@ delete Object.prototype.__proto__;
prompt: util.writable(prompt.prompt),
};
+ const workerNavigatorProperties = {
+ gpu: webgpu.gpu,
+ };
+
const workerRuntimeGlobalProperties = {
WorkerLocation: location.workerLocationConstructorDescriptor,
location: location.workerLocationDescriptor,
WorkerGlobalScope: globalInterfaces.workerGlobalScopeConstructorDescriptor,
DedicatedWorkerGlobalScope:
globalInterfaces.dedicatedWorkerGlobalScopeConstructorDescriptor,
+ navigator: util.readOnly(workerNavigatorProperties),
self: util.readOnly(globalThis),
onmessage: util.writable(onmessage),
onerror: util.writable(onerror),
diff --git a/runtime/lib.rs b/runtime/lib.rs
index 0d2179494..c523b24b7 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -10,6 +10,7 @@ extern crate log;
pub use deno_crypto;
pub use deno_fetch;
pub use deno_web;
+pub use deno_webgpu;
pub use deno_websocket;
pub mod colors;
diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs
index e5dba723a..0ef04ff3c 100644
--- a/runtime/ops/mod.rs
+++ b/runtime/ops/mod.rs
@@ -21,6 +21,7 @@ pub mod timers;
pub mod tls;
pub mod tty;
pub mod web_worker;
+pub mod webgpu;
pub mod websocket;
pub mod worker_host;
diff --git a/runtime/ops/webgpu.rs b/runtime/ops/webgpu.rs
new file mode 100644
index 000000000..d3b9cfa19
--- /dev/null
+++ b/runtime/ops/webgpu.rs
@@ -0,0 +1,421 @@
+use deno_webgpu::*;
+
+pub fn init(rt: &mut deno_core::JsRuntime) {
+ {
+ let op_state = rt.op_state();
+ let mut state = op_state.borrow_mut();
+ state.put(wgpu_core::hub::Global::new(
+ "webgpu",
+ wgpu_core::hub::IdentityManagerFactory,
+ wgpu_types::BackendBit::PRIMARY,
+ ));
+ let unstable_checker = state.borrow::<super::UnstableChecker>();
+ let unstable = unstable_checker.unstable;
+ state.put(Unstable(unstable));
+ }
+
+ super::reg_json_async(
+ rt,
+ "op_webgpu_request_adapter",
+ op_webgpu_request_adapter,
+ );
+ super::reg_json_async(
+ rt,
+ "op_webgpu_request_device",
+ op_webgpu_request_device,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_query_set",
+ op_webgpu_create_query_set,
+ );
+
+ {
+ // buffer
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_buffer",
+ buffer::op_webgpu_create_buffer,
+ );
+ super::reg_json_async(
+ rt,
+ "op_webgpu_buffer_get_map_async",
+ buffer::op_webgpu_buffer_get_map_async,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_buffer_get_mapped_range",
+ buffer::op_webgpu_buffer_get_mapped_range,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_buffer_unmap",
+ buffer::op_webgpu_buffer_unmap,
+ );
+ }
+ {
+ // texture
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_texture",
+ texture::op_webgpu_create_texture,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_texture_view",
+ texture::op_webgpu_create_texture_view,
+ );
+ }
+ {
+ // sampler
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_sampler",
+ sampler::op_webgpu_create_sampler,
+ );
+ }
+ {
+ // binding
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_bind_group_layout",
+ binding::op_webgpu_create_bind_group_layout,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_pipeline_layout",
+ binding::op_webgpu_create_pipeline_layout,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_bind_group",
+ binding::op_webgpu_create_bind_group,
+ );
+ }
+ {
+ // pipeline
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_compute_pipeline",
+ pipeline::op_webgpu_create_compute_pipeline,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pipeline_get_bind_group_layout",
+ pipeline::op_webgpu_compute_pipeline_get_bind_group_layout,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_render_pipeline",
+ pipeline::op_webgpu_create_render_pipeline,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pipeline_get_bind_group_layout",
+ pipeline::op_webgpu_render_pipeline_get_bind_group_layout,
+ );
+ }
+ {
+ // command_encoder
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_command_encoder",
+ command_encoder::op_webgpu_create_command_encoder,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_begin_render_pass",
+ command_encoder::op_webgpu_command_encoder_begin_render_pass,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_begin_compute_pass",
+ command_encoder::op_webgpu_command_encoder_begin_compute_pass,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_copy_buffer_to_buffer",
+ command_encoder::op_webgpu_command_encoder_copy_buffer_to_buffer,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_copy_buffer_to_texture",
+ command_encoder::op_webgpu_command_encoder_copy_buffer_to_texture,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_copy_texture_to_buffer",
+ command_encoder::op_webgpu_command_encoder_copy_texture_to_buffer,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_copy_texture_to_texture",
+ command_encoder::op_webgpu_command_encoder_copy_texture_to_texture,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_push_debug_group",
+ command_encoder::op_webgpu_command_encoder_push_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_pop_debug_group",
+ command_encoder::op_webgpu_command_encoder_pop_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_insert_debug_marker",
+ command_encoder::op_webgpu_command_encoder_insert_debug_marker,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_write_timestamp",
+ command_encoder::op_webgpu_command_encoder_write_timestamp,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_resolve_query_set",
+ command_encoder::op_webgpu_command_encoder_resolve_query_set,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_command_encoder_finish",
+ command_encoder::op_webgpu_command_encoder_finish,
+ );
+ }
+ {
+ // render_pass
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_viewport",
+ render_pass::op_webgpu_render_pass_set_viewport,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_scissor_rect",
+ render_pass::op_webgpu_render_pass_set_scissor_rect,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_blend_color",
+ render_pass::op_webgpu_render_pass_set_blend_color,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_stencil_reference",
+ render_pass::op_webgpu_render_pass_set_stencil_reference,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_begin_pipeline_statistics_query",
+ render_pass::op_webgpu_render_pass_begin_pipeline_statistics_query,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_end_pipeline_statistics_query",
+ render_pass::op_webgpu_render_pass_end_pipeline_statistics_query,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_write_timestamp",
+ render_pass::op_webgpu_render_pass_write_timestamp,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_execute_bundles",
+ render_pass::op_webgpu_render_pass_execute_bundles,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_end_pass",
+ render_pass::op_webgpu_render_pass_end_pass,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_bind_group",
+ render_pass::op_webgpu_render_pass_set_bind_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_push_debug_group",
+ render_pass::op_webgpu_render_pass_push_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_pop_debug_group",
+ render_pass::op_webgpu_render_pass_pop_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_insert_debug_marker",
+ render_pass::op_webgpu_render_pass_insert_debug_marker,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_pipeline",
+ render_pass::op_webgpu_render_pass_set_pipeline,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_index_buffer",
+ render_pass::op_webgpu_render_pass_set_index_buffer,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_set_vertex_buffer",
+ render_pass::op_webgpu_render_pass_set_vertex_buffer,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_draw",
+ render_pass::op_webgpu_render_pass_draw,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_draw_indexed",
+ render_pass::op_webgpu_render_pass_draw_indexed,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_draw_indirect",
+ render_pass::op_webgpu_render_pass_draw_indirect,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_pass_draw_indexed_indirect",
+ render_pass::op_webgpu_render_pass_draw_indexed_indirect,
+ );
+ }
+ {
+ // compute_pass
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_set_pipeline",
+ compute_pass::op_webgpu_compute_pass_set_pipeline,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_dispatch",
+ compute_pass::op_webgpu_compute_pass_dispatch,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_dispatch_indirect",
+ compute_pass::op_webgpu_compute_pass_dispatch_indirect,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_end_pass",
+ compute_pass::op_webgpu_compute_pass_end_pass,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_set_bind_group",
+ compute_pass::op_webgpu_compute_pass_set_bind_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_push_debug_group",
+ compute_pass::op_webgpu_compute_pass_push_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_pop_debug_group",
+ compute_pass::op_webgpu_compute_pass_pop_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_compute_pass_insert_debug_marker",
+ compute_pass::op_webgpu_compute_pass_insert_debug_marker,
+ );
+ }
+ {
+ // bundle
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_render_bundle_encoder",
+ bundle::op_webgpu_create_render_bundle_encoder,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_finish",
+ bundle::op_webgpu_render_bundle_encoder_finish,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_set_bind_group",
+ bundle::op_webgpu_render_bundle_encoder_set_bind_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_push_debug_group",
+ bundle::op_webgpu_render_bundle_encoder_push_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_pop_debug_group",
+ bundle::op_webgpu_render_bundle_encoder_pop_debug_group,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_insert_debug_marker",
+ bundle::op_webgpu_render_bundle_encoder_insert_debug_marker,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_set_pipeline",
+ bundle::op_webgpu_render_bundle_encoder_set_pipeline,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_set_index_buffer",
+ bundle::op_webgpu_render_bundle_encoder_set_index_buffer,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_set_vertex_buffer",
+ bundle::op_webgpu_render_bundle_encoder_set_vertex_buffer,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_draw",
+ bundle::op_webgpu_render_bundle_encoder_draw,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_draw_indexed",
+ bundle::op_webgpu_render_bundle_encoder_draw_indexed,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_render_bundle_encoder_draw_indirect",
+ bundle::op_webgpu_render_bundle_encoder_draw_indirect,
+ );
+ }
+ {
+ // queue
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_queue_submit",
+ queue::op_webgpu_queue_submit,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_write_buffer",
+ queue::op_webgpu_write_buffer,
+ );
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_write_texture",
+ queue::op_webgpu_write_texture,
+ );
+ }
+ {
+ // shader
+ super::reg_json_sync(
+ rt,
+ "op_webgpu_create_shader_module",
+ shader::op_webgpu_create_shader_module,
+ );
+ }
+}
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index 082cf7267..7f258b3eb 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -237,6 +237,7 @@ impl WebWorker {
deno_web::op_domain_to_ascii,
);
ops::io::init(js_runtime);
+ ops::webgpu::init(js_runtime);
ops::websocket::init(
js_runtime,
options.user_agent.clone(),
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 0b3fe3e10..be6951f51 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -142,6 +142,7 @@ impl MainWorker {
ops::signal::init(js_runtime);
ops::tls::init(js_runtime);
ops::tty::init(js_runtime);
+ ops::webgpu::init(js_runtime);
ops::websocket::init(
js_runtime,
options.user_agent.clone(),