1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-21 15:04:11 -05:00

feat: WebGPU API (#7977)

Co-authored-by: Luca Casonato <lucacasonato@yahoo.com>
This commit is contained in:
crowlKats 2021-03-01 11:31:13 +01:00 committed by GitHub
parent dbdbe7a1cf
commit 7cd14f97c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 15302 additions and 1 deletions

557
Cargo.lock generated
View file

@ -16,6 +16,12 @@ version = "0.2.3"
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"
@ -73,6 +79,15 @@ version = "0.5.2"
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"
@ -180,6 +195,21 @@ version = "0.13.0"
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"
@ -198,6 +228,12 @@ dependencies = [
"wyz",
]
[[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"
@ -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"
@ -270,6 +309,12 @@ version = "1.0.0"
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"
@ -298,6 +343,55 @@ dependencies = [
"vec_map",
]
[[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"
@ -343,6 +437,17 @@ dependencies = [
"lazy_static",
]
[[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"
@ -538,6 +643,7 @@ dependencies = [
"deno_crypto",
"deno_fetch",
"deno_web",
"deno_webgpu",
"deno_websocket",
"dlopen",
"encoding_rs",
@ -579,6 +685,17 @@ dependencies = [
"serde",
]
[[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"
@ -840,6 +957,21 @@ version = "1.0.7"
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"
@ -1059,6 +1191,205 @@ dependencies = [
"wasi 0.10.0+wasi-snapshot-preview1",
]
[[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"
@ -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"
@ -1246,6 +1580,12 @@ dependencies = [
"libc",
]
[[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"
@ -1301,6 +1641,15 @@ version = "0.4.7"
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"
@ -1326,6 +1675,16 @@ dependencies = [
"winapi-build",
]
[[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"
@ -1351,6 +1710,16 @@ version = "0.2.86"
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"
@ -1435,6 +1804,15 @@ dependencies = [
"syn 1.0.60",
]
[[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"
@ -1453,6 +1831,20 @@ version = "2.3.4"
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"
@ -1492,6 +1884,23 @@ dependencies = [
"winapi 0.3.9",
]
[[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"
@ -1600,6 +2009,25 @@ dependencies = [
"libc",
]
[[package]]
name = "objc"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
dependencies = [
"malloc_buf",
"objc_exception",
]
[[package]]
name = "objc_exception"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4"
dependencies = [
"cc",
]
[[package]]
name = "once_cell"
version = "1.5.2"
@ -2012,6 +2440,21 @@ dependencies = [
"rand_core 0.5.1",
]
[[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"
@ -2128,6 +2571,17 @@ dependencies = [
"winapi 0.3.9",
]
[[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"
@ -2349,6 +2803,12 @@ version = "0.4.2"
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"
@ -2388,6 +2848,27 @@ version = "0.5.2"
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"
@ -2400,6 +2881,15 @@ version = "1.1.0"
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"
@ -2925,6 +3415,12 @@ dependencies = [
"once_cell",
]
[[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"
@ -3087,9 +3583,21 @@ 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"
@ -3468,6 +3976,46 @@ dependencies = [
"webpki",
]
[[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"
@ -3554,6 +4102,15 @@ dependencies = [
"toml",
]
[[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"

View file

@ -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());
@ -260,6 +262,10 @@ fn main() {
"cargo:rustc-env=DENO_FETCH_LIB_PATH={}",
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()

View file

@ -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.

View file

@ -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 {

View file

@ -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

View file

@ -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() {

View file

@ -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";

View file

@ -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]));
});

View file

@ -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]);
}

Binary file not shown.

View file

@ -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);
}

View file

@ -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 =

View file

@ -1204,6 +1204,7 @@
window.removeEventListener = EventTarget.prototype.removeEventListener;
window.__bootstrap = (window.__bootstrap || {});
window.__bootstrap.eventTarget = {
EventTarget,
setEventTargetData,
};
window.__bootstrap.event = {

View file

@ -270,6 +270,10 @@ declare namespace globalThis {
): (v: any, opts: ValueConverterOpts) => any;
}
declare var eventTarget: {
EventTarget: typeof EventTarget;
};
declare var url: {
URLSearchParams: typeof URLSearchParams;
};

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -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"

View file

@ -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

362
op_crates/webgpu/binding.rs Normal file
View file

@ -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)
}))
}

243
op_crates/webgpu/buffer.rs Normal file
View file

@ -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) }))
}

465
op_crates/webgpu/bundle.rs Normal file
View file

@ -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!({}))
}

View file

@ -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)
}))
}

View file

@ -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!({}))
}

252
op_crates/webgpu/error.rs Normal file
View file

@ -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")
}

1126
op_crates/webgpu/lib.deno_webgpu.d.ts vendored Normal file

File diff suppressed because it is too large Load diff

541
op_crates/webgpu/lib.rs Normal file
View file

@ -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),
}))
}

View file

@ -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),
}))
}

157
op_crates/webgpu/queue.rs Normal file
View file

@ -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) }))
}

View file

@ -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!({}))
}

129
op_crates/webgpu/sampler.rs Normal file
View file

@ -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)
}))
}

View file

@ -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)
}))
}

256
op_crates/webgpu/texture.rs Normal file
View file

@ -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)
}))
}

1023
op_crates/webgpu/webgpu.idl Normal file

File diff suppressed because it is too large Load diff

View file

@ -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"

View file

@ -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();

View file

@ -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)

View file

@ -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),

View file

@ -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;

View file

@ -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;

421
runtime/ops/webgpu.rs Normal file
View file

@ -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,
);
}
}

View file

@ -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(),

View file

@ -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(),