mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-04-16 05:56:37 +00:00
refactor(deno): use object wrap (#7113)
This commit is contained in:
parent
0f111cb27c
commit
f0bf789f58
@ -9,8 +9,13 @@ skip-tree = [
|
||||
{ name = "miniz_oxide", version = "0.7.4" },
|
||||
|
||||
# introduced by Deno, to be investigated
|
||||
{ name = "deno_core", version = "0.321.0" },
|
||||
{ name = "deno_permissions", version = "0.39.0" },
|
||||
{ name = "strum_macros", version = "0.25.3" },
|
||||
{ name = "petgraph", version = "0.6.5" },
|
||||
{ name = "base64-simd", version = "0.7.0" },
|
||||
{ name = "bit-set", version = "0.5.3" },
|
||||
{ name = "bit-vec", version = "0.6.3" },
|
||||
{ name = "capacity_builder", version = "0.1.3" },
|
||||
{ name = "itertools", version = "0.10.5" },
|
||||
]
|
||||
skip = [
|
||||
# Strum uses an old version
|
||||
|
171
Cargo.lock
generated
171
Cargo.lock
generated
@ -289,6 +289,12 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "az"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
@ -540,6 +546,35 @@ dependencies = [
|
||||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "capacity_builder"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58ec49028cb308564429cd8fac4ef21290067a0afe8f5955330a8d487d0d790c"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "capacity_builder"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f2d24a6dcf0cd402a21b65d35340f3a49ff3475dc5fdac91d22d2733e6641c6"
|
||||
dependencies = [
|
||||
"capacity_builder_macros",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "capacity_builder_macros"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b4a6cae9efc04cc6cbb8faf338d2c497c165c83e74509cf4dbedea948bbf6e5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
@ -914,27 +949,31 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deno_console"
|
||||
version = "0.179.0"
|
||||
version = "0.190.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e09f2bbb2d842329b602da25dbab5cd4a342f9a8adcb7c02509fc322f796e79"
|
||||
checksum = "94352b8d75c288a26ef748ad0ddae07e181109374a02c547850f96eef76b5389"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_core"
|
||||
version = "0.321.0"
|
||||
version = "0.336.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd2a54cda74cdc187d5fc2d23370a45cf09f912caf566dd1cd24a50157d809c7"
|
||||
checksum = "fdd50476c4325d5fa52bb906804a1e35b127d2a1dcf674e3447b53dcf25525bf"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"az",
|
||||
"bincode",
|
||||
"bit-set 0.5.3",
|
||||
"bit-vec 0.6.3",
|
||||
"bytes",
|
||||
"capacity_builder 0.1.3",
|
||||
"cooked-waker",
|
||||
"deno_core_icudata",
|
||||
"deno_error",
|
||||
"deno_ops",
|
||||
"deno_path_util",
|
||||
"deno_unsync",
|
||||
"futures",
|
||||
"indexmap",
|
||||
@ -949,6 +988,7 @@ dependencies = [
|
||||
"smallvec",
|
||||
"sourcemap",
|
||||
"static_assertions",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
"url",
|
||||
"v8",
|
||||
@ -962,11 +1002,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695"
|
||||
|
||||
[[package]]
|
||||
name = "deno_ops"
|
||||
version = "0.197.0"
|
||||
name = "deno_error"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37a8825d92301cf445727c43f17fee2a20fcdf4370004339965156ae7c56c97e"
|
||||
checksum = "9c23dbc46d5804814b08b4675838f9884e3a52916987ec5105af36d42f9911b5"
|
||||
dependencies = [
|
||||
"deno_error_macro",
|
||||
"libc",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_error_macro"
|
||||
version = "0.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_ops"
|
||||
version = "0.212.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2d328067139909aa81522a5d90f119368b541fbddd73ab630e4d9f777865f0d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"proc-macro-rules",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -974,27 +1040,31 @@ dependencies = [
|
||||
"strum 0.25.0",
|
||||
"strum_macros 0.25.3",
|
||||
"syn",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_path_util"
|
||||
version = "0.2.1"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff25f6e08e7a0214bbacdd6f7195c7f1ebcd850c87a624e4ff06326b68b42d99"
|
||||
checksum = "c87b8996966ae1b13ee9c20219b1d10fc53905b9570faae6adfa34614fd15224"
|
||||
dependencies = [
|
||||
"deno_error",
|
||||
"percent-encoding",
|
||||
"thiserror 1.0.69",
|
||||
"sys_traits",
|
||||
"thiserror 2.0.11",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_permissions"
|
||||
version = "0.39.0"
|
||||
version = "0.49.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14e822f98185ab3ddf06104b2407681e0008af52361af32f1cd171b7eda5aa59"
|
||||
checksum = "abf879dff0b3de4dbcb78d6dda3a55e711369d5b9f479270a82853ef106c4176"
|
||||
dependencies = [
|
||||
"capacity_builder 0.5.0",
|
||||
"deno_core",
|
||||
"deno_error",
|
||||
"deno_path_util",
|
||||
"deno_terminal",
|
||||
"fqdn",
|
||||
@ -1003,8 +1073,8 @@ dependencies = [
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
"which 4.4.2",
|
||||
"thiserror 2.0.11",
|
||||
"which",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@ -1031,43 +1101,48 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deno_url"
|
||||
version = "0.179.0"
|
||||
version = "0.190.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad9a108794e505f2b07665e19ff336c1bcba6adcf7182c90c1d3a6c741d7fcd0"
|
||||
checksum = "d79e743ad841f7826d46c6944580f5ba665fe9ab4c31a68c4eed8b5a78225da3"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"thiserror 1.0.69",
|
||||
"deno_error",
|
||||
"thiserror 2.0.11",
|
||||
"urlpattern",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_web"
|
||||
version = "0.210.0"
|
||||
version = "0.221.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7679087bcc41f7ae3385f8c12d43bc81cfc54cb9b1ef73983d20f5e39fa4e0da"
|
||||
checksum = "8041ba73bb2f238c61b5e4ed341d2fe1f9464a71115a240ab3390480b3c10e12"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64-simd 0.8.0",
|
||||
"bytes",
|
||||
"deno_core",
|
||||
"deno_error",
|
||||
"deno_permissions",
|
||||
"encoding_rs",
|
||||
"flate2",
|
||||
"futures",
|
||||
"serde",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deno_webgpu"
|
||||
version = "0.146.0"
|
||||
version = "0.157.0"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
"hashbrown",
|
||||
"deno_error",
|
||||
"deno_unsync",
|
||||
"indexmap",
|
||||
"raw-window-handle 0.6.2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.11",
|
||||
"tokio",
|
||||
"wgpu-core",
|
||||
@ -1076,9 +1151,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deno_webidl"
|
||||
version = "0.179.0"
|
||||
version = "0.190.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b55d845e3d64f8de7eff67aaa4b6fe1b23bbc2efe967c984f8c64c8dd85fad4"
|
||||
checksum = "c4ff81a990196bf3a80fe5d339b4eb8b411ef17634d60d399a63bae6e71a37c9"
|
||||
dependencies = [
|
||||
"deno_core",
|
||||
]
|
||||
@ -1230,7 +1305,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1929,7 +2004,7 @@ checksum = "e19b23d53f35ce9f56aebc7d1bb4e6ac1e9c0db7ac85c8d1760c04379edced37"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2059,7 +2134,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3259,7 +3334,7 @@ dependencies = [
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3380,14 +3455,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_v8"
|
||||
version = "0.230.0"
|
||||
version = "0.245.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5a783242d2af51d6955cc04bf2b64adb643ab588b61e9573c908a69dabf8c2f"
|
||||
checksum = "945f93c91e0c7e4799b5fefff076756141aae92e262c4dc4833310dd3d2d845e"
|
||||
dependencies = [
|
||||
"deno_error",
|
||||
"num-bigint",
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thiserror 1.0.69",
|
||||
"thiserror 2.0.11",
|
||||
"v8",
|
||||
]
|
||||
|
||||
@ -3665,6 +3741,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sys_traits"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "638f0e61b5134e56b2abdf4c704fd44672603f15ca09013f314649056f3fee4d"
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
@ -3921,7 +4003,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69fff37da548239c3bf9e64a12193d261e8b22b660991c6fd2df057c168f435f"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"windows-targets 0.48.5",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4082,7 +4164,7 @@ dependencies = [
|
||||
"miniz_oxide 0.7.4",
|
||||
"once_cell",
|
||||
"paste",
|
||||
"which 6.0.3",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4225,11 +4307,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm_dep_analyzer"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f270206a91783fd90625c8bb0d8fbd459d0b1d1bf209b656f713f01ae7c04b8"
|
||||
checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca"
|
||||
dependencies = [
|
||||
"thiserror 1.0.69",
|
||||
"deno_error",
|
||||
"thiserror 2.0.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4654,18 +4737,6 @@ dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "6.0.3"
|
||||
@ -4700,7 +4771,7 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
16
Cargo.toml
16
Cargo.toml
@ -183,19 +183,21 @@ web-sys = { version = "0.3.74", default-features = false }
|
||||
web-time = "1.1.0"
|
||||
|
||||
# deno dependencies
|
||||
deno_console = "0.179.0"
|
||||
deno_core = "0.321.0"
|
||||
deno_url = "0.179.0"
|
||||
deno_web = "0.210.0"
|
||||
deno_webidl = "0.179.0"
|
||||
deno_webgpu = { version = "0.146.0", path = "./deno_webgpu" }
|
||||
deno_console = "0.190.0"
|
||||
deno_core = "0.336.0"
|
||||
deno_url = "0.190.0"
|
||||
deno_web = "0.221.0"
|
||||
deno_webidl = "0.190.0"
|
||||
deno_webgpu = { version = "0.157.0", path = "./deno_webgpu" }
|
||||
deno_unsync = "0.4.2"
|
||||
deno_error = "0.5.5"
|
||||
tokio = "1.41.1"
|
||||
termcolor = "1.4.1"
|
||||
|
||||
# android dependencies
|
||||
ndk-sys = "0.5.0"
|
||||
|
||||
# These overrides allow our examples to explicitly depend on release crates
|
||||
# These overrides allow our examples to explicitly depend on release crates
|
||||
[patch.crates-io]
|
||||
wgpu = { path = "./wgpu" }
|
||||
|
||||
|
@ -21,78 +21,6 @@ mod native {
|
||||
use termcolor::ColorSpec;
|
||||
use termcolor::WriteColor;
|
||||
|
||||
// temporary
|
||||
fn get_webgpu_error_class(e: &deno_webgpu::InitError) -> &'static str {
|
||||
match e {
|
||||
deno_webgpu::InitError::Resource(e) => {
|
||||
deno_core::error::get_custom_error_class(e).unwrap_or("Error")
|
||||
}
|
||||
deno_webgpu::InitError::RequestDevice(_) => "DOMExceptionOperationError",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_webgpu_buffer_error_class(e: &deno_webgpu::buffer::BufferError) -> &'static str {
|
||||
match e {
|
||||
deno_webgpu::buffer::BufferError::Resource(e) => {
|
||||
deno_core::error::get_custom_error_class(e).unwrap_or("Error")
|
||||
}
|
||||
deno_webgpu::buffer::BufferError::InvalidUsage => "TypeError",
|
||||
deno_webgpu::buffer::BufferError::Access(_) => "DOMExceptionOperationError",
|
||||
deno_webgpu::buffer::BufferError::Canceled(_) => "Error",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_webgpu_bundle_error_class(e: &deno_webgpu::bundle::BundleError) -> &'static str {
|
||||
match e {
|
||||
deno_webgpu::bundle::BundleError::Resource(e) => {
|
||||
deno_core::error::get_custom_error_class(e).unwrap_or("Error")
|
||||
}
|
||||
deno_webgpu::bundle::BundleError::InvalidSize => "TypeError",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_webgpu_byow_error_class(e: &deno_webgpu::byow::ByowError) -> &'static str {
|
||||
match e {
|
||||
deno_webgpu::byow::ByowError::WebGPUNotInitiated => "TypeError",
|
||||
deno_webgpu::byow::ByowError::InvalidParameters => "TypeError",
|
||||
deno_webgpu::byow::ByowError::CreateSurface(_) => "Error",
|
||||
deno_webgpu::byow::ByowError::InvalidSystem => "TypeError",
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "linux",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
deno_webgpu::byow::ByowError::NullWindow => "TypeError",
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
|
||||
deno_webgpu::byow::ByowError::NullDisplay => "TypeError",
|
||||
#[cfg(target_os = "macos")]
|
||||
deno_webgpu::byow::ByowError::NSViewDisplay => "TypeError",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_webgpu_render_pass_error_class(
|
||||
e: &deno_webgpu::render_pass::RenderPassError,
|
||||
) -> &'static str {
|
||||
match e {
|
||||
deno_webgpu::render_pass::RenderPassError::Resource(e) => {
|
||||
deno_core::error::get_custom_error_class(e).unwrap_or("Error")
|
||||
}
|
||||
deno_webgpu::render_pass::RenderPassError::InvalidSize => "TypeError",
|
||||
deno_webgpu::render_pass::RenderPassError::RenderPass(_) => "Error",
|
||||
}
|
||||
}
|
||||
|
||||
fn get_webgpu_surface_error_class(e: &deno_webgpu::surface::SurfaceError) -> &'static str {
|
||||
match e {
|
||||
deno_webgpu::surface::SurfaceError::Resource(e) => {
|
||||
deno_core::error::get_custom_error_class(e).unwrap_or("Error")
|
||||
}
|
||||
deno_webgpu::surface::SurfaceError::Surface(_) => "Error",
|
||||
deno_webgpu::surface::SurfaceError::InvalidStatus => "Error",
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run() -> Result<(), AnyError> {
|
||||
let mut args_iter = env::args();
|
||||
let _ = args_iter.next();
|
||||
@ -106,7 +34,6 @@ mod native {
|
||||
|
||||
let options = RuntimeOptions {
|
||||
module_loader: Some(Rc::new(deno_core::FsModuleLoader)),
|
||||
get_error_class_fn: Some(&get_error_class_name),
|
||||
extensions: vec![
|
||||
deno_webidl::deno_webidl::init_ops_and_esm(),
|
||||
deno_console::deno_console::init_ops_and_esm(),
|
||||
@ -161,13 +88,13 @@ mod native {
|
||||
);
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_exit(code: i32) -> Result<(), AnyError> {
|
||||
fn op_exit(code: i32) {
|
||||
std::process::exit(code)
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[buffer]
|
||||
fn op_read_file_sync(#[string] path: &str) -> Result<Vec<u8>, AnyError> {
|
||||
fn op_read_file_sync(#[string] path: &str) -> Result<Vec<u8>, std::io::Error> {
|
||||
let path = std::path::Path::new(path);
|
||||
let mut file = std::fs::File::open(path)?;
|
||||
let mut buf = Vec::new();
|
||||
@ -176,44 +103,16 @@ mod native {
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_write_file_sync(#[string] path: &str, #[buffer] buf: &[u8]) -> Result<(), AnyError> {
|
||||
fn op_write_file_sync(
|
||||
#[string] path: &str,
|
||||
#[buffer] buf: &[u8],
|
||||
) -> Result<(), std::io::Error> {
|
||||
let path = std::path::Path::new(path);
|
||||
let mut file = std::fs::File::create(path)?;
|
||||
file.write_all(buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_error_class_name(e: &AnyError) -> &'static str {
|
||||
deno_core::error::get_custom_error_class(e)
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<deno_webgpu::InitError>()
|
||||
.map(get_webgpu_error_class)
|
||||
})
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<deno_webgpu::buffer::BufferError>()
|
||||
.map(get_webgpu_buffer_error_class)
|
||||
})
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<deno_webgpu::bundle::BundleError>()
|
||||
.map(get_webgpu_bundle_error_class)
|
||||
})
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<deno_webgpu::byow::ByowError>()
|
||||
.map(get_webgpu_byow_error_class)
|
||||
})
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<deno_webgpu::render_pass::RenderPassError>()
|
||||
.map(get_webgpu_render_pass_error_class)
|
||||
})
|
||||
.or_else(|| {
|
||||
e.downcast_ref::<deno_webgpu::surface::SurfaceError>()
|
||||
.map(get_webgpu_surface_error_class)
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
panic!("Error '{e}' contains boxed error of unsupported type: {e:#}");
|
||||
})
|
||||
}
|
||||
|
||||
pub fn unwrap_or_exit<T>(result: Result<T, AnyError>) -> T {
|
||||
match result {
|
||||
Ok(value) => value,
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
import { core } from "ext:core/mod.js";
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
// @ts-check
|
||||
/// <reference path="../../core/lib.deno_core.d.ts" />
|
||||
@ -7,146 +7,17 @@
|
||||
/// <reference path="./lib.deno_webgpu.d.ts" />
|
||||
|
||||
import { primordials } from "ext:core/mod.js";
|
||||
import {
|
||||
op_webgpu_surface_configure,
|
||||
op_webgpu_surface_create,
|
||||
op_webgpu_surface_get_current_texture,
|
||||
op_webgpu_surface_present,
|
||||
} from "ext:core/ops";
|
||||
import { GPUCanvasContext, UnsafeWindowSurface } from "ext:core/ops";
|
||||
const {
|
||||
ObjectDefineProperty,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
|
||||
import * as webidl from "ext:deno_webidl/00_webidl.js";
|
||||
import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
|
||||
import { loadWebGPU } from "ext:deno_webgpu/00_init.js";
|
||||
|
||||
const _surfaceRid = Symbol("[[surfaceRid]]");
|
||||
const _configuration = Symbol("[[configuration]]");
|
||||
const _canvas = Symbol("[[canvas]]");
|
||||
const _currentTexture = Symbol("[[currentTexture]]");
|
||||
const _present = Symbol("[[present]]");
|
||||
const _dim = Symbol("[[dimensions]]");
|
||||
|
||||
class GPUCanvasContext {
|
||||
/** @type {number} */
|
||||
[_surfaceRid];
|
||||
[_configuration];
|
||||
[_canvas];
|
||||
/** @type {GPUTexture | undefined} */
|
||||
[_currentTexture];
|
||||
[_dim];
|
||||
|
||||
get canvas() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
return this[_canvas];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
webidl.illegalConstructor();
|
||||
}
|
||||
|
||||
configure(configuration) {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
configuration = webidl.converters.GPUCanvasConfiguration(configuration, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
|
||||
const { _device, assertDevice } = loadWebGPU();
|
||||
this[_device] = configuration.device[_device];
|
||||
this[_configuration] = configuration;
|
||||
const device = assertDevice(this, {
|
||||
prefix,
|
||||
context: "configuration.device",
|
||||
});
|
||||
|
||||
const { err } = op_webgpu_surface_configure({
|
||||
surfaceRid: this[_surfaceRid],
|
||||
deviceRid: device.rid,
|
||||
format: configuration.format,
|
||||
viewFormats: configuration.viewFormats,
|
||||
usage: configuration.usage,
|
||||
width: this[_dim].width,
|
||||
height: this[_dim].height,
|
||||
alphaMode: configuration.alphaMode,
|
||||
});
|
||||
|
||||
device.pushError(err);
|
||||
}
|
||||
|
||||
unconfigure() {
|
||||
const { _device } = loadWebGPU();
|
||||
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
|
||||
this[_configuration] = null;
|
||||
this[_device] = null;
|
||||
}
|
||||
|
||||
getCurrentTexture() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
const prefix =
|
||||
"Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
|
||||
|
||||
if (this[_configuration] === null) {
|
||||
throw new DOMException("Context is not configured", "InvalidStateError");
|
||||
}
|
||||
const { createGPUTexture, assertDevice } = loadWebGPU();
|
||||
|
||||
const device = assertDevice(this, { prefix, context: "this" });
|
||||
|
||||
if (this[_currentTexture]) {
|
||||
return this[_currentTexture];
|
||||
}
|
||||
|
||||
const { rid } = op_webgpu_surface_get_current_texture(
|
||||
device.rid,
|
||||
this[_surfaceRid],
|
||||
);
|
||||
|
||||
const texture = createGPUTexture(
|
||||
{
|
||||
size: {
|
||||
width: this[_configuration].width,
|
||||
height: this[_configuration].height,
|
||||
depthOrArrayLayers: 1,
|
||||
},
|
||||
mipLevelCount: 1,
|
||||
sampleCount: 1,
|
||||
dimension: "2d",
|
||||
format: this[_configuration].format,
|
||||
usage: this[_configuration].usage,
|
||||
},
|
||||
device,
|
||||
rid,
|
||||
);
|
||||
device.trackResource(texture);
|
||||
this[_currentTexture] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
// Required to present the texture; browser don't need this.
|
||||
[_present]() {
|
||||
const { assertDevice } = loadWebGPU();
|
||||
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
|
||||
const device = assertDevice(this[_currentTexture], {
|
||||
prefix,
|
||||
context: "this",
|
||||
});
|
||||
op_webgpu_surface_present(device.rid, this[_surfaceRid]);
|
||||
this[_currentTexture].destroy();
|
||||
this[_currentTexture] = undefined;
|
||||
}
|
||||
|
||||
[SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
|
||||
ObjectDefineProperty(GPUCanvasContext, SymbolFor("Deno.privateCustomInspect"), {
|
||||
__proto__: null,
|
||||
value(inspect, inspectOptions) {
|
||||
return inspect(
|
||||
createFilteredInspectProxy({
|
||||
object: this,
|
||||
@ -157,59 +28,8 @@ class GPUCanvasContext {
|
||||
}),
|
||||
inspectOptions,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
const GPUCanvasContextPrototype = GPUCanvasContext.prototype;
|
||||
|
||||
function createCanvasContext(options) {
|
||||
// lazy load webgpu if needed
|
||||
const canvasContext = webidl.createBranded(GPUCanvasContext);
|
||||
canvasContext[_surfaceRid] = options.surfaceRid;
|
||||
canvasContext[_canvas] = options.canvas;
|
||||
canvasContext[_dim] = { width: options.width, height: options.height };
|
||||
return canvasContext;
|
||||
}
|
||||
|
||||
// External webgpu surfaces
|
||||
|
||||
// TODO(@littledivy): This will extend `OffscreenCanvas` when we add it.
|
||||
class UnsafeWindowSurface {
|
||||
#ctx;
|
||||
#surfaceRid;
|
||||
#options;
|
||||
|
||||
constructor(options) {
|
||||
if (typeof options !== "object") {
|
||||
throw new TypeError("options must be provided.");
|
||||
}
|
||||
if (
|
||||
typeof options.width !== "number" || typeof options.height !== "number"
|
||||
) {
|
||||
throw new TypeError("width and height must be provided.");
|
||||
}
|
||||
|
||||
this.#surfaceRid = op_webgpu_surface_create(
|
||||
options.system,
|
||||
options.windowHandle,
|
||||
options.displayHandle,
|
||||
);
|
||||
this.#options = options;
|
||||
}
|
||||
|
||||
getContext(context) {
|
||||
if (context !== "webgpu") {
|
||||
throw new TypeError("Only 'webgpu' context is supported");
|
||||
}
|
||||
this.#ctx = createCanvasContext({
|
||||
surfaceRid: this.#surfaceRid,
|
||||
...this.#options,
|
||||
});
|
||||
return this.#ctx;
|
||||
}
|
||||
|
||||
present() {
|
||||
this.#ctx[_present]();
|
||||
}
|
||||
}
|
||||
|
||||
export { GPUCanvasContext, UnsafeWindowSurface };
|
||||
|
@ -1,13 +1,13 @@
|
||||
# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
# Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
[package]
|
||||
name = "deno_webgpu"
|
||||
version = "0.146.0"
|
||||
version = "0.157.0"
|
||||
authors = ["the Deno authors"]
|
||||
edition.workspace = true
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
repository.workspace = true
|
||||
repository = "https://github.com/gfx-rs/wgpu"
|
||||
description = "WebGPU implementation for Deno"
|
||||
|
||||
[lib]
|
||||
@ -28,11 +28,14 @@ wgpu-core = { workspace = true, features = [
|
||||
wgpu-types = { workspace = true, features = ["serde"] }
|
||||
|
||||
deno_core.workspace = true
|
||||
hashbrown = { workspace = true, features = ["serde"] }
|
||||
raw-window-handle = { workspace = true }
|
||||
deno_error.workspace = true
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
thiserror.workspace = true
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
raw-window-handle = { workspace = true }
|
||||
thiserror.workspace = true
|
||||
indexmap.workspace = true
|
||||
serde_json.workspace = true
|
||||
deno_unsync.workspace = true
|
||||
|
||||
# Apple Platforms
|
||||
#
|
||||
|
471
deno_webgpu/adapter.rs
Normal file
471
deno_webgpu/adapter.rs
Normal file
@ -0,0 +1,471 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
use std::collections::HashSet;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::cppgc::SameObject;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::OpState;
|
||||
use deno_core::WebIDL;
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
use super::device::GPUDevice;
|
||||
use crate::webidl::features_to_feature_names;
|
||||
use crate::webidl::GPUFeatureName;
|
||||
use crate::Instance;
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURequestAdapterOptions {
|
||||
pub power_preference: Option<GPUPowerPreference>,
|
||||
#[webidl(default = false)]
|
||||
pub force_fallback_adapter: bool,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUPowerPreference {
|
||||
LowPower,
|
||||
HighPerformance,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
struct GPUDeviceDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
label: String,
|
||||
|
||||
#[webidl(default = vec![])]
|
||||
required_features: Vec<GPUFeatureName>,
|
||||
#[webidl(default = Default::default())]
|
||||
#[options(enforce_range = true)]
|
||||
required_limits: indexmap::IndexMap<String, Option<u64>>,
|
||||
}
|
||||
|
||||
pub struct GPUAdapter {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::AdapterId,
|
||||
|
||||
pub features: SameObject<GPUSupportedFeatures>,
|
||||
pub limits: SameObject<GPUSupportedLimits>,
|
||||
pub info: Rc<SameObject<GPUAdapterInfo>>,
|
||||
}
|
||||
|
||||
impl Drop for GPUAdapter {
|
||||
fn drop(&mut self) {
|
||||
self.instance.adapter_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUAdapter {}
|
||||
|
||||
#[op2]
|
||||
impl GPUAdapter {
|
||||
#[getter]
|
||||
#[global]
|
||||
fn info(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
||||
self.info.get(scope, |_| {
|
||||
let info = self.instance.adapter_get_info(self.id);
|
||||
let limits = self.instance.adapter_limits(self.id);
|
||||
|
||||
GPUAdapterInfo {
|
||||
info,
|
||||
subgroup_min_size: limits.min_subgroup_size,
|
||||
subgroup_max_size: limits.max_subgroup_size,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[global]
|
||||
fn features(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
||||
self.features.get(scope, |scope| {
|
||||
let features = self.instance.adapter_features(self.id);
|
||||
let features = features_to_feature_names(features);
|
||||
GPUSupportedFeatures::new(scope, features)
|
||||
})
|
||||
}
|
||||
#[getter]
|
||||
#[global]
|
||||
fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
||||
self.limits.get(scope, |_| {
|
||||
let adapter_limits = self.instance.adapter_limits(self.id);
|
||||
GPUSupportedLimits(adapter_limits)
|
||||
})
|
||||
}
|
||||
#[getter]
|
||||
fn is_fallback_adapter(&self) -> bool {
|
||||
// TODO(lucacasonato): report correctly from wgpu
|
||||
false
|
||||
}
|
||||
|
||||
#[async_method(fake)]
|
||||
#[global]
|
||||
fn request_device(
|
||||
&self,
|
||||
state: &mut OpState,
|
||||
isolate_ptr: *mut v8::Isolate,
|
||||
scope: &mut v8::HandleScope,
|
||||
#[webidl] descriptor: GPUDeviceDescriptor,
|
||||
) -> Result<v8::Global<v8::Value>, CreateDeviceError> {
|
||||
let features = self.instance.adapter_features(self.id);
|
||||
let supported_features = features_to_feature_names(features);
|
||||
#[allow(clippy::disallowed_types)]
|
||||
let required_features = descriptor
|
||||
.required_features
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
if !required_features.is_subset(&supported_features) {
|
||||
return Err(CreateDeviceError::RequiredFeaturesNotASubset);
|
||||
}
|
||||
|
||||
let required_limits =
|
||||
serde_json::from_value(serde_json::to_value(descriptor.required_limits)?)?;
|
||||
|
||||
let wgpu_descriptor = wgpu_types::DeviceDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
required_features: super::webidl::feature_names_to_features(
|
||||
descriptor.required_features,
|
||||
),
|
||||
required_limits,
|
||||
memory_hints: Default::default(),
|
||||
};
|
||||
|
||||
let webgpu_trace = std::env::var("DENO_WEBGPU_TRACE").unwrap();
|
||||
|
||||
let (device, queue) = self.instance.adapter_request_device(
|
||||
self.id,
|
||||
&wgpu_descriptor,
|
||||
Some(webgpu_trace.as_str()),
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let (lost_sender, lost_receiver) = tokio::sync::oneshot::channel();
|
||||
let (uncaptured_sender, mut uncaptured_receiver) = tokio::sync::mpsc::unbounded_channel();
|
||||
let (uncaptured_sender_is_closed_sender, mut uncaptured_sender_is_closed_receiver) =
|
||||
tokio::sync::oneshot::channel::<()>();
|
||||
|
||||
let device = GPUDevice {
|
||||
instance: self.instance.clone(),
|
||||
id: device,
|
||||
queue,
|
||||
label: descriptor.label,
|
||||
queue_obj: SameObject::new(),
|
||||
adapter_info: self.info.clone(),
|
||||
error_handler: Arc::new(super::error::DeviceErrorHandler::new(
|
||||
lost_sender,
|
||||
uncaptured_sender,
|
||||
uncaptured_sender_is_closed_sender,
|
||||
)),
|
||||
adapter: self.id,
|
||||
lost_receiver: Mutex::new(Some(lost_receiver)),
|
||||
limits: SameObject::new(),
|
||||
features: SameObject::new(),
|
||||
};
|
||||
let device = deno_core::cppgc::make_cppgc_object(scope, device);
|
||||
let event_target_setup = state.borrow::<crate::EventTargetSetup>();
|
||||
let webidl_brand = v8::Local::new(scope, event_target_setup.brand.clone());
|
||||
device.set(scope, webidl_brand, webidl_brand);
|
||||
let set_event_target_data =
|
||||
v8::Local::new(scope, event_target_setup.set_event_target_data.clone())
|
||||
.cast::<v8::Function>();
|
||||
let null = v8::null(scope);
|
||||
set_event_target_data.call(scope, null.into(), &[device.into()]);
|
||||
|
||||
let key = v8::String::new(scope, "dispatchEvent").unwrap();
|
||||
let val = device.get(scope, key.into()).unwrap();
|
||||
let func = v8::Global::new(scope, val.try_cast::<v8::Function>().unwrap());
|
||||
let device = v8::Global::new(scope, device.cast::<v8::Value>());
|
||||
let error_event_class = state.borrow::<crate::ErrorEventClass>().0.clone();
|
||||
|
||||
let context = scope.get_current_context();
|
||||
let context = v8::Global::new(scope, context);
|
||||
|
||||
let task_device = device.clone();
|
||||
deno_unsync::spawn(async move {
|
||||
loop {
|
||||
// TODO(@crowlKats): check for uncaptured_receiver.is_closed instead once tokio is upgraded
|
||||
if !matches!(
|
||||
uncaptured_sender_is_closed_receiver.try_recv(),
|
||||
Err(tokio::sync::oneshot::error::TryRecvError::Empty)
|
||||
) {
|
||||
break;
|
||||
}
|
||||
let Some(error) = uncaptured_receiver.recv().await else {
|
||||
break;
|
||||
};
|
||||
|
||||
// SAFETY: eh, it's safe
|
||||
let isolate: &mut v8::Isolate = unsafe { &mut *isolate_ptr };
|
||||
let scope = &mut v8::HandleScope::with_context(isolate, &context);
|
||||
let error = deno_core::error::to_v8_error(scope, &error);
|
||||
|
||||
let error_event_class = v8::Local::new(scope, error_event_class.clone());
|
||||
let constructor = v8::Local::<v8::Function>::try_from(error_event_class).unwrap();
|
||||
let kind = v8::String::new(scope, "uncapturederror").unwrap();
|
||||
|
||||
let obj = v8::Object::new(scope);
|
||||
let key = v8::String::new(scope, "error").unwrap();
|
||||
obj.set(scope, key.into(), error);
|
||||
|
||||
let event = constructor
|
||||
.new_instance(scope, &[kind.into(), obj.into()])
|
||||
.unwrap();
|
||||
|
||||
let recv = v8::Local::new(scope, task_device.clone());
|
||||
func.open(scope).call(scope, recv, &[event.into()]);
|
||||
}
|
||||
});
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
pub enum CreateDeviceError {
|
||||
#[class(type)]
|
||||
#[error("requiredFeatures must be a subset of the adapter features")]
|
||||
RequiredFeaturesNotASubset,
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
Serde(#[from] serde_json::Error),
|
||||
#[class(type)]
|
||||
#[error(transparent)]
|
||||
Device(#[from] wgpu_core::instance::RequestDeviceError),
|
||||
}
|
||||
|
||||
pub struct GPUSupportedLimits(pub wgpu_types::Limits);
|
||||
|
||||
impl GarbageCollected for GPUSupportedLimits {}
|
||||
|
||||
#[op2]
|
||||
impl GPUSupportedLimits {
|
||||
#[getter]
|
||||
fn maxTextureDimension1D(&self) -> u32 {
|
||||
self.0.max_texture_dimension_1d
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxTextureDimension2D(&self) -> u32 {
|
||||
self.0.max_texture_dimension_2d
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxTextureDimension3D(&self) -> u32 {
|
||||
self.0.max_texture_dimension_3d
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxTextureArrayLayers(&self) -> u32 {
|
||||
self.0.max_texture_array_layers
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxBindGroups(&self) -> u32 {
|
||||
self.0.max_bind_groups
|
||||
}
|
||||
|
||||
// TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers
|
||||
|
||||
#[getter]
|
||||
fn maxBindingsPerBindGroup(&self) -> u32 {
|
||||
self.0.max_bindings_per_bind_group
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxDynamicUniformBuffersPerPipelineLayout(&self) -> u32 {
|
||||
self.0.max_dynamic_uniform_buffers_per_pipeline_layout
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxDynamicStorageBuffersPerPipelineLayout(&self) -> u32 {
|
||||
self.0.max_dynamic_storage_buffers_per_pipeline_layout
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxSampledTexturesPerShaderStage(&self) -> u32 {
|
||||
self.0.max_sampled_textures_per_shader_stage
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxSamplersPerShaderStage(&self) -> u32 {
|
||||
self.0.max_samplers_per_shader_stage
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxStorageBuffersPerShaderStage(&self) -> u32 {
|
||||
self.0.max_storage_buffers_per_shader_stage
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxStorageTexturesPerShaderStage(&self) -> u32 {
|
||||
self.0.max_storage_textures_per_shader_stage
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxUniformBuffersPerShaderStage(&self) -> u32 {
|
||||
self.0.max_uniform_buffers_per_shader_stage
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxUniformBufferBindingSize(&self) -> u32 {
|
||||
self.0.max_uniform_buffer_binding_size
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxStorageBufferBindingSize(&self) -> u32 {
|
||||
self.0.max_storage_buffer_binding_size
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn minUniformBufferOffsetAlignment(&self) -> u32 {
|
||||
self.0.min_uniform_buffer_offset_alignment
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn minStorageBufferOffsetAlignment(&self) -> u32 {
|
||||
self.0.min_storage_buffer_offset_alignment
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxVertexBuffers(&self) -> u32 {
|
||||
self.0.max_vertex_buffers
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[number]
|
||||
fn maxBufferSize(&self) -> u64 {
|
||||
self.0.max_buffer_size
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxVertexAttributes(&self) -> u32 {
|
||||
self.0.max_vertex_attributes
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxVertexBufferArrayStride(&self) -> u32 {
|
||||
self.0.max_vertex_buffer_array_stride
|
||||
}
|
||||
|
||||
// TODO(@crowlKats): support max_inter_stage_shader_variables
|
||||
|
||||
#[getter]
|
||||
fn maxColorAttachments(&self) -> u32 {
|
||||
self.0.max_color_attachments
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxColorAttachmentBytesPerSample(&self) -> u32 {
|
||||
self.0.max_color_attachment_bytes_per_sample
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxComputeWorkgroupStorageSize(&self) -> u32 {
|
||||
self.0.max_compute_workgroup_storage_size
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxComputeInvocationsPerWorkgroup(&self) -> u32 {
|
||||
self.0.max_compute_invocations_per_workgroup
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxComputeWorkgroupSizeX(&self) -> u32 {
|
||||
self.0.max_compute_workgroup_size_x
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxComputeWorkgroupSizeY(&self) -> u32 {
|
||||
self.0.max_compute_workgroup_size_y
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxComputeWorkgroupSizeZ(&self) -> u32 {
|
||||
self.0.max_compute_workgroup_size_z
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn maxComputeWorkgroupsPerDimension(&self) -> u32 {
|
||||
self.0.max_compute_workgroups_per_dimension
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GPUSupportedFeatures(v8::Global<v8::Value>);
|
||||
|
||||
impl GarbageCollected for GPUSupportedFeatures {}
|
||||
|
||||
impl GPUSupportedFeatures {
|
||||
#[allow(clippy::disallowed_types)]
|
||||
pub fn new(scope: &mut v8::HandleScope, features: HashSet<GPUFeatureName>) -> Self {
|
||||
let set = v8::Set::new(scope);
|
||||
|
||||
for feature in features {
|
||||
let key = v8::String::new(scope, feature.as_str()).unwrap();
|
||||
set.add(scope, key.into());
|
||||
}
|
||||
|
||||
Self(v8::Global::new(scope, <v8::Local<v8::Value>>::from(set)))
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
impl GPUSupportedFeatures {
|
||||
#[global]
|
||||
#[symbol("setlike_set")]
|
||||
fn set(&self) -> v8::Global<v8::Value> {
|
||||
self.0.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GPUAdapterInfo {
|
||||
pub info: wgpu_types::AdapterInfo,
|
||||
pub subgroup_min_size: u32,
|
||||
pub subgroup_max_size: u32,
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUAdapterInfo {}
|
||||
|
||||
#[op2]
|
||||
impl GPUAdapterInfo {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn vendor(&self) -> String {
|
||||
self.info.vendor.to_string()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[string]
|
||||
fn architecture(&self) -> &'static str {
|
||||
"" // TODO: wgpu#2170
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[string]
|
||||
fn device(&self) -> String {
|
||||
self.info.device.to_string()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[string]
|
||||
fn description(&self) -> String {
|
||||
self.info.name.clone()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn subgroup_min_size(&self) -> u32 {
|
||||
self.subgroup_min_size
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn subgroup_max_size(&self) -> u32 {
|
||||
self.subgroup_max_size
|
||||
}
|
||||
}
|
116
deno_webgpu/bind_group.rs
Normal file
116
deno_webgpu/bind_group.rs
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8::HandleScope;
|
||||
use deno_core::v8::Local;
|
||||
use deno_core::v8::Value;
|
||||
use deno_core::webidl::ContextFn;
|
||||
use deno_core::webidl::WebIdlConverter;
|
||||
use deno_core::webidl::WebIdlError;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
use crate::buffer::GPUBuffer;
|
||||
use crate::sampler::GPUSampler;
|
||||
use crate::texture::GPUTextureView;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUBindGroup {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::BindGroupId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPUBindGroup {
|
||||
fn drop(&mut self) {
|
||||
self.instance.bind_group_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPUBindGroup {
|
||||
const NAME: &'static str = "GPUBindGroup";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUBindGroup {}
|
||||
|
||||
#[op2]
|
||||
impl GPUBindGroup {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBindGroupDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub layout: Ptr<super::bind_group_layout::GPUBindGroupLayout>,
|
||||
pub entries: Vec<GPUBindGroupEntry>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBindGroupEntry {
|
||||
#[options(enforce_range = true)]
|
||||
pub binding: u32,
|
||||
pub resource: GPUBindingResource,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBufferBinding {
|
||||
pub buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
pub offset: u64,
|
||||
#[options(enforce_range = true)]
|
||||
pub size: Option<u64>,
|
||||
}
|
||||
|
||||
pub(crate) enum GPUBindingResource {
|
||||
Sampler(Ptr<GPUSampler>),
|
||||
TextureView(Ptr<GPUTextureView>),
|
||||
BufferBinding(GPUBufferBinding),
|
||||
}
|
||||
|
||||
impl<'a> WebIdlConverter<'a> for GPUBindingResource {
|
||||
type Options = ();
|
||||
|
||||
fn convert<'b>(
|
||||
scope: &mut HandleScope<'a>,
|
||||
value: Local<'a, Value>,
|
||||
prefix: Cow<'static, str>,
|
||||
context: ContextFn<'b>,
|
||||
options: &Self::Options,
|
||||
) -> Result<Self, WebIdlError> {
|
||||
<Ptr<GPUSampler>>::convert(scope, value, prefix.clone(), context.borrowed(), options)
|
||||
.map(Self::Sampler)
|
||||
.or_else(|_| {
|
||||
<Ptr<GPUTextureView>>::convert(
|
||||
scope,
|
||||
value,
|
||||
prefix.clone(),
|
||||
context.borrowed(),
|
||||
options,
|
||||
)
|
||||
.map(Self::TextureView)
|
||||
})
|
||||
.or_else(|_| {
|
||||
GPUBufferBinding::convert(scope, value, prefix, context, options)
|
||||
.map(Self::BufferBinding)
|
||||
})
|
||||
}
|
||||
}
|
176
deno_webgpu/bind_group_layout.rs
Normal file
176
deno_webgpu/bind_group_layout.rs
Normal file
@ -0,0 +1,176 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
use crate::texture::GPUTextureViewDimension;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUBindGroupLayout {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::BindGroupLayoutId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPUBindGroupLayout {
|
||||
fn drop(&mut self) {
|
||||
self.instance.bind_group_layout_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl deno_core::webidl::WebIdlInterfaceConverter for GPUBindGroupLayout {
|
||||
const NAME: &'static str = "GPUBindGroupLayout";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUBindGroupLayout {}
|
||||
|
||||
#[op2]
|
||||
impl GPUBindGroupLayout {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBindGroupLayoutDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
pub entries: Vec<GPUBindGroupLayoutEntry>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBindGroupLayoutEntry {
|
||||
#[options(enforce_range = true)]
|
||||
pub binding: u32,
|
||||
#[options(enforce_range = true)]
|
||||
pub visibility: u32,
|
||||
pub buffer: Option<GPUBufferBindingLayout>,
|
||||
pub sampler: Option<GPUSamplerBindingLayout>,
|
||||
pub texture: Option<GPUTextureBindingLayout>,
|
||||
pub storage_texture: Option<GPUStorageTextureBindingLayout>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBufferBindingLayout {
|
||||
#[webidl(default = GPUBufferBindingType::Uniform)]
|
||||
pub r#type: GPUBufferBindingType,
|
||||
#[webidl(default = false)]
|
||||
pub has_dynamic_offset: bool,
|
||||
#[webidl(default = 0)]
|
||||
pub min_binding_size: u64,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUBufferBindingType {
|
||||
Uniform,
|
||||
Storage,
|
||||
ReadOnlyStorage,
|
||||
}
|
||||
|
||||
impl From<GPUBufferBindingType> for wgpu_types::BufferBindingType {
|
||||
fn from(value: GPUBufferBindingType) -> Self {
|
||||
match value {
|
||||
GPUBufferBindingType::Uniform => Self::Uniform,
|
||||
GPUBufferBindingType::Storage => Self::Storage { read_only: false },
|
||||
GPUBufferBindingType::ReadOnlyStorage => Self::Storage { read_only: true },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUSamplerBindingLayout {
|
||||
#[webidl(default = GPUSamplerBindingType::Filtering)]
|
||||
pub r#type: GPUSamplerBindingType,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUSamplerBindingType {
|
||||
Filtering,
|
||||
NonFiltering,
|
||||
Comparison,
|
||||
}
|
||||
|
||||
impl From<GPUSamplerBindingType> for wgpu_types::SamplerBindingType {
|
||||
fn from(value: GPUSamplerBindingType) -> Self {
|
||||
match value {
|
||||
GPUSamplerBindingType::Filtering => Self::Filtering,
|
||||
GPUSamplerBindingType::NonFiltering => Self::NonFiltering,
|
||||
GPUSamplerBindingType::Comparison => Self::Comparison,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUTextureBindingLayout {
|
||||
#[webidl(default = GPUTextureSampleType::Float)]
|
||||
pub sample_type: GPUTextureSampleType,
|
||||
#[webidl(default = GPUTextureViewDimension::D2)]
|
||||
pub view_dimension: GPUTextureViewDimension,
|
||||
#[webidl(default = false)]
|
||||
pub multisampled: bool,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUTextureSampleType {
|
||||
Float,
|
||||
UnfilterableFloat,
|
||||
Depth,
|
||||
Sint,
|
||||
Uint,
|
||||
}
|
||||
|
||||
impl From<GPUTextureSampleType> for wgpu_types::TextureSampleType {
|
||||
fn from(value: GPUTextureSampleType) -> Self {
|
||||
match value {
|
||||
GPUTextureSampleType::Float => Self::Float { filterable: true },
|
||||
GPUTextureSampleType::UnfilterableFloat => Self::Float { filterable: false },
|
||||
GPUTextureSampleType::Depth => Self::Depth,
|
||||
GPUTextureSampleType::Sint => Self::Sint,
|
||||
GPUTextureSampleType::Uint => Self::Uint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUStorageTextureBindingLayout {
|
||||
#[webidl(default = GPUStorageTextureAccess::WriteOnly)]
|
||||
pub access: GPUStorageTextureAccess,
|
||||
pub format: super::texture::GPUTextureFormat,
|
||||
#[webidl(default = GPUTextureViewDimension::D2)]
|
||||
pub view_dimension: GPUTextureViewDimension,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUStorageTextureAccess {
|
||||
WriteOnly,
|
||||
ReadOnly,
|
||||
ReadWrite,
|
||||
}
|
||||
|
||||
impl From<GPUStorageTextureAccess> for wgpu_types::StorageTextureAccess {
|
||||
fn from(value: GPUStorageTextureAccess) -> Self {
|
||||
match value {
|
||||
GPUStorageTextureAccess::WriteOnly => Self::WriteOnly,
|
||||
GPUStorageTextureAccess::ReadOnly => Self::ReadOnly,
|
||||
GPUStorageTextureAccess::ReadWrite => Self::ReadWrite,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,312 +0,0 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub(crate) struct WebGpuBindGroupLayout(
|
||||
pub(crate) crate::Instance,
|
||||
pub(crate) wgpu_core::id::BindGroupLayoutId,
|
||||
);
|
||||
impl Resource for WebGpuBindGroupLayout {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBindGroupLayout".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.bind_group_layout_drop(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuBindGroup(
|
||||
pub(crate) crate::Instance,
|
||||
pub(crate) wgpu_core::id::BindGroupId,
|
||||
);
|
||||
impl Resource for WebGpuBindGroup {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBindGroup".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.bind_group_drop(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuBufferBindingLayout {
|
||||
r#type: GpuBufferBindingType,
|
||||
has_dynamic_offset: bool,
|
||||
min_binding_size: u64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
enum GpuBufferBindingType {
|
||||
Uniform,
|
||||
Storage,
|
||||
ReadOnlyStorage,
|
||||
}
|
||||
|
||||
impl From<GpuBufferBindingType> for wgpu_types::BufferBindingType {
|
||||
fn from(binding_type: GpuBufferBindingType) -> Self {
|
||||
match binding_type {
|
||||
GpuBufferBindingType::Uniform => wgpu_types::BufferBindingType::Uniform,
|
||||
GpuBufferBindingType::Storage => {
|
||||
wgpu_types::BufferBindingType::Storage { read_only: false }
|
||||
}
|
||||
GpuBufferBindingType::ReadOnlyStorage => {
|
||||
wgpu_types::BufferBindingType::Storage { read_only: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuSamplerBindingLayout {
|
||||
r#type: wgpu_types::SamplerBindingType,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuTextureBindingLayout {
|
||||
sample_type: GpuTextureSampleType,
|
||||
view_dimension: wgpu_types::TextureViewDimension,
|
||||
multisampled: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
enum GpuTextureSampleType {
|
||||
Float,
|
||||
UnfilterableFloat,
|
||||
Depth,
|
||||
Sint,
|
||||
Uint,
|
||||
}
|
||||
|
||||
impl From<GpuTextureSampleType> for wgpu_types::TextureSampleType {
|
||||
fn from(sample_type: GpuTextureSampleType) -> Self {
|
||||
match sample_type {
|
||||
GpuTextureSampleType::Float => {
|
||||
wgpu_types::TextureSampleType::Float { filterable: true }
|
||||
}
|
||||
GpuTextureSampleType::UnfilterableFloat => {
|
||||
wgpu_types::TextureSampleType::Float { filterable: false }
|
||||
}
|
||||
GpuTextureSampleType::Depth => wgpu_types::TextureSampleType::Depth,
|
||||
GpuTextureSampleType::Sint => wgpu_types::TextureSampleType::Sint,
|
||||
GpuTextureSampleType::Uint => wgpu_types::TextureSampleType::Uint,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuStorageTextureBindingLayout {
|
||||
access: wgpu_types::StorageTextureAccess,
|
||||
format: wgpu_types::TextureFormat,
|
||||
view_dimension: wgpu_types::TextureViewDimension,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuBindGroupLayoutEntry {
|
||||
binding: u32,
|
||||
visibility: u32,
|
||||
#[serde(flatten)]
|
||||
binding_type: GpuBindingType,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
enum GpuBindingType {
|
||||
Buffer(GpuBufferBindingLayout),
|
||||
Sampler(GpuSamplerBindingLayout),
|
||||
Texture(GpuTextureBindingLayout),
|
||||
StorageTexture(GpuStorageTextureBindingLayout),
|
||||
}
|
||||
|
||||
impl From<GpuBindingType> for wgpu_types::BindingType {
|
||||
fn from(binding_type: GpuBindingType) -> wgpu_types::BindingType {
|
||||
match binding_type {
|
||||
GpuBindingType::Buffer(buffer) => wgpu_types::BindingType::Buffer {
|
||||
ty: buffer.r#type.into(),
|
||||
has_dynamic_offset: buffer.has_dynamic_offset,
|
||||
min_binding_size: std::num::NonZeroU64::new(buffer.min_binding_size),
|
||||
},
|
||||
GpuBindingType::Sampler(sampler) => wgpu_types::BindingType::Sampler(sampler.r#type),
|
||||
GpuBindingType::Texture(texture) => wgpu_types::BindingType::Texture {
|
||||
sample_type: texture.sample_type.into(),
|
||||
view_dimension: texture.view_dimension,
|
||||
multisampled: texture.multisampled,
|
||||
},
|
||||
GpuBindingType::StorageTexture(storage_texture) => {
|
||||
wgpu_types::BindingType::StorageTexture {
|
||||
access: storage_texture.access,
|
||||
format: storage_texture.format,
|
||||
view_dimension: storage_texture.view_dimension,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_bind_group_layout(
|
||||
state: &mut OpState,
|
||||
#[smi] device_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[serde] entries: Vec<GpuBindGroupLayoutEntry>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let entries = entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
wgpu_types::BindGroupLayoutEntry {
|
||||
binding: entry.binding,
|
||||
visibility: wgpu_types::ShaderStages::from_bits(entry.visibility).unwrap(),
|
||||
ty: entry.binding_type.into(),
|
||||
count: None, // native-only
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
|
||||
label: Some(label),
|
||||
entries: Cow::from(entries),
|
||||
};
|
||||
|
||||
gfx_put!(instance.device_create_bind_group_layout(
|
||||
device,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, WebGpuBindGroupLayout)
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_pipeline_layout(
|
||||
state: &mut OpState,
|
||||
#[smi] device_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[serde] bind_group_layouts: Vec<u32>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let bind_group_layouts = bind_group_layouts
|
||||
.into_iter()
|
||||
.map(|rid| {
|
||||
let bind_group_layout = state.resource_table.get::<WebGpuBindGroupLayout>(rid)?;
|
||||
Ok(bind_group_layout.1)
|
||||
})
|
||||
.collect::<Result<Vec<_>, AnyError>>()?;
|
||||
|
||||
let descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
|
||||
label: Some(label),
|
||||
bind_group_layouts: Cow::from(bind_group_layouts),
|
||||
push_constant_ranges: Default::default(),
|
||||
};
|
||||
|
||||
gfx_put!(instance.device_create_pipeline_layout(
|
||||
device,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, super::pipeline::WebGpuPipelineLayout)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuBindGroupEntry {
|
||||
binding: u32,
|
||||
kind: String,
|
||||
resource: ResourceId,
|
||||
offset: Option<u64>,
|
||||
size: Option<u64>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_bind_group(
|
||||
state: &mut OpState,
|
||||
#[smi] device_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[smi] layout: ResourceId,
|
||||
#[serde] entries: Vec<GpuBindGroupEntry>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let entries = entries
|
||||
.into_iter()
|
||||
.map(|entry| {
|
||||
Ok(wgpu_core::binding_model::BindGroupEntry {
|
||||
binding: entry.binding,
|
||||
resource: match entry.kind.as_str() {
|
||||
"GPUSampler" => {
|
||||
let sampler_resource = state
|
||||
.resource_table
|
||||
.get::<super::sampler::WebGpuSampler>(entry.resource)?;
|
||||
wgpu_core::binding_model::BindingResource::Sampler(sampler_resource.1)
|
||||
}
|
||||
"GPUTextureView" => {
|
||||
let texture_view_resource =
|
||||
state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTextureView>(entry.resource)?;
|
||||
wgpu_core::binding_model::BindingResource::TextureView(
|
||||
texture_view_resource.1,
|
||||
)
|
||||
}
|
||||
"GPUBufferBinding" => {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(entry.resource)?;
|
||||
wgpu_core::binding_model::BindingResource::Buffer(
|
||||
wgpu_core::binding_model::BufferBinding {
|
||||
buffer: buffer_resource.1,
|
||||
offset: entry.offset.unwrap_or(0),
|
||||
size: std::num::NonZeroU64::new(entry.size.unwrap_or(0)),
|
||||
},
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
},
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, AnyError>>()?;
|
||||
|
||||
let bind_group_layout = state.resource_table.get::<WebGpuBindGroupLayout>(layout)?;
|
||||
|
||||
let descriptor = wgpu_core::binding_model::BindGroupDescriptor {
|
||||
label: Some(label),
|
||||
layout: bind_group_layout.1,
|
||||
entries: Cow::from(entries),
|
||||
};
|
||||
|
||||
gfx_put!(instance.device_create_bind_group(
|
||||
device,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, WebGpuBindGroup)
|
||||
}
|
@ -1,221 +1,259 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use deno_core::futures::channel::oneshot;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::ptr::NonNull;
|
||||
use std::rc::Rc;
|
||||
use std::time::Duration;
|
||||
use wgpu_core::resource::BufferAccessResult;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
use deno_core::futures::channel::oneshot;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use deno_error::JsErrorBox;
|
||||
use wgpu_core::device::HostMap as MapMode;
|
||||
|
||||
use crate::Instance;
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBufferDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub size: u64,
|
||||
#[options(enforce_range = true)]
|
||||
pub usage: u32,
|
||||
#[webidl(default = false)]
|
||||
pub mapped_at_creation: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
pub enum BufferError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error("usage is not valid")]
|
||||
InvalidUsage,
|
||||
#[error(transparent)]
|
||||
Access(#[from] wgpu_core::resource::BufferAccessError),
|
||||
#[class(generic)]
|
||||
#[error(transparent)]
|
||||
Canceled(#[from] oneshot::Canceled),
|
||||
#[class("DOMExceptionOperationError")]
|
||||
#[error(transparent)]
|
||||
Access(#[from] wgpu_core::resource::BufferAccessError),
|
||||
#[class("DOMExceptionOperationError")]
|
||||
#[error("{0}")]
|
||||
Operation(&'static str),
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
Other(#[from] JsErrorBox),
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuBuffer(
|
||||
pub(crate) super::Instance,
|
||||
pub(crate) wgpu_core::id::BufferId,
|
||||
);
|
||||
impl Resource for WebGpuBuffer {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBuffer".into()
|
||||
}
|
||||
pub struct GPUBuffer {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.buffer_drop(self.1);
|
||||
pub id: wgpu_core::id::BufferId,
|
||||
pub device: wgpu_core::id::DeviceId,
|
||||
|
||||
pub label: String,
|
||||
|
||||
pub size: u64,
|
||||
pub usage: u32,
|
||||
|
||||
pub map_state: RefCell<&'static str>,
|
||||
pub map_mode: RefCell<Option<MapMode>>,
|
||||
|
||||
pub mapped_js_buffers: RefCell<Vec<v8::Global<v8::ArrayBuffer>>>,
|
||||
}
|
||||
|
||||
impl Drop for GPUBuffer {
|
||||
fn drop(&mut self) {
|
||||
self.instance.buffer_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
struct WebGpuBufferMapped(NonNull<u8>, usize);
|
||||
impl Resource for WebGpuBufferMapped {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUBufferMapped".into()
|
||||
}
|
||||
impl WebIdlInterfaceConverter for GPUBuffer {
|
||||
const NAME: &'static str = "GPUBuffer";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUBuffer {}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] device_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[number] size: u64,
|
||||
usage: u32,
|
||||
mapped_at_creation: bool,
|
||||
) -> Result<WebGpuResult, BufferError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)
|
||||
.map_err(BufferError::Resource)?;
|
||||
let device = device_resource.1;
|
||||
impl GPUBuffer {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
let descriptor = wgpu_core::resource::BufferDescriptor {
|
||||
label: Some(label),
|
||||
size,
|
||||
usage: wgpu_types::BufferUsages::from_bits(usage).ok_or(BufferError::InvalidUsage)?,
|
||||
mapped_at_creation,
|
||||
};
|
||||
#[getter]
|
||||
#[number]
|
||||
fn size(&self) -> u64 {
|
||||
self.size
|
||||
}
|
||||
#[getter]
|
||||
fn usage(&self) -> u32 {
|
||||
self.usage
|
||||
}
|
||||
|
||||
gfx_put!(instance.device_create_buffer(
|
||||
device,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, WebGpuBuffer)
|
||||
}
|
||||
#[getter]
|
||||
#[string]
|
||||
fn map_state(&self) -> &'static str {
|
||||
*self.map_state.borrow()
|
||||
}
|
||||
|
||||
#[op2(async)]
|
||||
#[serde]
|
||||
pub async fn op_webgpu_buffer_get_map_async(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] buffer_rid: ResourceId,
|
||||
#[smi] device_rid: ResourceId,
|
||||
mode: u32,
|
||||
#[number] offset: u64,
|
||||
#[number] size: u64,
|
||||
) -> Result<WebGpuResult, BufferError> {
|
||||
let (sender, receiver) = oneshot::channel::<BufferAccessResult>();
|
||||
|
||||
let device;
|
||||
{
|
||||
let state_ = state.borrow();
|
||||
let instance = state_.borrow::<super::Instance>();
|
||||
let buffer_resource = state_
|
||||
.resource_table
|
||||
.get::<WebGpuBuffer>(buffer_rid)
|
||||
.map_err(BufferError::Resource)?;
|
||||
let buffer = buffer_resource.1;
|
||||
let device_resource = state_
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)
|
||||
.map_err(BufferError::Resource)?;
|
||||
device = device_resource.1;
|
||||
|
||||
let callback = Box::new(move |status| {
|
||||
sender.send(status).unwrap();
|
||||
});
|
||||
|
||||
// TODO(lucacasonato): error handling
|
||||
let maybe_err = instance
|
||||
.buffer_map_async(
|
||||
buffer,
|
||||
offset,
|
||||
Some(size),
|
||||
wgpu_core::resource::BufferMapOperation {
|
||||
host: match mode {
|
||||
1 => wgpu_core::device::HostMap::Read,
|
||||
2 => wgpu_core::device::HostMap::Write,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
callback: Some(callback),
|
||||
},
|
||||
)
|
||||
.err();
|
||||
|
||||
if maybe_err.is_some() {
|
||||
return Ok(WebGpuResult::maybe_err(maybe_err));
|
||||
#[async_method]
|
||||
async fn map_async(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] mode: u32,
|
||||
#[webidl(default = 0)] offset: u64,
|
||||
#[webidl] size: Option<u64>,
|
||||
) -> Result<(), BufferError> {
|
||||
let read_mode = (mode & 0x0001) == 0x0001;
|
||||
let write_mode = (mode & 0x0002) == 0x0002;
|
||||
if (read_mode && write_mode) || (!read_mode && !write_mode) {
|
||||
return Err(BufferError::Operation(
|
||||
"exactly one of READ or WRITE map mode must be set",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let 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>();
|
||||
instance
|
||||
.device_poll(device, wgpu_types::Maintain::wait())
|
||||
.unwrap();
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
Ok::<(), BufferError>(())
|
||||
};
|
||||
|
||||
let receiver_fut = async move {
|
||||
receiver.await??;
|
||||
let mut done = done_.borrow_mut();
|
||||
*done = true;
|
||||
Ok::<(), BufferError>(())
|
||||
};
|
||||
|
||||
tokio::try_join!(device_poll_fut, receiver_fut)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_buffer_get_mapped_range(
|
||||
state: &mut OpState,
|
||||
#[smi] buffer_rid: ResourceId,
|
||||
#[number] offset: u64,
|
||||
#[number] size: Option<u64>,
|
||||
#[buffer] buf: &mut [u8],
|
||||
) -> Result<WebGpuResult, BufferError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuBuffer>(buffer_rid)
|
||||
.map_err(BufferError::Resource)?;
|
||||
let buffer = buffer_resource.1;
|
||||
|
||||
let (slice_pointer, range_size) = instance
|
||||
.buffer_get_mapped_range(buffer, offset, size)
|
||||
.map_err(BufferError::Access)?;
|
||||
|
||||
// SAFETY: guarantee to be safe from wgpu
|
||||
let slice =
|
||||
unsafe { std::slice::from_raw_parts_mut(slice_pointer.as_ptr(), range_size as usize) };
|
||||
buf.copy_from_slice(slice);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGpuBufferMapped(slice_pointer, range_size as usize));
|
||||
|
||||
Ok(WebGpuResult::rid(rid))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_buffer_unmap(
|
||||
state: &mut OpState,
|
||||
#[smi] buffer_rid: ResourceId,
|
||||
#[smi] mapped_rid: ResourceId,
|
||||
#[buffer] buf: Option<&[u8]>,
|
||||
) -> Result<WebGpuResult, BufferError> {
|
||||
let mapped_resource = state
|
||||
.resource_table
|
||||
.take::<WebGpuBufferMapped>(mapped_rid)
|
||||
.map_err(BufferError::Resource)?;
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuBuffer>(buffer_rid)
|
||||
.map_err(BufferError::Resource)?;
|
||||
let buffer = buffer_resource.1;
|
||||
|
||||
if let Some(buf) = buf {
|
||||
// SAFETY: guarantee to be safe from wgpu
|
||||
let slice = unsafe {
|
||||
std::slice::from_raw_parts_mut(mapped_resource.0.as_ptr(), mapped_resource.1)
|
||||
let mode = if read_mode {
|
||||
MapMode::Read
|
||||
} else {
|
||||
assert!(write_mode);
|
||||
MapMode::Write
|
||||
};
|
||||
slice.copy_from_slice(buf);
|
||||
|
||||
{
|
||||
*self.map_state.borrow_mut() = "pending";
|
||||
}
|
||||
|
||||
let (sender, receiver) = oneshot::channel::<wgpu_core::resource::BufferAccessResult>();
|
||||
|
||||
{
|
||||
let callback = Box::new(move |status| {
|
||||
sender.send(status).unwrap();
|
||||
});
|
||||
|
||||
let err = self
|
||||
.instance
|
||||
.buffer_map_async(
|
||||
self.id,
|
||||
offset,
|
||||
size,
|
||||
wgpu_core::resource::BufferMapOperation {
|
||||
host: mode,
|
||||
callback: Some(callback),
|
||||
},
|
||||
)
|
||||
.err();
|
||||
|
||||
if err.is_some() {
|
||||
self.error_handler.push_error(err);
|
||||
return Err(BufferError::Operation("validation error occurred"));
|
||||
}
|
||||
}
|
||||
|
||||
let done = Rc::new(RefCell::new(false));
|
||||
let done_ = done.clone();
|
||||
let device_poll_fut = async move {
|
||||
while !*done.borrow() {
|
||||
{
|
||||
self.instance
|
||||
.device_poll(self.device, wgpu_types::Maintain::wait())
|
||||
.unwrap();
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
}
|
||||
Ok::<(), BufferError>(())
|
||||
};
|
||||
|
||||
let receiver_fut = async move {
|
||||
receiver.await??;
|
||||
let mut done = done_.borrow_mut();
|
||||
*done = true;
|
||||
Ok::<(), BufferError>(())
|
||||
};
|
||||
|
||||
tokio::try_join!(device_poll_fut, receiver_fut)?;
|
||||
|
||||
*self.map_state.borrow_mut() = "mapped";
|
||||
*self.map_mode.borrow_mut() = Some(mode);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
gfx_ok!(instance.buffer_unmap(buffer))
|
||||
fn get_mapped_range<'s>(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
#[webidl(default = 0)] offset: u64,
|
||||
#[webidl] size: Option<u64>,
|
||||
) -> Result<v8::Local<'s, v8::ArrayBuffer>, BufferError> {
|
||||
let (slice_pointer, range_size) = self
|
||||
.instance
|
||||
.buffer_get_mapped_range(self.id, offset, size)
|
||||
.map_err(BufferError::Access)?;
|
||||
|
||||
let mode = self.map_mode.borrow();
|
||||
let mode = mode.as_ref().unwrap();
|
||||
|
||||
let bs = if mode == &MapMode::Write {
|
||||
unsafe extern "C" fn noop_deleter_callback(
|
||||
_data: *mut std::ffi::c_void,
|
||||
_byte_length: usize,
|
||||
_deleter_data: *mut std::ffi::c_void,
|
||||
) {
|
||||
}
|
||||
|
||||
// SAFETY: creating a backing store from the pointer and length provided by wgpu
|
||||
unsafe {
|
||||
v8::ArrayBuffer::new_backing_store_from_ptr(
|
||||
slice_pointer.as_ptr() as _,
|
||||
range_size as usize,
|
||||
noop_deleter_callback,
|
||||
std::ptr::null_mut(),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// SAFETY: creating a vector from the pointer and length provided by wgpu
|
||||
let slice =
|
||||
unsafe { std::slice::from_raw_parts(slice_pointer.as_ptr(), range_size as usize) };
|
||||
v8::ArrayBuffer::new_backing_store_from_vec(slice.to_vec())
|
||||
};
|
||||
|
||||
let shared_bs = bs.make_shared();
|
||||
let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs);
|
||||
|
||||
if mode == &MapMode::Write {
|
||||
self.mapped_js_buffers
|
||||
.borrow_mut()
|
||||
.push(v8::Global::new(scope, ab));
|
||||
}
|
||||
|
||||
Ok(ab)
|
||||
}
|
||||
|
||||
#[nofast]
|
||||
fn unmap(&self, scope: &mut v8::HandleScope) -> Result<(), BufferError> {
|
||||
for ab in self.mapped_js_buffers.replace(vec![]) {
|
||||
let ab = ab.open(scope);
|
||||
ab.detach(None);
|
||||
}
|
||||
|
||||
self.instance
|
||||
.buffer_unmap(self.id)
|
||||
.map_err(BufferError::Access)?;
|
||||
|
||||
*self.map_state.borrow_mut() = "unmapped";
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn destroy(&self) -> Result<(), JsErrorBox> {
|
||||
self.instance
|
||||
.buffer_destroy(self.id)
|
||||
.map_err(|e| JsErrorBox::generic(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
@ -1,392 +0,0 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum BundleError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error("size must be larger than 0")]
|
||||
InvalidSize,
|
||||
}
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
|
||||
struct WebGpuRenderBundleEncoder(RefCell<wgpu_core::command::RenderBundleEncoder>);
|
||||
impl Resource for WebGpuRenderBundleEncoder {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderBundleEncoder".into()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuRenderBundle(
|
||||
pub(crate) super::Instance,
|
||||
pub(crate) wgpu_core::id::RenderBundleId,
|
||||
);
|
||||
impl Resource for WebGpuRenderBundle {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderBundle".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.render_bundle_drop(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateRenderBundleEncoderArgs {
|
||||
device_rid: ResourceId,
|
||||
label: String,
|
||||
color_formats: Vec<Option<wgpu_types::TextureFormat>>,
|
||||
depth_stencil_format: Option<wgpu_types::TextureFormat>,
|
||||
sample_count: u32,
|
||||
depth_read_only: bool,
|
||||
stencil_read_only: bool,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_render_bundle_encoder(
|
||||
state: &mut OpState,
|
||||
#[serde] args: CreateRenderBundleEncoderArgs,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(args.device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let depth_stencil =
|
||||
args.depth_stencil_format
|
||||
.map(|format| wgpu_types::RenderBundleDepthStencil {
|
||||
format,
|
||||
depth_read_only: args.depth_read_only,
|
||||
stencil_read_only: args.stencil_read_only,
|
||||
});
|
||||
|
||||
let descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
|
||||
label: Some(Cow::Owned(args.label)),
|
||||
color_formats: Cow::from(args.color_formats),
|
||||
sample_count: args.sample_count,
|
||||
depth_stencil,
|
||||
multiview: None,
|
||||
};
|
||||
|
||||
let res = wgpu_core::command::RenderBundleEncoder::new(&descriptor, device, None);
|
||||
let (render_bundle_encoder, maybe_err) = match res {
|
||||
Ok(encoder) => (encoder, None),
|
||||
Err(e) => (
|
||||
wgpu_core::command::RenderBundleEncoder::dummy(device),
|
||||
Some(e),
|
||||
),
|
||||
};
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGpuRenderBundleEncoder(RefCell::new(
|
||||
render_bundle_encoder,
|
||||
)));
|
||||
|
||||
Ok(WebGpuResult::rid_err(rid, maybe_err))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_finish(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.take::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
let render_bundle_encoder = Rc::try_unwrap(render_bundle_encoder_resource)
|
||||
.ok()
|
||||
.expect("unwrapping render_bundle_encoder_resource should succeed")
|
||||
.0
|
||||
.into_inner();
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
|
||||
gfx_put!(instance.render_bundle_encoder_finish(
|
||||
render_bundle_encoder,
|
||||
&wgpu_core::command::RenderBundleDescriptor {
|
||||
label: Some(label),
|
||||
},
|
||||
None
|
||||
) => state, WebGpuRenderBundle)
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_set_bind_group(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
index: u32,
|
||||
#[smi] bind_group: ResourceId,
|
||||
#[buffer] dynamic_offsets_data: &[u32],
|
||||
#[number] dynamic_offsets_data_start: usize,
|
||||
#[number] dynamic_offsets_data_length: usize,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let bind_group_resource = state
|
||||
.resource_table
|
||||
.get::<super::binding::WebGpuBindGroup>(bind_group)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
let start = dynamic_offsets_data_start;
|
||||
let len = dynamic_offsets_data_length;
|
||||
|
||||
// Assert that length and start are both in bounds
|
||||
assert!(start <= dynamic_offsets_data.len());
|
||||
assert!(len <= dynamic_offsets_data.len() - start);
|
||||
|
||||
let dynamic_offsets_data = &dynamic_offsets_data[start..start + len];
|
||||
|
||||
// SAFETY: the raw pointer and length are of the same slice, and that slice
|
||||
// lives longer than the below function invocation.
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
index,
|
||||
Some(bind_group_resource.1),
|
||||
dynamic_offsets_data.as_ptr(),
|
||||
dynamic_offsets_data.len(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_push_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
#[string] group_label: &str,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
let label = std::ffi::CString::new(group_label).unwrap();
|
||||
// SAFETY: the string the raw pointer points to lives longer than the below
|
||||
// function invocation.
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
);
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
#[string] marker_label: &str,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
let label = std::ffi::CString::new(marker_label).unwrap();
|
||||
// SAFETY: the string the raw pointer points to lives longer than the below
|
||||
// function invocation.
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
label.as_ptr(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_set_pipeline(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
#[smi] pipeline: ResourceId,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<super::pipeline::WebGpuRenderPipeline>(pipeline)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
render_pipeline_resource.1,
|
||||
);
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_set_index_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
#[smi] buffer: ResourceId,
|
||||
#[serde] index_format: wgpu_types::IndexFormat,
|
||||
#[number] offset: u64,
|
||||
#[number] size: u64,
|
||||
) -> Result<WebGpuResult, BundleError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(buffer)
|
||||
.map_err(BundleError::Resource)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)
|
||||
.map_err(BundleError::Resource)?;
|
||||
let size = Some(std::num::NonZeroU64::new(size).ok_or(BundleError::InvalidSize)?);
|
||||
|
||||
render_bundle_encoder_resource
|
||||
.0
|
||||
.borrow_mut()
|
||||
.set_index_buffer(buffer_resource.1, index_format, offset, size);
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_set_vertex_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
slot: u32,
|
||||
#[smi] buffer: ResourceId,
|
||||
#[number] offset: u64,
|
||||
#[number] size: Option<u64>,
|
||||
) -> Result<WebGpuResult, BundleError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(buffer)
|
||||
.map_err(BundleError::Resource)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)
|
||||
.map_err(BundleError::Resource)?;
|
||||
let size = if let Some(size) = size {
|
||||
Some(std::num::NonZeroU64::new(size).ok_or(BundleError::InvalidSize)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
slot,
|
||||
buffer_resource.1,
|
||||
offset,
|
||||
size,
|
||||
);
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_draw(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
vertex_count: u32,
|
||||
instance_count: u32,
|
||||
first_vertex: u32,
|
||||
first_instance: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
vertex_count,
|
||||
instance_count,
|
||||
first_vertex,
|
||||
first_instance,
|
||||
);
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_draw_indexed(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
first_index: u32,
|
||||
base_vertex: i32,
|
||||
first_instance: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
index_count,
|
||||
instance_count,
|
||||
first_index,
|
||||
base_vertex,
|
||||
first_instance,
|
||||
);
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_bundle_encoder_draw_indirect(
|
||||
state: &mut OpState,
|
||||
#[smi] render_bundle_encoder_rid: ResourceId,
|
||||
#[smi] indirect_buffer: ResourceId,
|
||||
#[number] indirect_offset: u64,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
|
||||
let render_bundle_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderBundleEncoder>(render_bundle_encoder_rid)?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(
|
||||
&mut render_bundle_encoder_resource.0.borrow_mut(),
|
||||
buffer_resource.1,
|
||||
indirect_offset,
|
||||
);
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
@ -1,8 +1,6 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::ResourceId;
|
||||
use std::cell::RefCell;
|
||||
use std::ffi::c_void;
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
@ -12,23 +10,39 @@ use std::ffi::c_void;
|
||||
))]
|
||||
use std::ptr::NonNull;
|
||||
|
||||
use crate::surface::WebGpuSurface;
|
||||
use deno_core::cppgc::SameObject;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::v8::Local;
|
||||
use deno_core::v8::Value;
|
||||
use deno_core::FromV8;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::OpState;
|
||||
use deno_error::JsErrorBox;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
use crate::surface::GPUCanvasContext;
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
pub enum ByowError {
|
||||
#[class(type)]
|
||||
#[error("Cannot create surface outside of WebGPU context. Did you forget to call `navigator.gpu.requestAdapter()`?")]
|
||||
WebGPUNotInitiated,
|
||||
#[class(type)]
|
||||
#[error("Invalid parameters")]
|
||||
InvalidParameters,
|
||||
#[class(generic)]
|
||||
#[error(transparent)]
|
||||
CreateSurface(wgpu_core::instance::CreateSurfaceError),
|
||||
#[cfg(target_os = "windows")]
|
||||
#[class(type)]
|
||||
#[error("Invalid system on Windows")]
|
||||
InvalidSystem,
|
||||
#[cfg(target_os = "macos")]
|
||||
#[class(type)]
|
||||
#[error("Invalid system on macOS")]
|
||||
InvalidSystem,
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[class(type)]
|
||||
#[error("Invalid system on Linux/BSD")]
|
||||
InvalidSystem,
|
||||
#[cfg(any(
|
||||
@ -37,56 +51,182 @@ pub enum ByowError {
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
#[class(type)]
|
||||
#[error("window is null")]
|
||||
NullWindow,
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
|
||||
#[class(type)]
|
||||
#[error("display is null")]
|
||||
NullDisplay,
|
||||
#[cfg(target_os = "macos")]
|
||||
#[class(type)]
|
||||
#[error("ns_view is null")]
|
||||
NSViewDisplay,
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
#[smi]
|
||||
pub fn op_webgpu_surface_create(
|
||||
state: &mut OpState,
|
||||
#[string] system: &str,
|
||||
p1: *const c_void,
|
||||
p2: *const c_void,
|
||||
) -> Result<ResourceId, ByowError> {
|
||||
let instance = state
|
||||
.try_borrow::<super::Instance>()
|
||||
.ok_or(ByowError::WebGPUNotInitiated)?;
|
||||
// Security note:
|
||||
//
|
||||
// The `p1` and `p2` parameters are pointers to platform-specific window
|
||||
// handles.
|
||||
//
|
||||
// The code below works under the assumption that:
|
||||
//
|
||||
// - handles can only be created by the FFI interface which
|
||||
// enforces --allow-ffi.
|
||||
//
|
||||
// - `*const c_void` deserizalizes null and v8::External.
|
||||
//
|
||||
// - Only FFI can export v8::External to user code.
|
||||
if p1.is_null() {
|
||||
return Err(ByowError::InvalidParameters);
|
||||
// TODO(@littledivy): This will extend `OffscreenCanvas` when we add it.
|
||||
pub struct UnsafeWindowSurface {
|
||||
pub id: wgpu_core::id::SurfaceId,
|
||||
pub width: RefCell<u32>,
|
||||
pub height: RefCell<u32>,
|
||||
|
||||
pub context: SameObject<GPUCanvasContext>,
|
||||
}
|
||||
|
||||
impl GarbageCollected for UnsafeWindowSurface {}
|
||||
|
||||
#[op2]
|
||||
impl UnsafeWindowSurface {
|
||||
#[constructor]
|
||||
#[cppgc]
|
||||
fn new(
|
||||
state: &mut OpState,
|
||||
#[from_v8] options: UnsafeWindowSurfaceOptions,
|
||||
) -> Result<UnsafeWindowSurface, ByowError> {
|
||||
let instance = state
|
||||
.try_borrow::<super::Instance>()
|
||||
.ok_or(ByowError::WebGPUNotInitiated)?;
|
||||
|
||||
// Security note:
|
||||
//
|
||||
// The `window_handle` and `display_handle` options are pointers to
|
||||
// platform-specific window handles.
|
||||
//
|
||||
// The code below works under the assumption that:
|
||||
//
|
||||
// - handles can only be created by the FFI interface which
|
||||
// enforces --allow-ffi.
|
||||
//
|
||||
// - `*const c_void` deserizalizes null and v8::External.
|
||||
//
|
||||
// - Only FFI can export v8::External to user code.
|
||||
if options.window_handle.is_null() {
|
||||
return Err(ByowError::InvalidParameters);
|
||||
}
|
||||
|
||||
let (win_handle, display_handle) = raw_window(
|
||||
options.system,
|
||||
options.window_handle,
|
||||
options.display_handle,
|
||||
)?;
|
||||
|
||||
// SAFETY: see above comment
|
||||
let id = unsafe {
|
||||
instance
|
||||
.instance_create_surface(display_handle, win_handle, None)
|
||||
.map_err(ByowError::CreateSurface)?
|
||||
};
|
||||
|
||||
Ok(UnsafeWindowSurface {
|
||||
id,
|
||||
width: RefCell::new(options.width),
|
||||
height: RefCell::new(options.height),
|
||||
context: SameObject::new(),
|
||||
})
|
||||
}
|
||||
|
||||
let (win_handle, display_handle) = raw_window(system, p1, p2)?;
|
||||
// SAFETY: see above comment
|
||||
let surface = unsafe {
|
||||
instance
|
||||
.instance_create_surface(display_handle, win_handle, None)
|
||||
.map_err(ByowError::CreateSurface)?
|
||||
};
|
||||
#[global]
|
||||
fn get_context(
|
||||
&self,
|
||||
#[this] this: v8::Global<v8::Object>,
|
||||
scope: &mut v8::HandleScope,
|
||||
) -> v8::Global<v8::Object> {
|
||||
self.context.get(scope, |_| GPUCanvasContext {
|
||||
surface_id: self.id,
|
||||
width: self.width.clone(),
|
||||
height: self.height.clone(),
|
||||
config: RefCell::new(None),
|
||||
texture: RefCell::new(None),
|
||||
canvas: this,
|
||||
})
|
||||
}
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGpuSurface(instance.clone(), surface));
|
||||
Ok(rid)
|
||||
#[nofast]
|
||||
fn present(&self, scope: &mut v8::HandleScope) -> Result<(), JsErrorBox> {
|
||||
let Some(context) = self.context.try_unwrap(scope) else {
|
||||
return Err(JsErrorBox::type_error("getContext was never called"));
|
||||
};
|
||||
|
||||
context.present().map_err(JsErrorBox::from_err)
|
||||
}
|
||||
}
|
||||
|
||||
struct UnsafeWindowSurfaceOptions {
|
||||
system: UnsafeWindowSurfaceSystem,
|
||||
window_handle: *const c_void,
|
||||
display_handle: *const c_void,
|
||||
width: u32,
|
||||
height: u32,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum UnsafeWindowSurfaceSystem {
|
||||
Cocoa,
|
||||
Win32,
|
||||
X11,
|
||||
Wayland,
|
||||
}
|
||||
|
||||
impl<'a> FromV8<'a> for UnsafeWindowSurfaceOptions {
|
||||
type Error = JsErrorBox;
|
||||
|
||||
fn from_v8(
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
value: Local<'a, Value>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
let obj = value
|
||||
.try_cast::<v8::Object>()
|
||||
.map_err(|_| JsErrorBox::type_error("is not an object"))?;
|
||||
|
||||
let key = v8::String::new(scope, "system").unwrap();
|
||||
let val = obj
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| JsErrorBox::type_error("missing field 'system'"))?;
|
||||
let s = String::from_v8(scope, val).unwrap();
|
||||
let system = match s.as_str() {
|
||||
"cocoa" => UnsafeWindowSurfaceSystem::Cocoa,
|
||||
"win32" => UnsafeWindowSurfaceSystem::Win32,
|
||||
"x11" => UnsafeWindowSurfaceSystem::X11,
|
||||
"wayland" => UnsafeWindowSurfaceSystem::Wayland,
|
||||
_ => return Err(JsErrorBox::type_error(format!("Invalid system kind '{s}'"))),
|
||||
};
|
||||
|
||||
let key = v8::String::new(scope, "windowHandle").unwrap();
|
||||
let val = obj
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| JsErrorBox::type_error("missing field 'windowHandle'"))?;
|
||||
let Some(window_handle) = deno_core::_ops::to_external_option(&val) else {
|
||||
return Err(JsErrorBox::type_error("expected external"));
|
||||
};
|
||||
|
||||
let key = v8::String::new(scope, "displayHandle").unwrap();
|
||||
let val = obj
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| JsErrorBox::type_error("missing field 'displayHandle'"))?;
|
||||
let Some(display_handle) = deno_core::_ops::to_external_option(&val) else {
|
||||
return Err(JsErrorBox::type_error("expected external"));
|
||||
};
|
||||
|
||||
let key = v8::String::new(scope, "width").unwrap();
|
||||
let val = obj
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| JsErrorBox::type_error("missing field 'width'"))?;
|
||||
let width = deno_core::convert::Number::<u32>::from_v8(scope, val)?.0;
|
||||
|
||||
let key = v8::String::new(scope, "height").unwrap();
|
||||
let val = obj
|
||||
.get(scope, key.into())
|
||||
.ok_or_else(|| JsErrorBox::type_error("missing field 'height'"))?;
|
||||
let height = deno_core::convert::Number::<u32>::from_v8(scope, val)?.0;
|
||||
|
||||
Ok(Self {
|
||||
system,
|
||||
window_handle,
|
||||
display_handle,
|
||||
width,
|
||||
height,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type RawHandles = (
|
||||
@ -96,11 +236,11 @@ type RawHandles = (
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn raw_window(
|
||||
system: &str,
|
||||
system: UnsafeWindowSurfaceSystem,
|
||||
_ns_window: *const c_void,
|
||||
ns_view: *const c_void,
|
||||
) -> Result<RawHandles, ByowError> {
|
||||
if system != "cocoa" {
|
||||
if system != UnsafeWindowSurfaceSystem::Cocoa {
|
||||
return Err(ByowError::InvalidSystem);
|
||||
}
|
||||
|
||||
@ -116,12 +256,12 @@ fn raw_window(
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn raw_window(
|
||||
system: &str,
|
||||
system: UnsafeWindowSurfaceSystem,
|
||||
window: *const c_void,
|
||||
hinstance: *const c_void,
|
||||
) -> Result<RawHandles, ByowError> {
|
||||
use raw_window_handle::WindowsDisplayHandle;
|
||||
if system != "win32" {
|
||||
if system != UnsafeWindowSurfaceSystem::Win32 {
|
||||
return Err(ByowError::InvalidSystem);
|
||||
}
|
||||
|
||||
@ -140,12 +280,12 @@ fn raw_window(
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
|
||||
fn raw_window(
|
||||
system: &str,
|
||||
system: UnsafeWindowSurfaceSystem,
|
||||
window: *const c_void,
|
||||
display: *const c_void,
|
||||
) -> Result<RawHandles, ByowError> {
|
||||
let (win_handle, display_handle);
|
||||
if system == "x11" {
|
||||
if system == UnsafeWindowSurfaceSystem::X11 {
|
||||
win_handle = raw_window_handle::RawWindowHandle::Xlib(
|
||||
raw_window_handle::XlibWindowHandle::new(window as *mut c_void as _),
|
||||
);
|
||||
@ -153,7 +293,7 @@ fn raw_window(
|
||||
display_handle = raw_window_handle::RawDisplayHandle::Xlib(
|
||||
raw_window_handle::XlibDisplayHandle::new(NonNull::new(display as *mut c_void), 0),
|
||||
);
|
||||
} else if system == "wayland" {
|
||||
} else if system == UnsafeWindowSurfaceSystem::Wayland {
|
||||
win_handle = raw_window_handle::RawWindowHandle::Wayland(
|
||||
raw_window_handle::WaylandWindowHandle::new(
|
||||
NonNull::new(window as *mut c_void).ok_or(ByowError::NullWindow)?,
|
||||
@ -180,9 +320,9 @@ fn raw_window(
|
||||
target_os = "openbsd",
|
||||
)))]
|
||||
fn raw_window(
|
||||
_system: &str,
|
||||
_system: UnsafeWindowSurfaceSystem,
|
||||
_window: *const c_void,
|
||||
_display: *const c_void,
|
||||
) -> Result<RawHandles, deno_core::error::AnyError> {
|
||||
Err(deno_core::error::type_error("Unsupported platform"))
|
||||
) -> Result<RawHandles, deno_error::JsErrorBox> {
|
||||
Err(deno_error::JsErrorBox::type_error("Unsupported platform"))
|
||||
}
|
||||
|
52
deno_webgpu/command_buffer.rs
Normal file
52
deno_webgpu/command_buffer.rs
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::cell::OnceCell;
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUCommandBuffer {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::CommandBufferId,
|
||||
pub label: String,
|
||||
|
||||
pub consumed: OnceCell<()>,
|
||||
}
|
||||
|
||||
impl Drop for GPUCommandBuffer {
|
||||
fn drop(&mut self) {
|
||||
if self.consumed.get().is_none() {
|
||||
self.instance.command_buffer_drop(self.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl deno_core::webidl::WebIdlInterfaceConverter for GPUCommandBuffer {
|
||||
const NAME: &'static str = "GPUCommandBuffer";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUCommandBuffer {}
|
||||
|
||||
#[op2]
|
||||
impl GPUCommandBuffer {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUCommandBufferDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
}
|
@ -1,618 +1,402 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use crate::WebGpuQuerySet;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use deno_error::JsErrorBox;
|
||||
use wgpu_core::command::PassChannel;
|
||||
use wgpu_types::TexelCopyBufferInfo;
|
||||
|
||||
pub(crate) struct WebGpuCommandEncoder(
|
||||
pub(crate) super::Instance,
|
||||
pub(crate) wgpu_core::id::CommandEncoderId, // TODO: should maybe be option?
|
||||
);
|
||||
impl Resource for WebGpuCommandEncoder {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUCommandEncoder".into()
|
||||
}
|
||||
use crate::buffer::GPUBuffer;
|
||||
use crate::command_buffer::GPUCommandBuffer;
|
||||
use crate::compute_pass::GPUComputePassEncoder;
|
||||
use crate::queue::GPUTexelCopyTextureInfo;
|
||||
use crate::render_pass::GPULoadOp;
|
||||
use crate::render_pass::GPURenderPassEncoder;
|
||||
use crate::webidl::GPUExtent3D;
|
||||
use crate::Instance;
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.command_encoder_drop(self.1);
|
||||
pub struct GPUCommandEncoder {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
pub id: wgpu_core::id::CommandEncoderId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPUCommandEncoder {
|
||||
fn drop(&mut self) {
|
||||
self.instance.command_encoder_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuCommandBuffer(
|
||||
pub(crate) super::Instance,
|
||||
pub(crate) RefCell<Option<wgpu_core::id::CommandBufferId>>,
|
||||
);
|
||||
impl Resource for WebGpuCommandBuffer {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUCommandBuffer".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
if let Some(id) = *self.1.borrow() {
|
||||
self.0.command_buffer_drop(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl GarbageCollected for GPUCommandEncoder {}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_command_encoder(
|
||||
state: &mut OpState,
|
||||
#[smi] device_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let descriptor = wgpu_types::CommandEncoderDescriptor { label: Some(label) };
|
||||
|
||||
gfx_put!(instance.device_create_command_encoder(
|
||||
device,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, WebGpuCommandEncoder)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuRenderPassColorAttachment {
|
||||
view: ResourceId,
|
||||
resolve_target: Option<ResourceId>,
|
||||
clear_value: Option<wgpu_types::Color>,
|
||||
load_op: LoadOp,
|
||||
store_op: wgpu_core::command::StoreOp,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum LoadOp {
|
||||
/// Clear the output attachment with the clear color. Clearing is faster than loading.
|
||||
Clear = 0,
|
||||
/// Do not clear output attachment.
|
||||
Load = 1,
|
||||
}
|
||||
|
||||
impl LoadOp {
|
||||
fn into_wgt<V>(self, clear: V) -> wgpu_types::LoadOp<V> {
|
||||
match self {
|
||||
LoadOp::Clear => wgpu_types::LoadOp::Clear(clear),
|
||||
LoadOp::Load => wgpu_types::LoadOp::Load,
|
||||
}
|
||||
impl GPUCommandEncoder {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuRenderPassDepthStencilAttachment {
|
||||
view: ResourceId,
|
||||
depth_clear_value: Option<f32>,
|
||||
depth_load_op: Option<LoadOp>,
|
||||
depth_store_op: Option<wgpu_core::command::StoreOp>,
|
||||
depth_read_only: bool,
|
||||
stencil_clear_value: u32,
|
||||
stencil_load_op: Option<LoadOp>,
|
||||
stencil_store_op: Option<wgpu_core::command::StoreOp>,
|
||||
stencil_read_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPURenderPassTimestampWrites {
|
||||
query_set: ResourceId,
|
||||
beginning_of_pass_write_index: Option<u32>,
|
||||
end_of_pass_write_index: Option<u32>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_begin_render_pass(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[serde] color_attachments: Vec<Option<GpuRenderPassColorAttachment>>,
|
||||
#[serde] depth_stencil_attachment: Option<GpuRenderPassDepthStencilAttachment>,
|
||||
#[smi] occlusion_query_set: Option<ResourceId>,
|
||||
#[serde] timestamp_writes: Option<GPURenderPassTimestampWrites>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
|
||||
let color_attachments = color_attachments
|
||||
.into_iter()
|
||||
.map(|color_attachment| {
|
||||
let rp_at = if let Some(at) = color_attachment.as_ref() {
|
||||
let texture_view_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTextureView>(at.view)?;
|
||||
|
||||
let resolve_target = at
|
||||
.resolve_target
|
||||
.map(|rid| {
|
||||
state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTextureView>(rid)
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn begin_render_pass(
|
||||
&self,
|
||||
#[webidl] descriptor: crate::render_pass::GPURenderPassDescriptor,
|
||||
) -> Result<GPURenderPassEncoder, JsErrorBox> {
|
||||
let color_attachments = Cow::Owned(
|
||||
descriptor
|
||||
.color_attachments
|
||||
.into_iter()
|
||||
.map(|attachment| {
|
||||
attachment.into_option().map(|attachment| {
|
||||
wgpu_core::command::RenderPassColorAttachment {
|
||||
view: attachment.view.id,
|
||||
resolve_target: attachment.resolve_target.map(|target| target.id),
|
||||
load_op: attachment
|
||||
.load_op
|
||||
.with_default_value(attachment.clear_value.map(Into::into)),
|
||||
store_op: attachment.store_op.into(),
|
||||
}
|
||||
})
|
||||
.transpose()?
|
||||
.map(|texture| texture.1);
|
||||
|
||||
Some(wgpu_core::command::RenderPassColorAttachment {
|
||||
view: texture_view_resource.1,
|
||||
resolve_target,
|
||||
load_op: at.load_op.into_wgt(at.clear_value.unwrap_or_default()),
|
||||
store_op: at.store_op,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(rp_at)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
|
||||
let depth_stencil_attachment = descriptor
|
||||
.depth_stencil_attachment
|
||||
.map(|attachment| {
|
||||
if attachment
|
||||
.depth_load_op
|
||||
.as_ref()
|
||||
.is_some_and(|op| matches!(op, GPULoadOp::Clear))
|
||||
&& attachment.depth_clear_value.is_none()
|
||||
{
|
||||
return Err(JsErrorBox::type_error(
|
||||
r#"'depthClearValue' must be specified when 'depthLoadOp' is "clear""#,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(wgpu_core::command::RenderPassDepthStencilAttachment {
|
||||
view: attachment.view.id,
|
||||
depth: PassChannel {
|
||||
load_op: attachment
|
||||
.depth_load_op
|
||||
.map(|load_op| load_op.with_value(attachment.depth_clear_value)),
|
||||
store_op: attachment.depth_store_op.map(Into::into),
|
||||
read_only: attachment.depth_read_only,
|
||||
},
|
||||
stencil: PassChannel {
|
||||
load_op: attachment.stencil_load_op.map(|load_op| {
|
||||
load_op.with_value(Some(attachment.stencil_clear_value))
|
||||
}),
|
||||
store_op: attachment.stencil_store_op.map(Into::into),
|
||||
read_only: attachment.stencil_read_only,
|
||||
},
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let timestamp_writes = descriptor.timestamp_writes.map(|timestamp_writes| {
|
||||
wgpu_core::command::PassTimestampWrites {
|
||||
query_set: timestamp_writes.query_set.id,
|
||||
beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index,
|
||||
end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,
|
||||
}
|
||||
});
|
||||
|
||||
let wgpu_descriptor = wgpu_core::command::RenderPassDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
color_attachments,
|
||||
depth_stencil_attachment: depth_stencil_attachment.as_ref(),
|
||||
timestamp_writes: timestamp_writes.as_ref(),
|
||||
occlusion_query_set: descriptor.occlusion_query_set.map(|query_set| query_set.id),
|
||||
};
|
||||
|
||||
let (render_pass, err) = self
|
||||
.instance
|
||||
.command_encoder_create_render_pass(self.id, &wgpu_descriptor);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(GPURenderPassEncoder {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
render_pass: RefCell::new(render_pass),
|
||||
label: descriptor.label,
|
||||
})
|
||||
.collect::<Result<Vec<_>, AnyError>>()?;
|
||||
|
||||
let mut processed_depth_stencil_attachment = None;
|
||||
|
||||
if let Some(attachment) = depth_stencil_attachment {
|
||||
let texture_view_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTextureView>(attachment.view)?;
|
||||
|
||||
processed_depth_stencil_attachment =
|
||||
Some(wgpu_core::command::RenderPassDepthStencilAttachment {
|
||||
view: texture_view_resource.1,
|
||||
depth: wgpu_core::command::PassChannel {
|
||||
load_op: attachment
|
||||
.depth_load_op
|
||||
.map(|load_op| load_op.into_wgt(attachment.depth_clear_value)),
|
||||
store_op: attachment.depth_store_op,
|
||||
read_only: attachment.depth_read_only,
|
||||
},
|
||||
stencil: wgpu_core::command::PassChannel {
|
||||
load_op: attachment
|
||||
.stencil_load_op
|
||||
.map(|load_op| load_op.into_wgt(Some(attachment.stencil_clear_value))),
|
||||
store_op: attachment.stencil_store_op,
|
||||
read_only: attachment.stencil_read_only,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
let timestamp_writes = if let Some(timestamp_writes) = timestamp_writes {
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuQuerySet>(timestamp_writes.query_set)?;
|
||||
let query_set = query_set_resource.1;
|
||||
#[cppgc]
|
||||
fn begin_compute_pass(
|
||||
&self,
|
||||
#[webidl] descriptor: crate::compute_pass::GPUComputePassDescriptor,
|
||||
) -> GPUComputePassEncoder {
|
||||
let timestamp_writes = descriptor.timestamp_writes.map(|timestamp_writes| {
|
||||
wgpu_core::command::PassTimestampWrites {
|
||||
query_set: timestamp_writes.query_set.id,
|
||||
beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index,
|
||||
end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,
|
||||
}
|
||||
});
|
||||
|
||||
Some(wgpu_core::command::PassTimestampWrites {
|
||||
query_set,
|
||||
beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index,
|
||||
end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let wgpu_descriptor = wgpu_core::command::ComputePassDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
timestamp_writes,
|
||||
};
|
||||
|
||||
let occlusion_query_set_resource = occlusion_query_set
|
||||
.map(|rid| state.resource_table.get::<WebGpuQuerySet>(rid))
|
||||
.transpose()?
|
||||
.map(|query_set| query_set.1);
|
||||
let (compute_pass, err) = self
|
||||
.instance
|
||||
.command_encoder_create_compute_pass(self.id, &wgpu_descriptor);
|
||||
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder = &command_encoder_resource.1;
|
||||
let descriptor = wgpu_core::command::RenderPassDescriptor {
|
||||
label: Some(label),
|
||||
color_attachments: Cow::from(color_attachments),
|
||||
depth_stencil_attachment: processed_depth_stencil_attachment.as_ref(),
|
||||
timestamp_writes: timestamp_writes.as_ref(),
|
||||
occlusion_query_set: occlusion_query_set_resource,
|
||||
};
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
let (render_pass, error) =
|
||||
instance.command_encoder_create_render_pass(*command_encoder, &descriptor);
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::render_pass::WebGpuRenderPass(RefCell::new(
|
||||
render_pass,
|
||||
)));
|
||||
GPUComputePassEncoder {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
compute_pass: RefCell::new(compute_pass),
|
||||
label: descriptor.label,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(WebGpuResult::rid_err(rid, error))
|
||||
#[required(5)]
|
||||
fn copy_buffer_to_buffer(
|
||||
&self,
|
||||
#[webidl] source: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] source_offset: u64,
|
||||
#[webidl] destination: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] destination_offset: u64,
|
||||
#[webidl(options(enforce_range = true))] size: u64,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_copy_buffer_to_buffer(
|
||||
self.id,
|
||||
source.id,
|
||||
source_offset,
|
||||
destination.id,
|
||||
destination_offset,
|
||||
size,
|
||||
)
|
||||
.err();
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(3)]
|
||||
fn copy_buffer_to_texture(
|
||||
&self,
|
||||
#[webidl] source: GPUTexelCopyBufferInfo,
|
||||
#[webidl] destination: GPUTexelCopyTextureInfo,
|
||||
#[webidl] copy_size: GPUExtent3D,
|
||||
) {
|
||||
let source = TexelCopyBufferInfo {
|
||||
buffer: source.buffer.id,
|
||||
layout: wgpu_types::TexelCopyBufferLayout {
|
||||
offset: source.offset,
|
||||
bytes_per_row: source.bytes_per_row,
|
||||
rows_per_image: source.rows_per_image,
|
||||
},
|
||||
};
|
||||
let destination = wgpu_types::TexelCopyTextureInfo {
|
||||
texture: destination.texture.id,
|
||||
mip_level: destination.mip_level,
|
||||
origin: destination.origin.into(),
|
||||
aspect: destination.aspect.into(),
|
||||
};
|
||||
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_copy_buffer_to_texture(
|
||||
self.id,
|
||||
&source,
|
||||
&destination,
|
||||
©_size.into(),
|
||||
)
|
||||
.err();
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(3)]
|
||||
fn copy_texture_to_buffer(
|
||||
&self,
|
||||
#[webidl] source: GPUTexelCopyTextureInfo,
|
||||
#[webidl] destination: GPUTexelCopyBufferInfo,
|
||||
#[webidl] copy_size: GPUExtent3D,
|
||||
) {
|
||||
let source = wgpu_types::TexelCopyTextureInfo {
|
||||
texture: source.texture.id,
|
||||
mip_level: source.mip_level,
|
||||
origin: source.origin.into(),
|
||||
aspect: source.aspect.into(),
|
||||
};
|
||||
let destination = TexelCopyBufferInfo {
|
||||
buffer: destination.buffer.id,
|
||||
layout: wgpu_types::TexelCopyBufferLayout {
|
||||
offset: destination.offset,
|
||||
bytes_per_row: destination.bytes_per_row,
|
||||
rows_per_image: destination.rows_per_image,
|
||||
},
|
||||
};
|
||||
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_copy_texture_to_buffer(
|
||||
self.id,
|
||||
&source,
|
||||
&destination,
|
||||
©_size.into(),
|
||||
)
|
||||
.err();
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(3)]
|
||||
fn copy_texture_to_texture(
|
||||
&self,
|
||||
#[webidl] source: GPUTexelCopyTextureInfo,
|
||||
#[webidl] destination: GPUTexelCopyTextureInfo,
|
||||
#[webidl] copy_size: GPUExtent3D,
|
||||
) {
|
||||
let source = wgpu_types::TexelCopyTextureInfo {
|
||||
texture: source.texture.id,
|
||||
mip_level: source.mip_level,
|
||||
origin: source.origin.into(),
|
||||
aspect: source.aspect.into(),
|
||||
};
|
||||
let destination = wgpu_types::TexelCopyTextureInfo {
|
||||
texture: destination.texture.id,
|
||||
mip_level: destination.mip_level,
|
||||
origin: destination.origin.into(),
|
||||
aspect: destination.aspect.into(),
|
||||
};
|
||||
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_copy_texture_to_texture(
|
||||
self.id,
|
||||
&source,
|
||||
&destination,
|
||||
©_size.into(),
|
||||
)
|
||||
.err();
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn clear_buffer(
|
||||
&self,
|
||||
#[webidl] buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(default = 0, options(enforce_range = true))] offset: u64,
|
||||
#[webidl(options(enforce_range = true))] size: Option<u64>,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_clear_buffer(self.id, buffer.id, offset, size)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(5)]
|
||||
fn resolve_query_set(
|
||||
&self,
|
||||
#[webidl] query_set: Ptr<super::query_set::GPUQuerySet>,
|
||||
#[webidl(options(enforce_range = true))] first_query: u32,
|
||||
#[webidl(options(enforce_range = true))] query_count: u32,
|
||||
#[webidl] destination: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] destination_offset: u64,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_resolve_query_set(
|
||||
self.id,
|
||||
query_set.id,
|
||||
first_query,
|
||||
query_count,
|
||||
destination.id,
|
||||
destination_offset,
|
||||
)
|
||||
.err();
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[cppgc]
|
||||
fn finish(
|
||||
&self,
|
||||
#[webidl] descriptor: crate::command_buffer::GPUCommandBufferDescriptor,
|
||||
) -> GPUCommandBuffer {
|
||||
let wgpu_descriptor = wgpu_types::CommandBufferDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
};
|
||||
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.command_encoder_finish(self.id, &wgpu_descriptor);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPUCommandBuffer {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
consumed: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_debug_group(&self, #[webidl] group_label: String) {
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_push_debug_group(self.id, &group_label)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn pop_debug_group(&self) {
|
||||
let err = self.instance.command_encoder_pop_debug_group(self.id).err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn insert_debug_marker(&self, #[webidl] marker_label: String) {
|
||||
let err = self
|
||||
.instance
|
||||
.command_encoder_insert_debug_marker(self.id, &marker_label)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPUComputePassTimestampWrites {
|
||||
query_set: ResourceId,
|
||||
beginning_of_pass_write_index: Option<u32>,
|
||||
end_of_pass_write_index: Option<u32>,
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUCommandEncoderDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_begin_compute_pass(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[serde] timestamp_writes: Option<GPUComputePassTimestampWrites>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
|
||||
let timestamp_writes = if let Some(timestamp_writes) = timestamp_writes {
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuQuerySet>(timestamp_writes.query_set)?;
|
||||
let query_set = query_set_resource.1;
|
||||
|
||||
Some(wgpu_core::command::PassTimestampWrites {
|
||||
query_set,
|
||||
beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index,
|
||||
end_of_pass_write_index: timestamp_writes.end_of_pass_write_index,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder = &command_encoder_resource.1;
|
||||
let descriptor = wgpu_core::command::ComputePassDescriptor {
|
||||
label: Some(label),
|
||||
timestamp_writes,
|
||||
};
|
||||
|
||||
let (compute_pass, error) =
|
||||
instance.command_encoder_create_compute_pass(*command_encoder, &descriptor);
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::compute_pass::WebGpuComputePass(RefCell::new(
|
||||
compute_pass,
|
||||
)));
|
||||
|
||||
Ok(WebGpuResult::rid_err(rid, error))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_copy_buffer_to_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[smi] source: ResourceId,
|
||||
#[number] source_offset: u64,
|
||||
#[smi] destination: ResourceId,
|
||||
#[number] destination_offset: u64,
|
||||
#[number] size: u64,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let source_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(source)?;
|
||||
let source_buffer = source_buffer_resource.1;
|
||||
let destination_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(destination)?;
|
||||
let destination_buffer = destination_buffer_resource.1;
|
||||
|
||||
gfx_ok!(instance.command_encoder_copy_buffer_to_buffer(
|
||||
command_encoder,
|
||||
source_buffer,
|
||||
source_offset,
|
||||
destination_buffer,
|
||||
destination_offset,
|
||||
size
|
||||
))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuTexelCopyBufferInfo {
|
||||
buffer: ResourceId,
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUTexelCopyBufferInfo {
|
||||
pub buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
offset: u64,
|
||||
#[options(enforce_range = true)]
|
||||
bytes_per_row: Option<u32>,
|
||||
#[options(enforce_range = true)]
|
||||
rows_per_image: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuTexelCopyTextureInfo {
|
||||
pub texture: ResourceId,
|
||||
pub mip_level: u32,
|
||||
pub origin: wgpu_types::Origin3d,
|
||||
pub aspect: wgpu_types::TextureAspect,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_copy_buffer_to_texture(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[serde] source: GpuTexelCopyBufferInfo,
|
||||
#[serde] destination: GpuTexelCopyTextureInfo,
|
||||
#[serde] copy_size: wgpu_types::Extent3d,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let source_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(source.buffer)?;
|
||||
let destination_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTexture>(destination.texture)?;
|
||||
|
||||
let source = wgpu_core::command::TexelCopyBufferInfo {
|
||||
buffer: source_buffer_resource.1,
|
||||
layout: wgpu_types::TexelCopyBufferLayout {
|
||||
offset: source.offset,
|
||||
bytes_per_row: source.bytes_per_row,
|
||||
rows_per_image: source.rows_per_image,
|
||||
},
|
||||
};
|
||||
let destination = wgpu_core::command::TexelCopyTextureInfo {
|
||||
texture: destination_texture_resource.id,
|
||||
mip_level: destination.mip_level,
|
||||
origin: destination.origin,
|
||||
aspect: destination.aspect,
|
||||
};
|
||||
gfx_ok!(instance.command_encoder_copy_buffer_to_texture(
|
||||
command_encoder,
|
||||
&source,
|
||||
&destination,
|
||||
©_size
|
||||
))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_copy_texture_to_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[serde] source: GpuTexelCopyTextureInfo,
|
||||
#[serde] destination: GpuTexelCopyBufferInfo,
|
||||
#[serde] copy_size: wgpu_types::Extent3d,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let source_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTexture>(source.texture)?;
|
||||
let destination_buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(destination.buffer)?;
|
||||
|
||||
let source = wgpu_core::command::TexelCopyTextureInfo {
|
||||
texture: source_texture_resource.id,
|
||||
mip_level: source.mip_level,
|
||||
origin: source.origin,
|
||||
aspect: source.aspect,
|
||||
};
|
||||
let destination = wgpu_core::command::TexelCopyBufferInfo {
|
||||
buffer: destination_buffer_resource.1,
|
||||
layout: wgpu_types::TexelCopyBufferLayout {
|
||||
offset: destination.offset,
|
||||
bytes_per_row: destination.bytes_per_row,
|
||||
rows_per_image: destination.rows_per_image,
|
||||
},
|
||||
};
|
||||
gfx_ok!(instance.command_encoder_copy_texture_to_buffer(
|
||||
command_encoder,
|
||||
&source,
|
||||
&destination,
|
||||
©_size
|
||||
))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_copy_texture_to_texture(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[serde] source: GpuTexelCopyTextureInfo,
|
||||
#[serde] destination: GpuTexelCopyTextureInfo,
|
||||
#[serde] copy_size: wgpu_types::Extent3d,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let source_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTexture>(source.texture)?;
|
||||
let destination_texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTexture>(destination.texture)?;
|
||||
|
||||
let source = wgpu_core::command::TexelCopyTextureInfo {
|
||||
texture: source_texture_resource.id,
|
||||
mip_level: source.mip_level,
|
||||
origin: source.origin,
|
||||
aspect: source.aspect,
|
||||
};
|
||||
let destination = wgpu_core::command::TexelCopyTextureInfo {
|
||||
texture: destination_texture_resource.id,
|
||||
mip_level: destination.mip_level,
|
||||
origin: destination.origin,
|
||||
aspect: destination.aspect,
|
||||
};
|
||||
gfx_ok!(instance.command_encoder_copy_texture_to_texture(
|
||||
command_encoder,
|
||||
&source,
|
||||
&destination,
|
||||
©_size
|
||||
))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_clear_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[smi] buffer_rid: ResourceId,
|
||||
#[number] offset: u64,
|
||||
#[number] size: u64,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let destination_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(buffer_rid)?;
|
||||
|
||||
gfx_ok!(instance.command_encoder_clear_buffer(
|
||||
command_encoder,
|
||||
destination_resource.1,
|
||||
offset,
|
||||
Some(size)
|
||||
))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_push_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[string] group_label: &str,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
|
||||
gfx_ok!(instance.command_encoder_push_debug_group(command_encoder, group_label))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
|
||||
gfx_ok!(instance.command_encoder_pop_debug_group(command_encoder))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[string] marker_label: &str,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
|
||||
gfx_ok!(instance.command_encoder_insert_debug_marker(command_encoder, marker_label))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_write_timestamp(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[smi] query_set: ResourceId,
|
||||
query_index: u32,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuQuerySet>(query_set)?;
|
||||
|
||||
gfx_ok!(instance.command_encoder_write_timestamp(
|
||||
command_encoder,
|
||||
query_set_resource.1,
|
||||
query_index
|
||||
))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_resolve_query_set(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[smi] query_set: ResourceId,
|
||||
first_query: u32,
|
||||
query_count: u32,
|
||||
#[smi] destination: ResourceId,
|
||||
#[number] destination_offset: u64,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let query_set_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuQuerySet>(query_set)?;
|
||||
let destination_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(destination)?;
|
||||
|
||||
gfx_ok!(instance.command_encoder_resolve_query_set(
|
||||
command_encoder,
|
||||
query_set_resource.1,
|
||||
first_query,
|
||||
query_count,
|
||||
destination_resource.1,
|
||||
destination_offset
|
||||
))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_command_encoder_finish(
|
||||
state: &mut OpState,
|
||||
#[smi] command_encoder_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let command_encoder_resource = state
|
||||
.resource_table
|
||||
.take::<WebGpuCommandEncoder>(command_encoder_rid)?;
|
||||
let command_encoder = command_encoder_resource.1;
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
|
||||
let descriptor = wgpu_types::CommandBufferDescriptor { label: Some(label) };
|
||||
|
||||
let (val, maybe_err) = instance.command_encoder_finish(command_encoder, &descriptor);
|
||||
|
||||
let rid = state.resource_table.add(WebGpuCommandBuffer(
|
||||
instance.clone(),
|
||||
RefCell::new(Some(val)),
|
||||
));
|
||||
|
||||
Ok(WebGpuResult::rid_err(rid, maybe_err))
|
||||
}
|
||||
|
@ -1,206 +1,220 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::webidl::IntOptions;
|
||||
use deno_core::webidl::Nullable;
|
||||
use deno_core::webidl::WebIdlConverter;
|
||||
use deno_core::webidl::WebIdlError;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
pub(crate) struct WebGpuComputePass(pub(crate) RefCell<wgpu_core::command::ComputePass>);
|
||||
impl Resource for WebGpuComputePass {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUComputePass".into()
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUComputePassEncoder {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
pub compute_pass: RefCell<wgpu_core::command::ComputePass>,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUComputePassEncoder {}
|
||||
|
||||
#[op2]
|
||||
impl GPUComputePassEncoder {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
fn set_pipeline(&self, #[webidl] pipeline: Ptr<crate::compute_pipeline::GPUComputePipeline>) {
|
||||
let err = self
|
||||
.instance
|
||||
.compute_pass_set_pipeline(&mut self.compute_pass.borrow_mut(), pipeline.id)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn dispatch_workgroups(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] work_group_count_x: u32,
|
||||
#[webidl(default = 1, options(enforce_range = true))] work_group_count_y: u32,
|
||||
#[webidl(default = 1, options(enforce_range = true))] work_group_count_z: u32,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.compute_pass_dispatch_workgroups(
|
||||
&mut self.compute_pass.borrow_mut(),
|
||||
work_group_count_x,
|
||||
work_group_count_y,
|
||||
work_group_count_z,
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn dispatch_workgroups_indirect(
|
||||
&self,
|
||||
#[webidl] indirect_buffer: Ptr<crate::buffer::GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] indirect_offset: u64,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.compute_pass_dispatch_workgroups_indirect(
|
||||
&mut self.compute_pass.borrow_mut(),
|
||||
indirect_buffer.id,
|
||||
indirect_offset,
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn end(&self) {
|
||||
let err = self
|
||||
.instance
|
||||
.compute_pass_end(&mut self.compute_pass.borrow_mut())
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn push_debug_group(&self, #[webidl] group_label: String) {
|
||||
let err = self
|
||||
.instance
|
||||
.compute_pass_push_debug_group(
|
||||
&mut self.compute_pass.borrow_mut(),
|
||||
&group_label,
|
||||
0, // wgpu#975
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn pop_debug_group(&self) {
|
||||
let err = self
|
||||
.instance
|
||||
.compute_pass_pop_debug_group(&mut self.compute_pass.borrow_mut())
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn insert_debug_marker(&self, #[webidl] marker_label: String) {
|
||||
let err = self
|
||||
.instance
|
||||
.compute_pass_insert_debug_marker(
|
||||
&mut self.compute_pass.borrow_mut(),
|
||||
&marker_label,
|
||||
0, // wgpu#975
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn set_bind_group<'a>(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
#[webidl(options(enforce_range = true))] index: u32,
|
||||
#[webidl] bind_group: Nullable<Ptr<crate::bind_group::GPUBindGroup>>,
|
||||
dynamic_offsets: v8::Local<'a, v8::Value>,
|
||||
dynamic_offsets_data_start: v8::Local<'a, v8::Value>,
|
||||
dynamic_offsets_data_length: v8::Local<'a, v8::Value>,
|
||||
) -> Result<(), WebIdlError> {
|
||||
const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'";
|
||||
let err = if let Ok(uint_32) = dynamic_offsets.try_cast::<v8::Uint32Array>() {
|
||||
let start = u64::convert(
|
||||
scope,
|
||||
dynamic_offsets_data_start,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 4")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)? as usize;
|
||||
let len = u32::convert(
|
||||
scope,
|
||||
dynamic_offsets_data_length,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 5")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)? as usize;
|
||||
|
||||
let ab = uint_32.buffer(scope).unwrap();
|
||||
let ptr = ab.data().unwrap();
|
||||
let ab_len = ab.byte_length() / 4;
|
||||
|
||||
// SAFETY: compute_pass_set_bind_group internally calls extend_from_slice with this slice
|
||||
let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) };
|
||||
|
||||
let offsets = &data[start..(start + len)];
|
||||
|
||||
self.instance
|
||||
.compute_pass_set_bind_group(
|
||||
&mut self.compute_pass.borrow_mut(),
|
||||
index,
|
||||
bind_group.into_option().map(|bind_group| bind_group.id),
|
||||
offsets,
|
||||
)
|
||||
.err()
|
||||
} else {
|
||||
let offsets = <Option<Vec<u32>>>::convert(
|
||||
scope,
|
||||
dynamic_offsets,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 3")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)?
|
||||
.unwrap_or_default();
|
||||
|
||||
self.instance
|
||||
.compute_pass_set_bind_group(
|
||||
&mut self.compute_pass.borrow_mut(),
|
||||
index,
|
||||
bind_group.into_option().map(|bind_group| bind_group.id),
|
||||
&offsets,
|
||||
)
|
||||
.err()
|
||||
};
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_set_pipeline(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
#[smi] pipeline: ResourceId,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let compute_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<super::pipeline::WebGpuComputePipeline>(pipeline)?;
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUComputePassDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_set_pipeline(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
compute_pipeline_resource.1,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
pub timestamp_writes: Option<GPUComputePassTimestampWrites>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_dispatch_workgroups(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
x: u32,
|
||||
y: u32,
|
||||
z: u32,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_dispatch_workgroups(&mut compute_pass_resource.0.borrow_mut(), x, y, z)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_dispatch_workgroups_indirect(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
#[smi] indirect_buffer: ResourceId,
|
||||
#[number] indirect_offset: u64,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_dispatch_workgroups_indirect(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
buffer_resource.1,
|
||||
indirect_offset,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_end(
|
||||
state: &mut OpState,
|
||||
#[smi] _command_encoder_rid: ResourceId,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.take::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_end(&mut compute_pass_resource.0.borrow_mut())?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_set_bind_group(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
index: u32,
|
||||
#[smi] bind_group: ResourceId,
|
||||
#[buffer] dynamic_offsets_data: &[u32],
|
||||
#[number] dynamic_offsets_data_start: usize,
|
||||
#[number] dynamic_offsets_data_length: usize,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let bind_group_resource = state
|
||||
.resource_table
|
||||
.get::<super::binding::WebGpuBindGroup>(bind_group)?;
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
|
||||
let start = dynamic_offsets_data_start;
|
||||
let len = dynamic_offsets_data_length;
|
||||
|
||||
// Assert that length and start are both in bounds
|
||||
assert!(start <= dynamic_offsets_data.len());
|
||||
assert!(len <= dynamic_offsets_data.len() - start);
|
||||
|
||||
let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_set_bind_group(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
index,
|
||||
Some(bind_group_resource.1),
|
||||
dynamic_offsets_data,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_push_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
#[string] group_label: &str,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_push_debug_group(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
group_label,
|
||||
0, // wgpu#975
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_pop_debug_group(&mut compute_pass_resource.0.borrow_mut())?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pass_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pass_rid: ResourceId,
|
||||
#[string] marker_label: &str,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let compute_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePass>(compute_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.compute_pass_insert_debug_marker(
|
||||
&mut compute_pass_resource.0.borrow_mut(),
|
||||
marker_label,
|
||||
0, // wgpu#975
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUComputePassTimestampWrites {
|
||||
pub query_set: Ptr<crate::query_set::GPUQuerySet>,
|
||||
#[options(enforce_range = true)]
|
||||
pub beginning_of_pass_write_index: Option<u32>,
|
||||
#[options(enforce_range = true)]
|
||||
pub end_of_pass_write_index: Option<u32>,
|
||||
}
|
||||
|
82
deno_webgpu/compute_pipeline.rs
Normal file
82
deno_webgpu/compute_pipeline.rs
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::bind_group_layout::GPUBindGroupLayout;
|
||||
use crate::shader::GPUShaderModule;
|
||||
use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUComputePipeline {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
pub id: wgpu_core::id::ComputePipelineId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPUComputePipeline {
|
||||
fn drop(&mut self) {
|
||||
self.instance.compute_pipeline_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPUComputePipeline {
|
||||
const NAME: &'static str = "GPUComputePipeline";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUComputePipeline {}
|
||||
|
||||
#[op2]
|
||||
impl GPUComputePipeline {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
#[cppgc]
|
||||
fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout {
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.compute_pipeline_get_bind_group_layout(self.id, index, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
// TODO(wgpu): needs to support retrieving the label
|
||||
GPUBindGroupLayout {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUComputePipelineDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub compute: GPUProgrammableStage,
|
||||
pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUProgrammableStage {
|
||||
pub module: Ptr<GPUShaderModule>,
|
||||
pub entry_point: Option<String>,
|
||||
#[webidl(default = Default::default())]
|
||||
pub constants: IndexMap<String, f64>,
|
||||
}
|
843
deno_webgpu/device.rs
Normal file
843
deno_webgpu/device.rs
Normal file
@ -0,0 +1,843 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZeroU64;
|
||||
use std::rc::Rc;
|
||||
|
||||
use deno_core::cppgc::SameObject;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_error::JsErrorBox;
|
||||
use wgpu_core::binding_model::BindingResource;
|
||||
use wgpu_core::pipeline::ProgrammableStageDescriptor;
|
||||
use wgpu_types::BindingType;
|
||||
|
||||
use super::bind_group::GPUBindGroup;
|
||||
use super::bind_group::GPUBindingResource;
|
||||
use super::bind_group_layout::GPUBindGroupLayout;
|
||||
use super::buffer::GPUBuffer;
|
||||
use super::compute_pipeline::GPUComputePipeline;
|
||||
use super::pipeline_layout::GPUPipelineLayout;
|
||||
use super::queue::GPUQueue;
|
||||
use super::sampler::GPUSampler;
|
||||
use super::shader::GPUShaderModule;
|
||||
use super::texture::GPUTexture;
|
||||
use crate::adapter::GPUAdapterInfo;
|
||||
use crate::adapter::GPUSupportedFeatures;
|
||||
use crate::adapter::GPUSupportedLimits;
|
||||
use crate::command_encoder::GPUCommandEncoder;
|
||||
use crate::query_set::GPUQuerySet;
|
||||
use crate::render_bundle::GPURenderBundleEncoder;
|
||||
use crate::render_pipeline::GPURenderPipeline;
|
||||
use crate::webidl::features_to_feature_names;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUDevice {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::DeviceId,
|
||||
pub adapter: wgpu_core::id::AdapterId,
|
||||
pub queue: wgpu_core::id::QueueId,
|
||||
|
||||
pub label: String,
|
||||
|
||||
pub features: SameObject<GPUSupportedFeatures>,
|
||||
pub limits: SameObject<GPUSupportedLimits>,
|
||||
pub adapter_info: Rc<SameObject<GPUAdapterInfo>>,
|
||||
|
||||
pub queue_obj: SameObject<GPUQueue>,
|
||||
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
pub lost_receiver: tokio::sync::Mutex<Option<tokio::sync::oneshot::Receiver<()>>>,
|
||||
}
|
||||
|
||||
impl Drop for GPUDevice {
|
||||
fn drop(&mut self) {
|
||||
self.instance.device_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPUDevice {
|
||||
const NAME: &'static str = "GPUDevice";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUDevice {}
|
||||
|
||||
// EventTarget is extended in JS
|
||||
#[op2]
|
||||
impl GPUDevice {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[global]
|
||||
fn features(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
||||
self.features.get(scope, |scope| {
|
||||
let features = self.instance.device_features(self.id);
|
||||
let features = features_to_feature_names(features);
|
||||
GPUSupportedFeatures::new(scope, features)
|
||||
})
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[global]
|
||||
fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
||||
self.limits.get(scope, |_| {
|
||||
let limits = self.instance.device_limits(self.id);
|
||||
GPUSupportedLimits(limits)
|
||||
})
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[global]
|
||||
fn adapter_info(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
||||
self.adapter_info.get(scope, |_| {
|
||||
let info = self.instance.adapter_get_info(self.adapter);
|
||||
let limits = self.instance.adapter_limits(self.adapter);
|
||||
|
||||
GPUAdapterInfo {
|
||||
info,
|
||||
subgroup_min_size: limits.min_subgroup_size,
|
||||
subgroup_max_size: limits.max_subgroup_size,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[global]
|
||||
fn queue(&self, scope: &mut v8::HandleScope) -> v8::Global<v8::Object> {
|
||||
self.queue_obj.get(scope, |_| GPUQueue {
|
||||
id: self.queue,
|
||||
error_handler: self.error_handler.clone(),
|
||||
instance: self.instance.clone(),
|
||||
label: self.label.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn destroy(&self) {
|
||||
self.instance.device_destroy(self.id);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_buffer(
|
||||
&self,
|
||||
#[webidl] descriptor: super::buffer::GPUBufferDescriptor,
|
||||
) -> Result<GPUBuffer, JsErrorBox> {
|
||||
let wgpu_descriptor = wgpu_core::resource::BufferDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
size: descriptor.size,
|
||||
usage: wgpu_types::BufferUsages::from_bits(descriptor.usage)
|
||||
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
|
||||
mapped_at_creation: descriptor.mapped_at_creation,
|
||||
};
|
||||
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.device_create_buffer(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(GPUBuffer {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
id,
|
||||
device: self.id,
|
||||
label: descriptor.label,
|
||||
size: descriptor.size,
|
||||
usage: descriptor.usage,
|
||||
map_state: RefCell::new(if descriptor.mapped_at_creation {
|
||||
"mapped"
|
||||
} else {
|
||||
"unmapped"
|
||||
}),
|
||||
map_mode: RefCell::new(if descriptor.mapped_at_creation {
|
||||
Some(wgpu_core::device::HostMap::Write)
|
||||
} else {
|
||||
None
|
||||
}),
|
||||
mapped_js_buffers: RefCell::new(vec![]),
|
||||
})
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_texture(
|
||||
&self,
|
||||
#[webidl] descriptor: super::texture::GPUTextureDescriptor,
|
||||
) -> Result<GPUTexture, JsErrorBox> {
|
||||
let wgpu_descriptor = wgpu_core::resource::TextureDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
size: descriptor.size.into(),
|
||||
mip_level_count: descriptor.mip_level_count,
|
||||
sample_count: descriptor.sample_count,
|
||||
dimension: descriptor.dimension.clone().into(),
|
||||
format: descriptor.format.clone().into(),
|
||||
usage: wgpu_types::TextureUsages::from_bits(descriptor.usage)
|
||||
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
|
||||
view_formats: descriptor
|
||||
.view_formats
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.device_create_texture(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(GPUTexture {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
size: wgpu_descriptor.size,
|
||||
mip_level_count: wgpu_descriptor.mip_level_count,
|
||||
sample_count: wgpu_descriptor.sample_count,
|
||||
dimension: descriptor.dimension,
|
||||
format: descriptor.format,
|
||||
usage: descriptor.usage,
|
||||
})
|
||||
}
|
||||
|
||||
#[cppgc]
|
||||
fn create_sampler(
|
||||
&self,
|
||||
#[webidl] descriptor: super::sampler::GPUSamplerDescriptor,
|
||||
) -> Result<GPUSampler, JsErrorBox> {
|
||||
let wgpu_descriptor = wgpu_core::resource::SamplerDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
address_modes: [
|
||||
descriptor.address_mode_u.into(),
|
||||
descriptor.address_mode_v.into(),
|
||||
descriptor.address_mode_w.into(),
|
||||
],
|
||||
mag_filter: descriptor.mag_filter.into(),
|
||||
min_filter: descriptor.min_filter.into(),
|
||||
mipmap_filter: descriptor.mipmap_filter.into(),
|
||||
lod_min_clamp: descriptor.lod_min_clamp,
|
||||
lod_max_clamp: descriptor.lod_max_clamp,
|
||||
compare: descriptor.compare.map(Into::into),
|
||||
anisotropy_clamp: descriptor.max_anisotropy,
|
||||
border_color: None,
|
||||
};
|
||||
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.device_create_sampler(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(GPUSampler {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
})
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_bind_group_layout(
|
||||
&self,
|
||||
#[webidl] descriptor: super::bind_group_layout::GPUBindGroupLayoutDescriptor,
|
||||
) -> Result<GPUBindGroupLayout, JsErrorBox> {
|
||||
let mut entries = Vec::with_capacity(descriptor.entries.len());
|
||||
|
||||
for entry in descriptor.entries {
|
||||
let n_entries = [
|
||||
entry.buffer.is_some(),
|
||||
entry.sampler.is_some(),
|
||||
entry.texture.is_some(),
|
||||
entry.storage_texture.is_some(),
|
||||
]
|
||||
.into_iter()
|
||||
.filter(|t| *t)
|
||||
.count();
|
||||
|
||||
if n_entries != 1 {
|
||||
return Err(JsErrorBox::type_error("Only one of 'buffer', 'sampler', 'texture' and 'storageTexture' may be specified"));
|
||||
}
|
||||
|
||||
let ty = if let Some(buffer) = entry.buffer {
|
||||
BindingType::Buffer {
|
||||
ty: buffer.r#type.into(),
|
||||
has_dynamic_offset: buffer.has_dynamic_offset,
|
||||
min_binding_size: NonZeroU64::new(buffer.min_binding_size),
|
||||
}
|
||||
} else if let Some(sampler) = entry.sampler {
|
||||
BindingType::Sampler(sampler.r#type.into())
|
||||
} else if let Some(texture) = entry.texture {
|
||||
BindingType::Texture {
|
||||
sample_type: texture.sample_type.into(),
|
||||
view_dimension: texture.view_dimension.into(),
|
||||
multisampled: texture.multisampled,
|
||||
}
|
||||
} else if let Some(storage_texture) = entry.storage_texture {
|
||||
BindingType::StorageTexture {
|
||||
access: storage_texture.access.into(),
|
||||
format: storage_texture.format.into(),
|
||||
view_dimension: storage_texture.view_dimension.into(),
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
entries.push(wgpu_types::BindGroupLayoutEntry {
|
||||
binding: entry.binding,
|
||||
visibility: wgpu_types::ShaderStages::from_bits(entry.visibility)
|
||||
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
|
||||
ty,
|
||||
count: None, // native-only
|
||||
});
|
||||
}
|
||||
|
||||
let wgpu_descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
entries: Cow::Owned(entries),
|
||||
};
|
||||
|
||||
let (id, err) =
|
||||
self.instance
|
||||
.device_create_bind_group_layout(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(GPUBindGroupLayout {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
})
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_pipeline_layout(
|
||||
&self,
|
||||
#[webidl] descriptor: super::pipeline_layout::GPUPipelineLayoutDescriptor,
|
||||
) -> GPUPipelineLayout {
|
||||
let bind_group_layouts = descriptor
|
||||
.bind_group_layouts
|
||||
.into_iter()
|
||||
.map(|bind_group_layout| bind_group_layout.id)
|
||||
.collect();
|
||||
|
||||
let wgpu_descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
bind_group_layouts: Cow::Owned(bind_group_layouts),
|
||||
push_constant_ranges: Default::default(),
|
||||
};
|
||||
|
||||
let (id, err) =
|
||||
self.instance
|
||||
.device_create_pipeline_layout(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPUPipelineLayout {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
}
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_bind_group(
|
||||
&self,
|
||||
#[webidl] descriptor: super::bind_group::GPUBindGroupDescriptor,
|
||||
) -> GPUBindGroup {
|
||||
let entries = descriptor
|
||||
.entries
|
||||
.into_iter()
|
||||
.map(|entry| wgpu_core::binding_model::BindGroupEntry {
|
||||
binding: entry.binding,
|
||||
resource: match entry.resource {
|
||||
GPUBindingResource::Sampler(sampler) => BindingResource::Sampler(sampler.id),
|
||||
GPUBindingResource::TextureView(texture_view) => {
|
||||
BindingResource::TextureView(texture_view.id)
|
||||
}
|
||||
GPUBindingResource::BufferBinding(buffer_binding) => {
|
||||
BindingResource::Buffer(wgpu_core::binding_model::BufferBinding {
|
||||
buffer: buffer_binding.buffer.id,
|
||||
offset: buffer_binding.offset,
|
||||
size: buffer_binding.size.and_then(NonZeroU64::new),
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let wgpu_descriptor = wgpu_core::binding_model::BindGroupDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
layout: descriptor.layout.id,
|
||||
entries: Cow::Owned(entries),
|
||||
};
|
||||
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.device_create_bind_group(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPUBindGroup {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
}
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_shader_module(
|
||||
&self,
|
||||
#[webidl] descriptor: super::shader::GPUShaderModuleDescriptor,
|
||||
) -> GPUShaderModule {
|
||||
let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
runtime_checks: wgpu_types::ShaderRuntimeChecks::default(),
|
||||
};
|
||||
|
||||
let (id, err) = self.instance.device_create_shader_module(
|
||||
self.id,
|
||||
&wgpu_descriptor,
|
||||
wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(descriptor.code)),
|
||||
None,
|
||||
);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPUShaderModule {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
}
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_compute_pipeline(
|
||||
&self,
|
||||
#[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
|
||||
) -> GPUComputePipeline {
|
||||
self.new_compute_pipeline(descriptor)
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_render_pipeline(
|
||||
&self,
|
||||
#[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
|
||||
) -> Result<GPURenderPipeline, JsErrorBox> {
|
||||
self.new_render_pipeline(descriptor)
|
||||
}
|
||||
|
||||
#[async_method]
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
async fn create_compute_pipeline_async(
|
||||
&self,
|
||||
#[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
|
||||
) -> GPUComputePipeline {
|
||||
self.new_compute_pipeline(descriptor)
|
||||
}
|
||||
|
||||
#[async_method]
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
async fn create_render_pipeline_async(
|
||||
&self,
|
||||
#[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
|
||||
) -> Result<GPURenderPipeline, JsErrorBox> {
|
||||
self.new_render_pipeline(descriptor)
|
||||
}
|
||||
|
||||
#[cppgc]
|
||||
fn create_command_encoder(
|
||||
&self,
|
||||
#[webidl] descriptor: Option<super::command_encoder::GPUCommandEncoderDescriptor>,
|
||||
) -> GPUCommandEncoder {
|
||||
let label = descriptor.map(|d| d.label).unwrap_or_default();
|
||||
let wgpu_descriptor = wgpu_types::CommandEncoderDescriptor {
|
||||
label: Some(Cow::Owned(label.clone())),
|
||||
};
|
||||
|
||||
let (id, err) =
|
||||
self.instance
|
||||
.device_create_command_encoder(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPUCommandEncoder {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
id,
|
||||
label,
|
||||
}
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_render_bundle_encoder(
|
||||
&self,
|
||||
#[webidl] descriptor: super::render_bundle::GPURenderBundleEncoderDescriptor,
|
||||
) -> GPURenderBundleEncoder {
|
||||
let wgpu_descriptor = wgpu_core::command::RenderBundleEncoderDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
color_formats: Cow::Owned(
|
||||
descriptor
|
||||
.color_formats
|
||||
.into_iter()
|
||||
.map(|format| format.into_option().map(Into::into))
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
depth_stencil: descriptor.depth_stencil_format.map(|format| {
|
||||
wgpu_types::RenderBundleDepthStencil {
|
||||
format: format.into(),
|
||||
depth_read_only: descriptor.depth_read_only,
|
||||
stencil_read_only: descriptor.stencil_read_only,
|
||||
}
|
||||
}),
|
||||
sample_count: descriptor.sample_count,
|
||||
multiview: None,
|
||||
};
|
||||
|
||||
let res = wgpu_core::command::RenderBundleEncoder::new(&wgpu_descriptor, self.id, None);
|
||||
let (encoder, err) = match res {
|
||||
Ok(encoder) => (encoder, None),
|
||||
Err(e) => (
|
||||
wgpu_core::command::RenderBundleEncoder::dummy(self.id),
|
||||
Some(e),
|
||||
),
|
||||
};
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPURenderBundleEncoder {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
encoder: RefCell::new(Some(encoder)),
|
||||
label: descriptor.label,
|
||||
}
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
#[cppgc]
|
||||
fn create_query_set(
|
||||
&self,
|
||||
#[webidl] descriptor: crate::query_set::GPUQuerySetDescriptor,
|
||||
) -> GPUQuerySet {
|
||||
let wgpu_descriptor = wgpu_core::resource::QuerySetDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
ty: descriptor.r#type.clone().into(),
|
||||
count: descriptor.count,
|
||||
};
|
||||
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.device_create_query_set(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPUQuerySet {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
r#type: descriptor.r#type,
|
||||
count: descriptor.count,
|
||||
label: descriptor.label,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(@crowlKats): support returning same promise
|
||||
#[async_method]
|
||||
#[getter]
|
||||
#[cppgc]
|
||||
async fn lost(&self) -> GPUDeviceLostInfo {
|
||||
if let Some(lost_receiver) = self.lost_receiver.lock().await.take() {
|
||||
let _ = lost_receiver.await;
|
||||
}
|
||||
|
||||
GPUDeviceLostInfo
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn push_error_scope(&self, #[webidl] filter: super::error::GPUErrorFilter) {
|
||||
self.error_handler
|
||||
.scopes
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push((filter, vec![]));
|
||||
}
|
||||
|
||||
#[async_method(fake)]
|
||||
#[global]
|
||||
fn pop_error_scope(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope,
|
||||
) -> Result<v8::Global<v8::Value>, JsErrorBox> {
|
||||
if self.error_handler.is_lost.get().is_some() {
|
||||
let val = v8::null(scope).cast::<v8::Value>();
|
||||
return Ok(v8::Global::new(scope, val));
|
||||
}
|
||||
|
||||
let Some((_, errors)) = self.error_handler.scopes.lock().unwrap().pop() else {
|
||||
return Err(JsErrorBox::new(
|
||||
"DOMExceptionOperationError",
|
||||
"There are no error scopes on the error scope stack",
|
||||
));
|
||||
};
|
||||
|
||||
let val = if let Some(err) = errors.into_iter().next() {
|
||||
deno_core::error::to_v8_error(scope, &err)
|
||||
} else {
|
||||
v8::null(scope).into()
|
||||
};
|
||||
|
||||
Ok(v8::Global::new(scope, val))
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn start_capture(&self) {
|
||||
self.instance.device_start_capture(self.id);
|
||||
}
|
||||
#[fast]
|
||||
fn stop_capture(&self) {
|
||||
self.instance
|
||||
.device_poll(self.id, wgpu_types::Maintain::wait())
|
||||
.unwrap();
|
||||
self.instance.device_stop_capture(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl GPUDevice {
|
||||
fn new_compute_pipeline(
|
||||
&self,
|
||||
descriptor: super::compute_pipeline::GPUComputePipelineDescriptor,
|
||||
) -> GPUComputePipeline {
|
||||
let wgpu_descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
layout: descriptor.layout.into(),
|
||||
stage: ProgrammableStageDescriptor {
|
||||
module: descriptor.compute.module.id,
|
||||
entry_point: descriptor.compute.entry_point.map(Into::into),
|
||||
constants: Cow::Owned(descriptor.compute.constants.into_iter().collect()),
|
||||
zero_initialize_workgroup_memory: true,
|
||||
},
|
||||
cache: None,
|
||||
};
|
||||
|
||||
let (id, err) =
|
||||
self.instance
|
||||
.device_create_compute_pipeline(self.id, &wgpu_descriptor, None, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPUComputePipeline {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
id,
|
||||
label: descriptor.label.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn new_render_pipeline(
|
||||
&self,
|
||||
descriptor: super::render_pipeline::GPURenderPipelineDescriptor,
|
||||
) -> Result<GPURenderPipeline, JsErrorBox> {
|
||||
let vertex = wgpu_core::pipeline::VertexState {
|
||||
stage: ProgrammableStageDescriptor {
|
||||
module: descriptor.vertex.module.id,
|
||||
entry_point: descriptor.vertex.entry_point.map(Into::into),
|
||||
constants: Cow::Owned(descriptor.vertex.constants.into_iter().collect()),
|
||||
zero_initialize_workgroup_memory: true,
|
||||
},
|
||||
buffers: Cow::Owned(
|
||||
descriptor
|
||||
.vertex
|
||||
.buffers
|
||||
.into_iter()
|
||||
.map(|b| {
|
||||
let layout = b.into_option().ok_or_else(|| {
|
||||
JsErrorBox::type_error(
|
||||
"Nullable GPUVertexBufferLayouts are currently not supported",
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(wgpu_core::pipeline::VertexBufferLayout {
|
||||
array_stride: layout.array_stride,
|
||||
step_mode: layout.step_mode.into(),
|
||||
attributes: Cow::Owned(
|
||||
layout
|
||||
.attributes
|
||||
.into_iter()
|
||||
.map(|attr| wgpu_types::VertexAttribute {
|
||||
format: attr.format.into(),
|
||||
offset: attr.offset,
|
||||
shader_location: attr.shader_location,
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, JsErrorBox>>()?,
|
||||
),
|
||||
};
|
||||
|
||||
let primitive = wgpu_types::PrimitiveState {
|
||||
topology: descriptor.primitive.topology.into(),
|
||||
strip_index_format: descriptor.primitive.strip_index_format.map(Into::into),
|
||||
front_face: descriptor.primitive.front_face.into(),
|
||||
cull_mode: descriptor.primitive.cull_mode.into(),
|
||||
unclipped_depth: descriptor.primitive.unclipped_depth,
|
||||
polygon_mode: Default::default(),
|
||||
conservative: false,
|
||||
};
|
||||
|
||||
let depth_stencil = descriptor.depth_stencil.map(|depth_stencil| {
|
||||
let front = wgpu_types::StencilFaceState {
|
||||
compare: depth_stencil.stencil_front.compare.into(),
|
||||
fail_op: depth_stencil.stencil_front.fail_op.into(),
|
||||
depth_fail_op: depth_stencil.stencil_front.depth_fail_op.into(),
|
||||
pass_op: depth_stencil.stencil_front.pass_op.into(),
|
||||
};
|
||||
let back = wgpu_types::StencilFaceState {
|
||||
compare: depth_stencil.stencil_back.compare.into(),
|
||||
fail_op: depth_stencil.stencil_back.fail_op.into(),
|
||||
depth_fail_op: depth_stencil.stencil_back.depth_fail_op.into(),
|
||||
pass_op: depth_stencil.stencil_back.pass_op.into(),
|
||||
};
|
||||
|
||||
wgpu_types::DepthStencilState {
|
||||
format: depth_stencil.format.into(),
|
||||
depth_write_enabled: depth_stencil.depth_write_enabled.unwrap_or_default(),
|
||||
depth_compare: depth_stencil
|
||||
.depth_compare
|
||||
.map(Into::into)
|
||||
.unwrap_or(wgpu_types::CompareFunction::Never), // TODO(wgpu): should be optional here
|
||||
stencil: wgpu_types::StencilState {
|
||||
front,
|
||||
back,
|
||||
read_mask: depth_stencil.stencil_read_mask,
|
||||
write_mask: depth_stencil.stencil_write_mask,
|
||||
},
|
||||
bias: wgpu_types::DepthBiasState {
|
||||
constant: depth_stencil.depth_bias,
|
||||
slope_scale: depth_stencil.depth_bias_slope_scale,
|
||||
clamp: depth_stencil.depth_bias_clamp,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let multisample = wgpu_types::MultisampleState {
|
||||
count: descriptor.multisample.count,
|
||||
mask: descriptor.multisample.mask as u64,
|
||||
alpha_to_coverage_enabled: descriptor.multisample.alpha_to_coverage_enabled,
|
||||
};
|
||||
|
||||
let fragment = descriptor
|
||||
.fragment
|
||||
.map(|fragment| {
|
||||
Ok::<_, JsErrorBox>(wgpu_core::pipeline::FragmentState {
|
||||
stage: ProgrammableStageDescriptor {
|
||||
module: fragment.module.id,
|
||||
entry_point: fragment.entry_point.map(Into::into),
|
||||
constants: Cow::Owned(fragment.constants.into_iter().collect()),
|
||||
zero_initialize_workgroup_memory: true,
|
||||
},
|
||||
targets: Cow::Owned(
|
||||
fragment
|
||||
.targets
|
||||
.into_iter()
|
||||
.map(|target| {
|
||||
target
|
||||
.into_option()
|
||||
.map(|target| {
|
||||
Ok(wgpu_types::ColorTargetState {
|
||||
format: target.format.into(),
|
||||
blend: target.blend.map(|blend| {
|
||||
wgpu_types::BlendState {
|
||||
color: wgpu_types::BlendComponent {
|
||||
src_factor: blend.color.src_factor.into(),
|
||||
dst_factor: blend.color.dst_factor.into(),
|
||||
operation: blend.color.operation.into(),
|
||||
},
|
||||
alpha: wgpu_types::BlendComponent {
|
||||
src_factor: blend.alpha.src_factor.into(),
|
||||
dst_factor: blend.alpha.dst_factor.into(),
|
||||
operation: blend.alpha.operation.into(),
|
||||
},
|
||||
}
|
||||
}),
|
||||
write_mask: wgpu_types::ColorWrites::from_bits(
|
||||
target.write_mask,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
JsErrorBox::type_error("usage is not valid")
|
||||
})?,
|
||||
})
|
||||
})
|
||||
.transpose()
|
||||
})
|
||||
.collect::<Result<_, JsErrorBox>>()?,
|
||||
),
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let wgpu_descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
layout: descriptor.layout.into(),
|
||||
vertex,
|
||||
primitive,
|
||||
depth_stencil,
|
||||
multisample,
|
||||
fragment,
|
||||
cache: None,
|
||||
multiview: None,
|
||||
};
|
||||
|
||||
let (id, err) =
|
||||
self.instance
|
||||
.device_create_render_pipeline(self.id, &wgpu_descriptor, None, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(GPURenderPipeline {
|
||||
instance: self.instance.clone(),
|
||||
error_handler: self.error_handler.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GPUDeviceLostInfo;
|
||||
|
||||
impl GarbageCollected for GPUDeviceLostInfo {}
|
||||
|
||||
#[op2]
|
||||
impl GPUDeviceLostInfo {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn reason(&self) -> &'static str {
|
||||
"unknown"
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[string]
|
||||
fn message(&self) -> &'static str {
|
||||
"device was lost"
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::fmt::Formatter;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use deno_core::ResourceId;
|
||||
use serde::Serialize;
|
||||
use std::convert::From;
|
||||
use std::error::Error;
|
||||
use std::fmt::Write;
|
||||
use wgpu_core::binding_model::CreateBindGroupError;
|
||||
use wgpu_core::binding_model::CreateBindGroupLayoutError;
|
||||
use wgpu_core::binding_model::CreatePipelineLayoutError;
|
||||
@ -31,270 +31,322 @@ use wgpu_core::resource::CreateSamplerError;
|
||||
use wgpu_core::resource::CreateTextureError;
|
||||
use wgpu_core::resource::CreateTextureViewError;
|
||||
|
||||
fn fmt_err(err: &(dyn Error + 'static)) -> String {
|
||||
let mut output = err.to_string();
|
||||
let mut level = 0;
|
||||
pub type ErrorHandler = std::sync::Arc<DeviceErrorHandler>;
|
||||
|
||||
fn print_tree(output: &mut String, level: &mut usize, e: &(dyn Error + 'static)) {
|
||||
let mut print = |e: &(dyn Error + 'static)| {
|
||||
writeln!(output, "{}{}", " ".repeat(*level * 2), e).unwrap();
|
||||
pub struct DeviceErrorHandler {
|
||||
pub is_lost: OnceLock<()>,
|
||||
lost_sender: Mutex<Option<tokio::sync::oneshot::Sender<()>>>,
|
||||
uncaptured_sender_is_closed: Mutex<Option<tokio::sync::oneshot::Sender<()>>>,
|
||||
|
||||
if let Some(e) = e.source() {
|
||||
*level += 1;
|
||||
print_tree(output, level, e);
|
||||
*level -= 1;
|
||||
}
|
||||
};
|
||||
if let Some(multi) = e.downcast_ref::<wgpu_core::error::MultiError>() {
|
||||
for e in multi.errors() {
|
||||
print(e);
|
||||
}
|
||||
} else {
|
||||
print(e);
|
||||
pub uncaptured_sender: tokio::sync::mpsc::UnboundedSender<GPUError>,
|
||||
|
||||
pub scopes: Mutex<Vec<(GPUErrorFilter, Vec<GPUError>)>>,
|
||||
}
|
||||
|
||||
impl Drop for DeviceErrorHandler {
|
||||
fn drop(&mut self) {
|
||||
if let Some(sender) = self.uncaptured_sender_is_closed.lock().unwrap().take() {
|
||||
let _ = sender.send(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceErrorHandler {
|
||||
pub fn new(
|
||||
lost_sender: tokio::sync::oneshot::Sender<()>,
|
||||
uncaptured_sender: tokio::sync::mpsc::UnboundedSender<GPUError>,
|
||||
uncaptured_sender_is_closed: tokio::sync::oneshot::Sender<()>,
|
||||
) -> Self {
|
||||
Self {
|
||||
is_lost: Default::default(),
|
||||
lost_sender: Mutex::new(Some(lost_sender)),
|
||||
uncaptured_sender,
|
||||
uncaptured_sender_is_closed: Mutex::new(Some(uncaptured_sender_is_closed)),
|
||||
scopes: Mutex::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
print_tree(&mut output, &mut level, err);
|
||||
pub fn push_error<E: Into<GPUError>>(&self, err: Option<E>) {
|
||||
let Some(err) = err else {
|
||||
return;
|
||||
};
|
||||
|
||||
if self.is_lost.get().is_some() {
|
||||
return;
|
||||
}
|
||||
|
||||
let err = err.into();
|
||||
|
||||
if matches!(err, GPUError::Lost) {
|
||||
let _ = self.is_lost.set(());
|
||||
|
||||
if let Some(sender) = self.lost_sender.lock().unwrap().take() {
|
||||
let _ = sender.send(());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let error_filter = match err {
|
||||
GPUError::Lost => unreachable!(),
|
||||
GPUError::Validation(_) => GPUErrorFilter::Validation,
|
||||
GPUError::OutOfMemory => GPUErrorFilter::OutOfMemory,
|
||||
GPUError::Internal => GPUErrorFilter::Internal,
|
||||
};
|
||||
|
||||
let mut scopes = self.scopes.lock().unwrap();
|
||||
let scope = scopes
|
||||
.iter_mut()
|
||||
.rfind(|(filter, _)| filter == &error_filter);
|
||||
|
||||
if let Some(scope) = scope {
|
||||
scope.1.push(err);
|
||||
} else {
|
||||
self.uncaptured_sender.send(err).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(deno_core::WebIDL, Eq, PartialEq)]
|
||||
#[webidl(enum)]
|
||||
pub enum GPUErrorFilter {
|
||||
Validation,
|
||||
OutOfMemory,
|
||||
Internal,
|
||||
}
|
||||
|
||||
#[derive(Debug, deno_error::JsError)]
|
||||
pub enum GPUError {
|
||||
// TODO(@crowlKats): consider adding an unreachable value that uses unreachable!()
|
||||
#[class("UNREACHABLE")]
|
||||
Lost,
|
||||
#[class("GPUValidationError")]
|
||||
Validation(String),
|
||||
#[class("GPUOutOfMemoryError")]
|
||||
OutOfMemory,
|
||||
#[allow(dead_code)]
|
||||
#[class("GPUInternalError")]
|
||||
Internal,
|
||||
}
|
||||
|
||||
impl Display for GPUError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
GPUError::Lost => Ok(()),
|
||||
GPUError::Validation(s) => f.write_str(s),
|
||||
GPUError::OutOfMemory => f.write_str("not enough memory left"),
|
||||
GPUError::Internal => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for GPUError {}
|
||||
|
||||
fn fmt_err(err: &(dyn std::error::Error + 'static)) -> String {
|
||||
let mut output = err.to_string();
|
||||
|
||||
let mut e = err.source();
|
||||
while let Some(source) = e {
|
||||
output.push_str(&format!(": {source}"));
|
||||
e = source.source();
|
||||
}
|
||||
|
||||
if output.is_empty() {
|
||||
output.push_str("validation error");
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct WebGpuResult {
|
||||
pub rid: Option<ResourceId>,
|
||||
pub err: Option<WebGpuError>,
|
||||
}
|
||||
|
||||
impl WebGpuResult {
|
||||
pub fn rid(rid: ResourceId) -> Self {
|
||||
Self {
|
||||
rid: Some(rid),
|
||||
err: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rid_err<T: Into<WebGpuError>>(rid: ResourceId, err: Option<T>) -> Self {
|
||||
Self {
|
||||
rid: Some(rid),
|
||||
err: err.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn maybe_err<T: Into<WebGpuError>>(err: Option<T>) -> Self {
|
||||
Self {
|
||||
rid: None,
|
||||
err: err.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
rid: None,
|
||||
err: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(tag = "type", content = "value")]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum WebGpuError {
|
||||
Lost,
|
||||
OutOfMemory,
|
||||
Validation(String),
|
||||
Internal,
|
||||
}
|
||||
|
||||
impl From<CreateBufferError> for WebGpuError {
|
||||
impl From<CreateBufferError> for GPUError {
|
||||
fn from(err: CreateBufferError) -> Self {
|
||||
match err {
|
||||
CreateBufferError::Device(err) => err.into(),
|
||||
CreateBufferError::AccessError(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeviceError> for WebGpuError {
|
||||
impl From<DeviceError> for GPUError {
|
||||
fn from(err: DeviceError) -> Self {
|
||||
match err {
|
||||
DeviceError::Lost => WebGpuError::Lost,
|
||||
DeviceError::OutOfMemory => WebGpuError::OutOfMemory,
|
||||
_ => WebGpuError::Validation(fmt_err(&err)),
|
||||
DeviceError::Lost => GPUError::Lost,
|
||||
DeviceError::OutOfMemory => GPUError::OutOfMemory,
|
||||
_ => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BufferAccessError> for WebGpuError {
|
||||
impl From<BufferAccessError> for GPUError {
|
||||
fn from(err: BufferAccessError) -> Self {
|
||||
match err {
|
||||
BufferAccessError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateBindGroupLayoutError> for WebGpuError {
|
||||
impl From<CreateBindGroupLayoutError> for GPUError {
|
||||
fn from(err: CreateBindGroupLayoutError) -> Self {
|
||||
match err {
|
||||
CreateBindGroupLayoutError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreatePipelineLayoutError> for WebGpuError {
|
||||
impl From<CreatePipelineLayoutError> for GPUError {
|
||||
fn from(err: CreatePipelineLayoutError) -> Self {
|
||||
match err {
|
||||
CreatePipelineLayoutError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateBindGroupError> for WebGpuError {
|
||||
impl From<CreateBindGroupError> for GPUError {
|
||||
fn from(err: CreateBindGroupError) -> Self {
|
||||
match err {
|
||||
CreateBindGroupError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RenderBundleError> for WebGpuError {
|
||||
impl From<RenderBundleError> for GPUError {
|
||||
fn from(err: RenderBundleError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateRenderBundleError> for WebGpuError {
|
||||
impl From<CreateRenderBundleError> for GPUError {
|
||||
fn from(err: CreateRenderBundleError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CopyError> for WebGpuError {
|
||||
impl From<CopyError> for GPUError {
|
||||
fn from(err: CopyError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandEncoderError> for WebGpuError {
|
||||
impl From<CommandEncoderError> for GPUError {
|
||||
fn from(err: CommandEncoderError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueryError> for WebGpuError {
|
||||
impl From<QueryError> for GPUError {
|
||||
fn from(err: QueryError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ComputePassError> for WebGpuError {
|
||||
impl From<ComputePassError> for GPUError {
|
||||
fn from(err: ComputePassError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateComputePipelineError> for WebGpuError {
|
||||
impl From<CreateComputePipelineError> for GPUError {
|
||||
fn from(err: CreateComputePipelineError) -> Self {
|
||||
match err {
|
||||
CreateComputePipelineError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GetBindGroupLayoutError> for WebGpuError {
|
||||
impl From<GetBindGroupLayoutError> for GPUError {
|
||||
fn from(err: GetBindGroupLayoutError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateRenderPipelineError> for WebGpuError {
|
||||
impl From<CreateRenderPipelineError> for GPUError {
|
||||
fn from(err: CreateRenderPipelineError) -> Self {
|
||||
match err {
|
||||
CreateRenderPipelineError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RenderPassError> for WebGpuError {
|
||||
impl From<RenderPassError> for GPUError {
|
||||
fn from(err: RenderPassError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateSamplerError> for WebGpuError {
|
||||
impl From<CreateSamplerError> for GPUError {
|
||||
fn from(err: CreateSamplerError) -> Self {
|
||||
match err {
|
||||
CreateSamplerError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateShaderModuleError> for WebGpuError {
|
||||
impl From<CreateShaderModuleError> for GPUError {
|
||||
fn from(err: CreateShaderModuleError) -> Self {
|
||||
match err {
|
||||
CreateShaderModuleError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateTextureError> for WebGpuError {
|
||||
impl From<CreateTextureError> for GPUError {
|
||||
fn from(err: CreateTextureError) -> Self {
|
||||
match err {
|
||||
CreateTextureError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateTextureViewError> for WebGpuError {
|
||||
impl From<CreateTextureViewError> for GPUError {
|
||||
fn from(err: CreateTextureViewError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CreateQuerySetError> for WebGpuError {
|
||||
impl From<CreateQuerySetError> for GPUError {
|
||||
fn from(err: CreateQuerySetError) -> Self {
|
||||
match err {
|
||||
CreateQuerySetError::Device(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueueSubmitError> for WebGpuError {
|
||||
impl From<QueueSubmitError> for GPUError {
|
||||
fn from(err: QueueSubmitError) -> Self {
|
||||
match err {
|
||||
QueueSubmitError::Queue(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<QueueWriteError> for WebGpuError {
|
||||
impl From<QueueWriteError> for GPUError {
|
||||
fn from(err: QueueWriteError) -> Self {
|
||||
match err {
|
||||
QueueWriteError::Queue(err) => err.into(),
|
||||
err => WebGpuError::Validation(fmt_err(&err)),
|
||||
err => GPUError::Validation(fmt_err(&err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ClearError> for WebGpuError {
|
||||
impl From<ClearError> for GPUError {
|
||||
fn from(err: ClearError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigureSurfaceError> for WebGpuError {
|
||||
impl From<ConfigureSurfaceError> for GPUError {
|
||||
fn from(err: ConfigureSurfaceError) -> Self {
|
||||
WebGpuError::Validation(fmt_err(&err))
|
||||
GPUError::Validation(fmt_err(&err))
|
||||
}
|
||||
}
|
||||
|
@ -1,772 +1,204 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
#![cfg(not(target_arch = "wasm32"))]
|
||||
#![warn(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use hashbrown::HashSet;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use error::WebGpuResult;
|
||||
use deno_core::cppgc::SameObject;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::OpState;
|
||||
pub use wgpu_core;
|
||||
pub use wgpu_types;
|
||||
use wgpu_types::PowerPreference;
|
||||
|
||||
mod adapter;
|
||||
mod bind_group;
|
||||
mod bind_group_layout;
|
||||
mod buffer;
|
||||
mod byow;
|
||||
mod command_buffer;
|
||||
mod command_encoder;
|
||||
mod compute_pass;
|
||||
mod compute_pipeline;
|
||||
mod device;
|
||||
mod error;
|
||||
mod pipeline_layout;
|
||||
mod query_set;
|
||||
mod queue;
|
||||
mod render_bundle;
|
||||
mod render_pass;
|
||||
mod render_pipeline;
|
||||
mod sampler;
|
||||
mod shader;
|
||||
mod surface;
|
||||
mod texture;
|
||||
mod webidl;
|
||||
|
||||
pub const UNSTABLE_FEATURE_NAME: &str = "webgpu";
|
||||
|
||||
#[macro_use]
|
||||
mod macros {
|
||||
macro_rules! gfx_put {
|
||||
($global:ident.$method:ident( $($param:expr),* ) => $state:expr, $rc:expr) => {{
|
||||
let (val, maybe_err) = $global.$method($($param),*);
|
||||
let rid = $state.resource_table.add($rc($global.clone(), val));
|
||||
Ok(WebGpuResult::rid_err(rid, maybe_err))
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! gfx_ok {
|
||||
($global:ident.$method:ident( $($param:expr),* )) => {{
|
||||
let maybe_err = $global.$method($($param),*).err();
|
||||
Ok(WebGpuResult::maybe_err(maybe_err))
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
pub mod binding;
|
||||
pub mod buffer;
|
||||
pub mod bundle;
|
||||
pub mod byow;
|
||||
pub mod command_encoder;
|
||||
pub mod compute_pass;
|
||||
pub mod error;
|
||||
pub mod pipeline;
|
||||
pub mod queue;
|
||||
pub mod render_pass;
|
||||
pub mod sampler;
|
||||
pub mod shader;
|
||||
pub mod surface;
|
||||
pub mod texture;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum InitError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error(transparent)]
|
||||
RequestDevice(wgpu_core::instance::RequestDeviceError),
|
||||
}
|
||||
|
||||
pub type Instance = std::sync::Arc<wgpu_core::global::Global>;
|
||||
|
||||
struct WebGpuAdapter(Instance, wgpu_core::id::AdapterId);
|
||||
impl Resource for WebGpuAdapter {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUAdapter".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.adapter_drop(self.1);
|
||||
#[allow(clippy::print_stdout)]
|
||||
pub fn print_linker_flags(name: &str) {
|
||||
if cfg!(windows) {
|
||||
// these dls load slowly, so delay loading them
|
||||
let dlls = [
|
||||
// webgpu
|
||||
"d3dcompiler_47",
|
||||
"OPENGL32",
|
||||
// network related functions
|
||||
"iphlpapi",
|
||||
];
|
||||
for dll in dlls {
|
||||
println!("cargo:rustc-link-arg-bin={name}=/delayload:{dll}.dll");
|
||||
}
|
||||
// enable delay loading
|
||||
println!("cargo:rustc-link-arg-bin={name}=delayimp.lib");
|
||||
}
|
||||
}
|
||||
|
||||
struct WebGpuDevice(Instance, wgpu_core::id::DeviceId);
|
||||
impl Resource for WebGpuDevice {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUDevice".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.device_drop(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
struct WebGpuQuerySet(Instance, wgpu_core::id::QuerySetId);
|
||||
impl Resource for WebGpuQuerySet {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUQuerySet".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.query_set_drop(self.1);
|
||||
}
|
||||
}
|
||||
pub type Instance = Arc<wgpu_core::global::Global>;
|
||||
|
||||
deno_core::extension!(
|
||||
deno_webgpu,
|
||||
deps = [deno_webidl, deno_web],
|
||||
ops = [
|
||||
// Request device/adapter
|
||||
op_webgpu_request_adapter,
|
||||
op_webgpu_request_device,
|
||||
op_webgpu_request_adapter_info,
|
||||
// Query Set
|
||||
op_webgpu_create_query_set,
|
||||
// buffer
|
||||
buffer::op_webgpu_create_buffer,
|
||||
buffer::op_webgpu_buffer_get_mapped_range,
|
||||
buffer::op_webgpu_buffer_unmap,
|
||||
// buffer async
|
||||
buffer::op_webgpu_buffer_get_map_async,
|
||||
// remaining sync ops
|
||||
|
||||
// texture
|
||||
texture::op_webgpu_create_texture,
|
||||
texture::op_webgpu_create_texture_view,
|
||||
// sampler
|
||||
sampler::op_webgpu_create_sampler,
|
||||
// binding
|
||||
binding::op_webgpu_create_bind_group_layout,
|
||||
binding::op_webgpu_create_pipeline_layout,
|
||||
binding::op_webgpu_create_bind_group,
|
||||
// pipeline
|
||||
pipeline::op_webgpu_create_compute_pipeline,
|
||||
pipeline::op_webgpu_compute_pipeline_get_bind_group_layout,
|
||||
pipeline::op_webgpu_create_render_pipeline,
|
||||
pipeline::op_webgpu_render_pipeline_get_bind_group_layout,
|
||||
// command_encoder
|
||||
command_encoder::op_webgpu_create_command_encoder,
|
||||
command_encoder::op_webgpu_command_encoder_begin_render_pass,
|
||||
command_encoder::op_webgpu_command_encoder_begin_compute_pass,
|
||||
command_encoder::op_webgpu_command_encoder_copy_buffer_to_buffer,
|
||||
command_encoder::op_webgpu_command_encoder_copy_buffer_to_texture,
|
||||
command_encoder::op_webgpu_command_encoder_copy_texture_to_buffer,
|
||||
command_encoder::op_webgpu_command_encoder_copy_texture_to_texture,
|
||||
command_encoder::op_webgpu_command_encoder_clear_buffer,
|
||||
command_encoder::op_webgpu_command_encoder_push_debug_group,
|
||||
command_encoder::op_webgpu_command_encoder_pop_debug_group,
|
||||
command_encoder::op_webgpu_command_encoder_insert_debug_marker,
|
||||
command_encoder::op_webgpu_command_encoder_write_timestamp,
|
||||
command_encoder::op_webgpu_command_encoder_resolve_query_set,
|
||||
command_encoder::op_webgpu_command_encoder_finish,
|
||||
render_pass::op_webgpu_render_pass_set_viewport,
|
||||
render_pass::op_webgpu_render_pass_set_scissor_rect,
|
||||
render_pass::op_webgpu_render_pass_set_blend_constant,
|
||||
render_pass::op_webgpu_render_pass_set_stencil_reference,
|
||||
render_pass::op_webgpu_render_pass_begin_occlusion_query,
|
||||
render_pass::op_webgpu_render_pass_end_occlusion_query,
|
||||
render_pass::op_webgpu_render_pass_execute_bundles,
|
||||
render_pass::op_webgpu_render_pass_end,
|
||||
render_pass::op_webgpu_render_pass_set_bind_group,
|
||||
render_pass::op_webgpu_render_pass_push_debug_group,
|
||||
render_pass::op_webgpu_render_pass_pop_debug_group,
|
||||
render_pass::op_webgpu_render_pass_insert_debug_marker,
|
||||
render_pass::op_webgpu_render_pass_set_pipeline,
|
||||
render_pass::op_webgpu_render_pass_set_index_buffer,
|
||||
render_pass::op_webgpu_render_pass_set_vertex_buffer,
|
||||
render_pass::op_webgpu_render_pass_draw,
|
||||
render_pass::op_webgpu_render_pass_draw_indexed,
|
||||
render_pass::op_webgpu_render_pass_draw_indirect,
|
||||
render_pass::op_webgpu_render_pass_draw_indexed_indirect,
|
||||
compute_pass::op_webgpu_compute_pass_set_pipeline,
|
||||
compute_pass::op_webgpu_compute_pass_dispatch_workgroups,
|
||||
compute_pass::op_webgpu_compute_pass_dispatch_workgroups_indirect,
|
||||
compute_pass::op_webgpu_compute_pass_end,
|
||||
compute_pass::op_webgpu_compute_pass_set_bind_group,
|
||||
compute_pass::op_webgpu_compute_pass_push_debug_group,
|
||||
compute_pass::op_webgpu_compute_pass_pop_debug_group,
|
||||
compute_pass::op_webgpu_compute_pass_insert_debug_marker,
|
||||
// bundle
|
||||
bundle::op_webgpu_create_render_bundle_encoder,
|
||||
bundle::op_webgpu_render_bundle_encoder_finish,
|
||||
bundle::op_webgpu_render_bundle_encoder_set_bind_group,
|
||||
bundle::op_webgpu_render_bundle_encoder_push_debug_group,
|
||||
bundle::op_webgpu_render_bundle_encoder_pop_debug_group,
|
||||
bundle::op_webgpu_render_bundle_encoder_insert_debug_marker,
|
||||
bundle::op_webgpu_render_bundle_encoder_set_pipeline,
|
||||
bundle::op_webgpu_render_bundle_encoder_set_index_buffer,
|
||||
bundle::op_webgpu_render_bundle_encoder_set_vertex_buffer,
|
||||
bundle::op_webgpu_render_bundle_encoder_draw,
|
||||
bundle::op_webgpu_render_bundle_encoder_draw_indexed,
|
||||
bundle::op_webgpu_render_bundle_encoder_draw_indirect,
|
||||
// queue
|
||||
queue::op_webgpu_queue_submit,
|
||||
queue::op_webgpu_write_buffer,
|
||||
queue::op_webgpu_write_texture,
|
||||
// shader
|
||||
shader::op_webgpu_create_shader_module,
|
||||
// surface
|
||||
surface::op_webgpu_surface_configure,
|
||||
surface::op_webgpu_surface_get_current_texture,
|
||||
surface::op_webgpu_surface_present,
|
||||
// byow
|
||||
byow::op_webgpu_surface_create,
|
||||
ops = [op_create_gpu],
|
||||
objects = [
|
||||
GPU,
|
||||
adapter::GPUAdapter,
|
||||
adapter::GPUAdapterInfo,
|
||||
bind_group::GPUBindGroup,
|
||||
bind_group_layout::GPUBindGroupLayout,
|
||||
buffer::GPUBuffer,
|
||||
command_buffer::GPUCommandBuffer,
|
||||
command_encoder::GPUCommandEncoder,
|
||||
compute_pass::GPUComputePassEncoder,
|
||||
compute_pipeline::GPUComputePipeline,
|
||||
device::GPUDevice,
|
||||
device::GPUDeviceLostInfo,
|
||||
pipeline_layout::GPUPipelineLayout,
|
||||
query_set::GPUQuerySet,
|
||||
queue::GPUQueue,
|
||||
render_bundle::GPURenderBundle,
|
||||
render_bundle::GPURenderBundleEncoder,
|
||||
render_pass::GPURenderPassEncoder,
|
||||
render_pipeline::GPURenderPipeline,
|
||||
sampler::GPUSampler,
|
||||
shader::GPUShaderModule,
|
||||
adapter::GPUSupportedFeatures,
|
||||
adapter::GPUSupportedLimits,
|
||||
texture::GPUTexture,
|
||||
texture::GPUTextureView,
|
||||
byow::UnsafeWindowSurface,
|
||||
surface::GPUCanvasContext,
|
||||
],
|
||||
esm = ["00_init.js", "02_surface.js"],
|
||||
lazy_loaded_esm = ["01_webgpu.js"],
|
||||
);
|
||||
|
||||
fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> {
|
||||
let mut return_features: Vec<&'static str> = vec![];
|
||||
|
||||
// api
|
||||
if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) {
|
||||
return_features.push("depth-clip-control");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
|
||||
return_features.push("timestamp-query");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) {
|
||||
return_features.push("indirect-first-instance");
|
||||
}
|
||||
// shader
|
||||
if features.contains(wgpu_types::Features::SHADER_F16) {
|
||||
return_features.push("shader-f16");
|
||||
}
|
||||
// texture formats
|
||||
if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) {
|
||||
return_features.push("depth32float-stencil8");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
|
||||
return_features.push("texture-compression-bc");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC_SLICED_3D) {
|
||||
return_features.push("texture-compression-bc-sliced-3d");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
|
||||
return_features.push("texture-compression-etc2");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC) {
|
||||
return_features.push("texture-compression-astc");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) {
|
||||
return_features.push("rg11b10ufloat-renderable");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) {
|
||||
return_features.push("bgra8unorm-storage");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) {
|
||||
return_features.push("float32-filterable");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::DUAL_SOURCE_BLENDING) {
|
||||
return_features.push("dual-source-blending");
|
||||
}
|
||||
|
||||
// extended from spec
|
||||
|
||||
// texture formats
|
||||
if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) {
|
||||
return_features.push("texture-format-16-bit-norm");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
|
||||
return_features.push("texture-compression-astc-hdr");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) {
|
||||
return_features.push("texture-adapter-specific-format-features");
|
||||
}
|
||||
// api
|
||||
if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
|
||||
return_features.push("pipeline-statistics-query");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) {
|
||||
return_features.push("timestamp-query-inside-passes");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
|
||||
return_features.push("mappable-primary-buffers");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) {
|
||||
return_features.push("texture-binding-array");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) {
|
||||
return_features.push("buffer-binding-array");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
|
||||
return_features.push("storage-resource-binding-array");
|
||||
}
|
||||
if features.contains(
|
||||
wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||
) {
|
||||
return_features.push("sampled-texture-and-storage-buffer-array-non-uniform-indexing");
|
||||
}
|
||||
if features.contains(
|
||||
wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
) {
|
||||
return_features.push("uniform-buffer-and-storage-texture-array-non-uniform-indexing");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
|
||||
return_features.push("partially-bound-binding-array");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
|
||||
return_features.push("multi-draw-indirect");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
|
||||
return_features.push("multi-draw-indirect-count");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::PUSH_CONSTANTS) {
|
||||
return_features.push("push-constants");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) {
|
||||
return_features.push("address-mode-clamp-to-zero");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
|
||||
return_features.push("address-mode-clamp-to-border");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::POLYGON_MODE_LINE) {
|
||||
return_features.push("polygon-mode-line");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) {
|
||||
return_features.push("polygon-mode-point");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) {
|
||||
return_features.push("conservative-rasterization");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) {
|
||||
return_features.push("vertex-writable-storage");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::CLEAR_TEXTURE) {
|
||||
return_features.push("clear-texture");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) {
|
||||
return_features.push("spirv-shader-passthrough");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTIVIEW) {
|
||||
return_features.push("multiview");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
|
||||
return_features.push("vertex-attribute-64-bit");
|
||||
}
|
||||
// shader
|
||||
if features.contains(wgpu_types::Features::SHADER_F64) {
|
||||
return_features.push("shader-f64");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SHADER_I16) {
|
||||
return_features.push("shader-i16");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) {
|
||||
return_features.push("shader-primitive-index");
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) {
|
||||
return_features.push("shader-early-depth-test");
|
||||
}
|
||||
|
||||
return_features
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum GpuAdapterResOrErr {
|
||||
Error { err: String },
|
||||
Features(GpuAdapterRes),
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuAdapterRes {
|
||||
rid: ResourceId,
|
||||
limits: wgpu_types::Limits,
|
||||
features: Vec<&'static str>,
|
||||
is_fallback: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuDeviceRes {
|
||||
rid: ResourceId,
|
||||
queue_rid: ResourceId,
|
||||
limits: wgpu_types::Limits,
|
||||
features: Vec<&'static str>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_request_adapter(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[serde] power_preference: Option<wgpu_types::PowerPreference>,
|
||||
force_fallback_adapter: bool,
|
||||
) -> GpuAdapterResOrErr {
|
||||
let mut state = state.borrow_mut();
|
||||
|
||||
let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else(
|
||||
|_| wgpu_types::Backends::all(),
|
||||
|s| wgpu_types::Backends::from_comma_list(&s),
|
||||
);
|
||||
let instance = if let Some(instance) = state.try_borrow::<Instance>() {
|
||||
instance
|
||||
} else {
|
||||
state.put(std::sync::Arc::new(wgpu_core::global::Global::new(
|
||||
"webgpu",
|
||||
&wgpu_types::InstanceDescriptor {
|
||||
backends,
|
||||
flags: wgpu_types::InstanceFlags::from_build_config(),
|
||||
backend_options: wgpu_types::BackendOptions {
|
||||
dx12: wgpu_types::Dx12BackendOptions {
|
||||
shader_compiler: wgpu_types::Dx12Compiler::Fxc,
|
||||
},
|
||||
gl: wgpu_types::GlBackendOptions::default(),
|
||||
noop: wgpu_types::NoopBackendOptions::default(),
|
||||
},
|
||||
},
|
||||
)));
|
||||
state.borrow::<Instance>()
|
||||
};
|
||||
|
||||
let descriptor = wgpu_core::instance::RequestAdapterOptions {
|
||||
power_preference: power_preference.unwrap_or_default(),
|
||||
force_fallback_adapter,
|
||||
compatible_surface: None, // windowless
|
||||
};
|
||||
let res = instance.request_adapter(&descriptor, backends, None);
|
||||
|
||||
let adapter = match res {
|
||||
Ok(adapter) => adapter,
|
||||
Err(err) => {
|
||||
return GpuAdapterResOrErr::Error {
|
||||
err: err.to_string(),
|
||||
}
|
||||
}
|
||||
};
|
||||
let adapter_features = instance.adapter_features(adapter);
|
||||
let features = deserialize_features(&adapter_features);
|
||||
let adapter_limits = instance.adapter_limits(adapter);
|
||||
|
||||
let instance = instance.clone();
|
||||
|
||||
let rid = state.resource_table.add(WebGpuAdapter(instance, adapter));
|
||||
|
||||
GpuAdapterResOrErr::Features(GpuAdapterRes {
|
||||
rid,
|
||||
features,
|
||||
limits: adapter_limits,
|
||||
// TODO(lucacasonato): report correctly from wgpu
|
||||
is_fallback: false,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct GpuRequiredFeatures(HashSet<String>);
|
||||
|
||||
impl From<GpuRequiredFeatures> for wgpu_types::Features {
|
||||
fn from(required_features: GpuRequiredFeatures) -> wgpu_types::Features {
|
||||
let mut features: wgpu_types::Features = wgpu_types::Features::empty();
|
||||
// api
|
||||
features.set(
|
||||
wgpu_types::Features::DEPTH_CLIP_CONTROL,
|
||||
required_features.0.contains("depth-clip-control"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TIMESTAMP_QUERY,
|
||||
required_features.0.contains("timestamp-query"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::INDIRECT_FIRST_INSTANCE,
|
||||
required_features.0.contains("indirect-first-instance"),
|
||||
);
|
||||
// shader
|
||||
features.set(
|
||||
wgpu_types::Features::SHADER_F16,
|
||||
required_features.0.contains("shader-f16"),
|
||||
);
|
||||
// texture formats
|
||||
features.set(
|
||||
wgpu_types::Features::DEPTH32FLOAT_STENCIL8,
|
||||
required_features.0.contains("depth32float-stencil8"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_COMPRESSION_BC,
|
||||
required_features.0.contains("texture-compression-bc"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
|
||||
required_features
|
||||
.0
|
||||
.contains("texture-compression-bc-sliced-3d"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_COMPRESSION_ETC2,
|
||||
required_features.0.contains("texture-compression-etc2"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_COMPRESSION_ASTC,
|
||||
required_features.0.contains("texture-compression-astc"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::RG11B10UFLOAT_RENDERABLE,
|
||||
required_features.0.contains("rg11b10ufloat-renderable"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::BGRA8UNORM_STORAGE,
|
||||
required_features.0.contains("bgra8unorm-storage"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::FLOAT32_FILTERABLE,
|
||||
required_features.0.contains("float32-filterable"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::DUAL_SOURCE_BLENDING,
|
||||
required_features.0.contains("dual-source-blending"),
|
||||
);
|
||||
|
||||
// extended from spec
|
||||
|
||||
// texture formats
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM,
|
||||
required_features.0.contains("texture-format-16-bit-norm"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR,
|
||||
required_features.0.contains("texture-compression-astc-hdr"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||
required_features
|
||||
.0
|
||||
.contains("texture-adapter-specific-format-features"),
|
||||
);
|
||||
// api
|
||||
features.set(
|
||||
wgpu_types::Features::PIPELINE_STATISTICS_QUERY,
|
||||
required_features.0.contains("pipeline-statistics-query"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES,
|
||||
required_features
|
||||
.0
|
||||
.contains("timestamp-query-inside-passes"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS,
|
||||
required_features.0.contains("mappable-primary-buffers"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::TEXTURE_BINDING_ARRAY,
|
||||
required_features.0.contains("texture-binding-array"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::BUFFER_BINDING_ARRAY,
|
||||
required_features.0.contains("buffer-binding-array"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY,
|
||||
required_features
|
||||
.0
|
||||
.contains("storage-resource-binding-array"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||
required_features
|
||||
.0
|
||||
.contains("sampled-texture-and-storage-buffer-array-non-uniform-indexing"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
required_features
|
||||
.0
|
||||
.contains("uniform-buffer-and-storage-texture-array-non-uniform-indexing"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY,
|
||||
required_features
|
||||
.0
|
||||
.contains("partially-bound-binding-array"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::MULTI_DRAW_INDIRECT,
|
||||
required_features.0.contains("multi-draw-indirect"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT,
|
||||
required_features.0.contains("multi-draw-indirect-count"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::PUSH_CONSTANTS,
|
||||
required_features.0.contains("push-constants"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO,
|
||||
required_features.0.contains("address-mode-clamp-to-zero"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
required_features.0.contains("address-mode-clamp-to-border"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::POLYGON_MODE_LINE,
|
||||
required_features.0.contains("polygon-mode-line"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::POLYGON_MODE_POINT,
|
||||
required_features.0.contains("polygon-mode-point"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::CONSERVATIVE_RASTERIZATION,
|
||||
required_features.0.contains("conservative-rasterization"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::VERTEX_WRITABLE_STORAGE,
|
||||
required_features.0.contains("vertex-writable-storage"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::CLEAR_TEXTURE,
|
||||
required_features.0.contains("clear-texture"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH,
|
||||
required_features.0.contains("spirv-shader-passthrough"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::MULTIVIEW,
|
||||
required_features.0.contains("multiview"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT,
|
||||
required_features.0.contains("vertex-attribute-64-bit"),
|
||||
);
|
||||
// shader
|
||||
features.set(
|
||||
wgpu_types::Features::SHADER_F64,
|
||||
required_features.0.contains("shader-f64"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::SHADER_I16,
|
||||
required_features.0.contains("shader-i16"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::SHADER_PRIMITIVE_INDEX,
|
||||
required_features.0.contains("shader-primitive-index"),
|
||||
);
|
||||
features.set(
|
||||
wgpu_types::Features::SHADER_EARLY_DEPTH_TEST,
|
||||
required_features.0.contains("shader-early-depth-test"),
|
||||
);
|
||||
|
||||
features
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_request_device(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] adapter_rid: ResourceId,
|
||||
#[string] label: String,
|
||||
#[serde] required_features: GpuRequiredFeatures,
|
||||
#[serde] required_limits: Option<wgpu_types::Limits>,
|
||||
) -> Result<GpuDeviceRes, InitError> {
|
||||
let mut state = state.borrow_mut();
|
||||
let adapter_resource = state
|
||||
.resource_table
|
||||
.take::<WebGpuAdapter>(adapter_rid)
|
||||
.map_err(InitError::Resource)?;
|
||||
let adapter = adapter_resource.1;
|
||||
let instance = state.borrow::<Instance>();
|
||||
|
||||
let descriptor = wgpu_types::DeviceDescriptor {
|
||||
label: Some(Cow::Owned(label)),
|
||||
required_features: required_features.into(),
|
||||
required_limits: required_limits.unwrap_or_default(),
|
||||
memory_hints: wgpu_types::MemoryHints::default(),
|
||||
};
|
||||
|
||||
let webgpu_trace = std::env::var("DENO_WEBGPU_TRACE").unwrap();
|
||||
|
||||
let res = instance.adapter_request_device(
|
||||
adapter,
|
||||
&descriptor,
|
||||
Some(webgpu_trace.as_str()),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
adapter_resource.close();
|
||||
|
||||
let (device, queue) = res.map_err(InitError::RequestDevice)?;
|
||||
|
||||
let device_features = instance.device_features(device);
|
||||
let features = deserialize_features(&device_features);
|
||||
let limits = instance.device_limits(device);
|
||||
|
||||
let instance = instance.clone();
|
||||
let instance2 = instance.clone();
|
||||
let rid = state.resource_table.add(WebGpuDevice(instance, device));
|
||||
let queue_rid = state
|
||||
.resource_table
|
||||
.add(queue::WebGpuQueue(instance2, queue));
|
||||
|
||||
Ok(GpuDeviceRes {
|
||||
rid,
|
||||
queue_rid,
|
||||
features,
|
||||
limits,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GPUAdapterInfo {
|
||||
vendor: String,
|
||||
architecture: String,
|
||||
device: String,
|
||||
description: String,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_request_adapter_info(
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[smi] adapter_rid: ResourceId,
|
||||
) -> Result<GPUAdapterInfo, deno_core::error::AnyError> {
|
||||
let state = state.borrow_mut();
|
||||
let adapter_resource = state.resource_table.get::<WebGpuAdapter>(adapter_rid)?;
|
||||
let adapter = adapter_resource.1;
|
||||
let instance = state.borrow::<Instance>();
|
||||
|
||||
let info = instance.adapter_get_info(adapter);
|
||||
adapter_resource.close();
|
||||
|
||||
Ok(GPUAdapterInfo {
|
||||
vendor: info.vendor.to_string(),
|
||||
architecture: String::new(), // TODO(#2170)
|
||||
device: info.device.to_string(),
|
||||
description: info.name,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateQuerySetArgs {
|
||||
device_rid: ResourceId,
|
||||
label: String,
|
||||
#[serde(flatten)]
|
||||
r#type: GpuQueryType,
|
||||
count: u32,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "kebab-case", tag = "type")]
|
||||
enum GpuQueryType {
|
||||
Occlusion,
|
||||
Timestamp,
|
||||
}
|
||||
|
||||
impl From<GpuQueryType> for wgpu_types::QueryType {
|
||||
fn from(query_type: GpuQueryType) -> Self {
|
||||
match query_type {
|
||||
GpuQueryType::Occlusion => wgpu_types::QueryType::Occlusion,
|
||||
GpuQueryType::Timestamp => wgpu_types::QueryType::Timestamp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_query_set(
|
||||
#[cppgc]
|
||||
pub fn op_create_gpu(
|
||||
state: &mut OpState,
|
||||
#[serde] args: CreateQuerySetArgs,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let device_resource = state.resource_table.get::<WebGpuDevice>(args.device_rid)?;
|
||||
let device = device_resource.1;
|
||||
let instance = state.borrow::<Instance>();
|
||||
|
||||
let descriptor = wgpu_types::QuerySetDescriptor {
|
||||
label: Some(Cow::Owned(args.label)),
|
||||
ty: args.r#type.into(),
|
||||
count: args.count,
|
||||
};
|
||||
|
||||
gfx_put!(instance.device_create_query_set(
|
||||
device,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, WebGpuQuerySet)
|
||||
scope: &mut v8::HandleScope,
|
||||
webidl_brand: v8::Local<v8::Value>,
|
||||
set_event_target_data: v8::Local<v8::Value>,
|
||||
error_event_class: v8::Local<v8::Value>,
|
||||
) -> GPU {
|
||||
state.put(EventTargetSetup {
|
||||
brand: v8::Global::new(scope, webidl_brand),
|
||||
set_event_target_data: v8::Global::new(scope, set_event_target_data),
|
||||
});
|
||||
state.put(ErrorEventClass(v8::Global::new(scope, error_event_class)));
|
||||
GPU
|
||||
}
|
||||
|
||||
struct EventTargetSetup {
|
||||
brand: v8::Global<v8::Value>,
|
||||
set_event_target_data: v8::Global<v8::Value>,
|
||||
}
|
||||
struct ErrorEventClass(v8::Global<v8::Value>);
|
||||
|
||||
pub struct GPU;
|
||||
|
||||
impl GarbageCollected for GPU {}
|
||||
|
||||
#[op2]
|
||||
impl GPU {
|
||||
#[async_method]
|
||||
#[cppgc]
|
||||
async fn request_adapter(
|
||||
&self,
|
||||
state: Rc<RefCell<OpState>>,
|
||||
#[webidl] options: adapter::GPURequestAdapterOptions,
|
||||
) -> Option<adapter::GPUAdapter> {
|
||||
let mut state = state.borrow_mut();
|
||||
|
||||
let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else(
|
||||
|_| wgpu_types::Backends::all(),
|
||||
|s| wgpu_types::Backends::from_comma_list(&s),
|
||||
);
|
||||
let instance = if let Some(instance) = state.try_borrow::<Instance>() {
|
||||
instance
|
||||
} else {
|
||||
state.put(Arc::new(wgpu_core::global::Global::new(
|
||||
"webgpu",
|
||||
&wgpu_types::InstanceDescriptor {
|
||||
backends,
|
||||
flags: wgpu_types::InstanceFlags::from_build_config(),
|
||||
backend_options: wgpu_types::BackendOptions {
|
||||
dx12: wgpu_types::Dx12BackendOptions {
|
||||
shader_compiler: wgpu_types::Dx12Compiler::Fxc,
|
||||
},
|
||||
gl: wgpu_types::GlBackendOptions::default(),
|
||||
noop: wgpu_types::NoopBackendOptions::default(),
|
||||
},
|
||||
},
|
||||
)));
|
||||
state.borrow::<Instance>()
|
||||
};
|
||||
|
||||
let descriptor = wgpu_core::instance::RequestAdapterOptions {
|
||||
power_preference: options
|
||||
.power_preference
|
||||
.map(|pp| match pp {
|
||||
adapter::GPUPowerPreference::LowPower => PowerPreference::LowPower,
|
||||
adapter::GPUPowerPreference::HighPerformance => {
|
||||
PowerPreference::HighPerformance
|
||||
}
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
force_fallback_adapter: options.force_fallback_adapter,
|
||||
compatible_surface: None, // windowless
|
||||
};
|
||||
let id = instance.request_adapter(&descriptor, backends, None).ok()?;
|
||||
|
||||
Some(adapter::GPUAdapter {
|
||||
instance: instance.clone(),
|
||||
features: SameObject::new(),
|
||||
limits: SameObject::new(),
|
||||
info: Rc::new(SameObject::new()),
|
||||
id,
|
||||
})
|
||||
}
|
||||
|
||||
#[string]
|
||||
fn getPreferredCanvasFormat(&self) -> &'static str {
|
||||
// https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47
|
||||
if cfg!(target_os = "android") {
|
||||
texture::GPUTextureFormat::Rgba8unorm.as_str()
|
||||
} else {
|
||||
texture::GPUTextureFormat::Bgra8unorm.as_str()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_label<'a>(label: String) -> Option<std::borrow::Cow<'a, str>> {
|
||||
if label.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(std::borrow::Cow::Owned(label))
|
||||
}
|
||||
}
|
||||
|
@ -1,420 +0,0 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use hashbrown::HashMap;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use super::error::WebGpuError;
|
||||
use super::error::WebGpuResult;
|
||||
|
||||
pub(crate) struct WebGpuPipelineLayout(
|
||||
pub(crate) crate::Instance,
|
||||
pub(crate) wgpu_core::id::PipelineLayoutId,
|
||||
);
|
||||
impl Resource for WebGpuPipelineLayout {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUPipelineLayout".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.pipeline_layout_drop(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuComputePipeline(
|
||||
pub(crate) crate::Instance,
|
||||
pub(crate) wgpu_core::id::ComputePipelineId,
|
||||
);
|
||||
impl Resource for WebGpuComputePipeline {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUComputePipeline".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.compute_pipeline_drop(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuRenderPipeline(
|
||||
pub(crate) crate::Instance,
|
||||
pub(crate) wgpu_core::id::RenderPipelineId,
|
||||
);
|
||||
impl Resource for WebGpuRenderPipeline {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderPipeline".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.render_pipeline_drop(self.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum GPUAutoLayoutMode {
|
||||
Auto,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum GPUPipelineLayoutOrGPUAutoLayoutMode {
|
||||
Layout(ResourceId),
|
||||
Auto(GPUAutoLayoutMode),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuProgrammableStage {
|
||||
module: ResourceId,
|
||||
entry_point: Option<String>,
|
||||
constants: Option<HashMap<String, f64>>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_compute_pipeline(
|
||||
state: &mut OpState,
|
||||
#[smi] device_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[serde] layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
|
||||
#[serde] compute: GpuProgrammableStage,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let pipeline_layout = match layout {
|
||||
GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => {
|
||||
let id = state.resource_table.get::<WebGpuPipelineLayout>(rid)?;
|
||||
Some(id.1)
|
||||
}
|
||||
GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None,
|
||||
};
|
||||
|
||||
let compute_shader_module_resource = state
|
||||
.resource_table
|
||||
.get::<super::shader::WebGpuShaderModule>(compute.module)?;
|
||||
|
||||
let descriptor = wgpu_core::pipeline::ComputePipelineDescriptor {
|
||||
label: Some(label),
|
||||
layout: pipeline_layout,
|
||||
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
|
||||
module: compute_shader_module_resource.1,
|
||||
entry_point: compute.entry_point.map(Cow::from),
|
||||
constants: Cow::Owned(compute.constants.unwrap_or_default()),
|
||||
zero_initialize_workgroup_memory: true,
|
||||
},
|
||||
cache: None,
|
||||
};
|
||||
|
||||
let (compute_pipeline, maybe_err) =
|
||||
instance.device_create_compute_pipeline(device, &descriptor, None, None);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGpuComputePipeline(instance.clone(), compute_pipeline));
|
||||
|
||||
Ok(WebGpuResult::rid_err(rid, maybe_err))
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct PipelineLayout {
|
||||
rid: ResourceId,
|
||||
err: Option<WebGpuError>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_compute_pipeline_get_bind_group_layout(
|
||||
state: &mut OpState,
|
||||
#[smi] compute_pipeline_rid: ResourceId,
|
||||
index: u32,
|
||||
) -> Result<PipelineLayout, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let compute_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuComputePipeline>(compute_pipeline_rid)?;
|
||||
let compute_pipeline = compute_pipeline_resource.1;
|
||||
|
||||
let (bind_group_layout, maybe_err) =
|
||||
instance.compute_pipeline_get_bind_group_layout(compute_pipeline, index, None);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::binding::WebGpuBindGroupLayout(
|
||||
instance.clone(),
|
||||
bind_group_layout,
|
||||
));
|
||||
|
||||
Ok(PipelineLayout {
|
||||
rid,
|
||||
err: maybe_err.map(WebGpuError::from),
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum GpuCullMode {
|
||||
None,
|
||||
Front,
|
||||
Back,
|
||||
}
|
||||
|
||||
impl From<GpuCullMode> for Option<wgpu_types::Face> {
|
||||
fn from(value: GpuCullMode) -> Option<wgpu_types::Face> {
|
||||
match value {
|
||||
GpuCullMode::None => None,
|
||||
GpuCullMode::Front => Some(wgpu_types::Face::Front),
|
||||
GpuCullMode::Back => Some(wgpu_types::Face::Back),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuPrimitiveState {
|
||||
topology: wgpu_types::PrimitiveTopology,
|
||||
strip_index_format: Option<wgpu_types::IndexFormat>,
|
||||
front_face: wgpu_types::FrontFace,
|
||||
cull_mode: GpuCullMode,
|
||||
unclipped_depth: bool,
|
||||
}
|
||||
|
||||
impl From<GpuPrimitiveState> for wgpu_types::PrimitiveState {
|
||||
fn from(value: GpuPrimitiveState) -> wgpu_types::PrimitiveState {
|
||||
wgpu_types::PrimitiveState {
|
||||
topology: value.topology,
|
||||
strip_index_format: value.strip_index_format,
|
||||
front_face: value.front_face,
|
||||
cull_mode: value.cull_mode.into(),
|
||||
unclipped_depth: value.unclipped_depth,
|
||||
polygon_mode: Default::default(), // native-only
|
||||
conservative: false, // native-only
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuDepthStencilState {
|
||||
format: wgpu_types::TextureFormat,
|
||||
depth_write_enabled: bool,
|
||||
depth_compare: wgpu_types::CompareFunction,
|
||||
stencil_front: wgpu_types::StencilFaceState,
|
||||
stencil_back: wgpu_types::StencilFaceState,
|
||||
stencil_read_mask: u32,
|
||||
stencil_write_mask: u32,
|
||||
depth_bias: i32,
|
||||
depth_bias_slope_scale: f32,
|
||||
depth_bias_clamp: f32,
|
||||
}
|
||||
|
||||
impl From<GpuDepthStencilState> for wgpu_types::DepthStencilState {
|
||||
fn from(state: GpuDepthStencilState) -> wgpu_types::DepthStencilState {
|
||||
wgpu_types::DepthStencilState {
|
||||
format: state.format,
|
||||
depth_write_enabled: state.depth_write_enabled,
|
||||
depth_compare: state.depth_compare,
|
||||
stencil: wgpu_types::StencilState {
|
||||
front: state.stencil_front,
|
||||
back: state.stencil_back,
|
||||
read_mask: state.stencil_read_mask,
|
||||
write_mask: state.stencil_write_mask,
|
||||
},
|
||||
bias: wgpu_types::DepthBiasState {
|
||||
constant: state.depth_bias,
|
||||
slope_scale: state.depth_bias_slope_scale,
|
||||
clamp: state.depth_bias_clamp,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuVertexBufferLayout {
|
||||
array_stride: u64,
|
||||
step_mode: wgpu_types::VertexStepMode,
|
||||
attributes: Vec<wgpu_types::VertexAttribute>,
|
||||
}
|
||||
|
||||
impl<'a> From<GpuVertexBufferLayout> for wgpu_core::pipeline::VertexBufferLayout<'a> {
|
||||
fn from(layout: GpuVertexBufferLayout) -> wgpu_core::pipeline::VertexBufferLayout<'a> {
|
||||
wgpu_core::pipeline::VertexBufferLayout {
|
||||
array_stride: layout.array_stride,
|
||||
step_mode: layout.step_mode,
|
||||
attributes: Cow::Owned(layout.attributes),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuVertexState {
|
||||
module: ResourceId,
|
||||
entry_point: Option<String>,
|
||||
constants: Option<HashMap<String, f64>>,
|
||||
buffers: Vec<Option<GpuVertexBufferLayout>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuMultisampleState {
|
||||
count: u32,
|
||||
mask: u64,
|
||||
alpha_to_coverage_enabled: bool,
|
||||
}
|
||||
|
||||
impl From<GpuMultisampleState> for wgpu_types::MultisampleState {
|
||||
fn from(gms: GpuMultisampleState) -> wgpu_types::MultisampleState {
|
||||
wgpu_types::MultisampleState {
|
||||
count: gms.count,
|
||||
mask: gms.mask,
|
||||
alpha_to_coverage_enabled: gms.alpha_to_coverage_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct GpuFragmentState {
|
||||
targets: Vec<Option<wgpu_types::ColorTargetState>>,
|
||||
module: u32,
|
||||
entry_point: Option<String>,
|
||||
constants: Option<HashMap<String, f64>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateRenderPipelineArgs {
|
||||
device_rid: ResourceId,
|
||||
label: String,
|
||||
layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
|
||||
vertex: GpuVertexState,
|
||||
primitive: GpuPrimitiveState,
|
||||
depth_stencil: Option<GpuDepthStencilState>,
|
||||
multisample: wgpu_types::MultisampleState,
|
||||
fragment: Option<GpuFragmentState>,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_render_pipeline(
|
||||
state: &mut OpState,
|
||||
#[serde] args: CreateRenderPipelineArgs,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(args.device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let layout = match args.layout {
|
||||
GPUPipelineLayoutOrGPUAutoLayoutMode::Layout(rid) => {
|
||||
let pipeline_layout_resource = state.resource_table.get::<WebGpuPipelineLayout>(rid)?;
|
||||
Some(pipeline_layout_resource.1)
|
||||
}
|
||||
GPUPipelineLayoutOrGPUAutoLayoutMode::Auto(GPUAutoLayoutMode::Auto) => None,
|
||||
};
|
||||
|
||||
let vertex_shader_module_resource = state
|
||||
.resource_table
|
||||
.get::<super::shader::WebGpuShaderModule>(args.vertex.module)?;
|
||||
|
||||
let fragment = if let Some(fragment) = args.fragment {
|
||||
let fragment_shader_module_resource =
|
||||
state
|
||||
.resource_table
|
||||
.get::<super::shader::WebGpuShaderModule>(fragment.module)?;
|
||||
|
||||
Some(wgpu_core::pipeline::FragmentState {
|
||||
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
|
||||
module: fragment_shader_module_resource.1,
|
||||
entry_point: fragment.entry_point.map(Cow::from),
|
||||
constants: Cow::Owned(fragment.constants.unwrap_or_default()),
|
||||
// Required to be true for WebGPU
|
||||
zero_initialize_workgroup_memory: true,
|
||||
},
|
||||
targets: Cow::Owned(fragment.targets),
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let vertex_buffers = args
|
||||
.vertex
|
||||
.buffers
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
let descriptor = wgpu_core::pipeline::RenderPipelineDescriptor {
|
||||
label: Some(Cow::Owned(args.label)),
|
||||
layout,
|
||||
vertex: wgpu_core::pipeline::VertexState {
|
||||
stage: wgpu_core::pipeline::ProgrammableStageDescriptor {
|
||||
module: vertex_shader_module_resource.1,
|
||||
entry_point: args.vertex.entry_point.map(Cow::Owned),
|
||||
constants: Cow::Owned(args.vertex.constants.unwrap_or_default()),
|
||||
// Required to be true for WebGPU
|
||||
zero_initialize_workgroup_memory: true,
|
||||
},
|
||||
buffers: Cow::Owned(vertex_buffers),
|
||||
},
|
||||
primitive: args.primitive.into(),
|
||||
depth_stencil: args.depth_stencil.map(Into::into),
|
||||
multisample: args.multisample,
|
||||
fragment,
|
||||
multiview: None,
|
||||
cache: None,
|
||||
};
|
||||
|
||||
let (render_pipeline, maybe_err) =
|
||||
instance.device_create_render_pipeline(device, &descriptor, None, None);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(WebGpuRenderPipeline(instance.clone(), render_pipeline));
|
||||
|
||||
Ok(WebGpuResult::rid_err(rid, maybe_err))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pipeline_get_bind_group_layout(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pipeline_rid: ResourceId,
|
||||
index: u32,
|
||||
) -> Result<PipelineLayout, AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let render_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPipeline>(render_pipeline_rid)?;
|
||||
let render_pipeline = render_pipeline_resource.1;
|
||||
|
||||
let (bind_group_layout, maybe_err) =
|
||||
instance.render_pipeline_get_bind_group_layout(render_pipeline, index, None);
|
||||
|
||||
let rid = state
|
||||
.resource_table
|
||||
.add(super::binding::WebGpuBindGroupLayout(
|
||||
instance.clone(),
|
||||
bind_group_layout,
|
||||
));
|
||||
|
||||
Ok(PipelineLayout {
|
||||
rid,
|
||||
err: maybe_err.map(WebGpuError::from),
|
||||
})
|
||||
}
|
50
deno_webgpu/pipeline_layout.rs
Normal file
50
deno_webgpu/pipeline_layout.rs
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUPipelineLayout {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::PipelineLayoutId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPUPipelineLayout {
|
||||
fn drop(&mut self) {
|
||||
self.instance.pipeline_layout_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPUPipelineLayout {
|
||||
const NAME: &'static str = "GPUPipelineLayout";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUPipelineLayout {}
|
||||
|
||||
#[op2]
|
||||
impl GPUPipelineLayout {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUPipelineLayoutDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub bind_group_layouts: Vec<Ptr<super::bind_group_layout::GPUBindGroupLayout>>,
|
||||
}
|
87
deno_webgpu/query_set.rs
Normal file
87
deno_webgpu/query_set.rs
Normal file
@ -0,0 +1,87 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use deno_error::JsErrorBox;
|
||||
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPUQuerySet {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::QuerySetId,
|
||||
pub r#type: GPUQueryType,
|
||||
pub count: u32,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPUQuerySet {
|
||||
fn drop(&mut self) {
|
||||
self.instance.query_set_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPUQuerySet {
|
||||
const NAME: &'static str = "GPUQuerySet";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUQuerySet {}
|
||||
|
||||
#[op2]
|
||||
impl GPUQuerySet {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn destroy(&self) -> Result<(), JsErrorBox> {
|
||||
Err(JsErrorBox::generic(
|
||||
"This operation is currently not supported",
|
||||
))
|
||||
}
|
||||
|
||||
#[getter]
|
||||
#[string]
|
||||
fn r#type(&self) -> &'static str {
|
||||
self.r#type.as_str()
|
||||
}
|
||||
|
||||
#[getter]
|
||||
fn count(&self) -> u32 {
|
||||
self.count
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUQuerySetDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub r#type: GPUQueryType,
|
||||
#[options(enforce_range = true)]
|
||||
pub count: u32,
|
||||
}
|
||||
|
||||
#[derive(WebIDL, Clone)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUQueryType {
|
||||
Occlusion,
|
||||
Timestamp,
|
||||
}
|
||||
impl From<GPUQueryType> for wgpu_types::QueryType {
|
||||
fn from(value: GPUQueryType) -> Self {
|
||||
match value {
|
||||
GPUQueryType::Occlusion => Self::Occlusion,
|
||||
GPUQueryType::Timestamp => Self::Timestamp,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,131 +1,157 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use crate::command_encoder::WebGpuCommandBuffer;
|
||||
use crate::Instance;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use deno_error::JsErrorBox;
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use crate::buffer::GPUBuffer;
|
||||
use crate::command_buffer::GPUCommandBuffer;
|
||||
use crate::texture::GPUTexture;
|
||||
use crate::texture::GPUTextureAspect;
|
||||
use crate::webidl::GPUExtent3D;
|
||||
use crate::webidl::GPUOrigin3D;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct WebGpuQueue(pub Instance, pub wgpu_core::id::QueueId);
|
||||
impl Resource for WebGpuQueue {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUQueue".into()
|
||||
}
|
||||
pub struct GPUQueue {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.queue_drop(self.1);
|
||||
pub label: String,
|
||||
|
||||
pub id: wgpu_core::id::QueueId,
|
||||
}
|
||||
|
||||
impl Drop for GPUQueue {
|
||||
fn drop(&mut self) {
|
||||
self.instance.queue_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUQueue {}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_queue_submit(
|
||||
state: &mut OpState,
|
||||
#[smi] queue_rid: ResourceId,
|
||||
#[serde] command_buffers: Vec<ResourceId>,
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<Instance>();
|
||||
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
|
||||
let queue = queue_resource.1;
|
||||
|
||||
let ids = command_buffers
|
||||
.iter()
|
||||
.map(|rid| {
|
||||
let buffer_resource = state.resource_table.get::<WebGpuCommandBuffer>(*rid)?;
|
||||
let mut id = buffer_resource.1.borrow_mut();
|
||||
Ok(id.take().unwrap())
|
||||
})
|
||||
.collect::<Result<Vec<_>, AnyError>>()?;
|
||||
|
||||
let maybe_err = instance.queue_submit(queue, &ids).err().map(|(_idx, e)| e);
|
||||
|
||||
for rid in command_buffers {
|
||||
let resource = state.resource_table.take::<WebGpuCommandBuffer>(rid)?;
|
||||
resource.close();
|
||||
impl GPUQueue {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
Ok(WebGpuResult::maybe_err(maybe_err))
|
||||
#[required(1)]
|
||||
fn submit(
|
||||
&self,
|
||||
#[webidl] command_buffers: Vec<Ptr<GPUCommandBuffer>>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let ids = command_buffers
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, cb)| {
|
||||
if cb.consumed.set(()).is_err() {
|
||||
Err(JsErrorBox::type_error(format!(
|
||||
"The command buffer at position {i} has already been submitted."
|
||||
)))
|
||||
} else {
|
||||
Ok(cb.id)
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let err = self.instance.queue_submit(self.id, &ids).err();
|
||||
|
||||
if let Some((_, err)) = err {
|
||||
self.error_handler.push_error(Some(err));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[async_method]
|
||||
async fn on_submitted_work_done(&self) -> Result<(), JsErrorBox> {
|
||||
Err(JsErrorBox::generic(
|
||||
"This operation is currently not supported",
|
||||
))
|
||||
}
|
||||
|
||||
#[required(3)]
|
||||
fn write_buffer(
|
||||
&self,
|
||||
#[webidl] buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] buffer_offset: u64,
|
||||
#[anybuffer] buf: &[u8],
|
||||
#[webidl(default = 0, options(enforce_range = true))] data_offset: u64,
|
||||
#[webidl(options(enforce_range = true))] size: Option<u64>,
|
||||
) {
|
||||
let data = match size {
|
||||
Some(size) => &buf[(data_offset as usize)..((data_offset + size) as usize)],
|
||||
None => &buf[(data_offset as usize)..],
|
||||
};
|
||||
|
||||
let err = self
|
||||
.instance
|
||||
.queue_write_buffer(self.id, buffer.id, buffer_offset, data)
|
||||
.err();
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(4)]
|
||||
fn write_texture(
|
||||
&self,
|
||||
#[webidl] destination: GPUTexelCopyTextureInfo,
|
||||
#[anybuffer] buf: &[u8],
|
||||
#[webidl] data_layout: GPUTexelCopyBufferLayout,
|
||||
#[webidl] size: GPUExtent3D,
|
||||
) {
|
||||
let destination = wgpu_core::command::TexelCopyTextureInfo {
|
||||
texture: destination.texture.id,
|
||||
mip_level: destination.mip_level,
|
||||
origin: destination.origin.into(),
|
||||
aspect: destination.aspect.into(),
|
||||
};
|
||||
|
||||
let data_layout = wgpu_types::TexelCopyBufferLayout {
|
||||
offset: data_layout.offset,
|
||||
bytes_per_row: data_layout.bytes_per_row,
|
||||
rows_per_image: data_layout.rows_per_image,
|
||||
};
|
||||
|
||||
let err = self
|
||||
.instance
|
||||
.queue_write_texture(self.id, &destination, buf, &data_layout, &size.into())
|
||||
.err();
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GpuTexelCopyBufferLayout {
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUTexelCopyTextureInfo {
|
||||
pub texture: Ptr<GPUTexture>,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
pub mip_level: u32,
|
||||
#[webidl(default = Default::default())]
|
||||
pub origin: GPUOrigin3D,
|
||||
#[webidl(default = GPUTextureAspect::All)]
|
||||
pub aspect: GPUTextureAspect,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
struct GPUTexelCopyBufferLayout {
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
offset: u64,
|
||||
#[options(enforce_range = true)]
|
||||
bytes_per_row: Option<u32>,
|
||||
#[options(enforce_range = true)]
|
||||
rows_per_image: Option<u32>,
|
||||
}
|
||||
|
||||
impl From<GpuTexelCopyBufferLayout> for wgpu_types::TexelCopyBufferLayout {
|
||||
fn from(layout: GpuTexelCopyBufferLayout) -> Self {
|
||||
wgpu_types::TexelCopyBufferLayout {
|
||||
offset: layout.offset,
|
||||
bytes_per_row: layout.bytes_per_row,
|
||||
rows_per_image: layout.rows_per_image,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_write_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] queue_rid: ResourceId,
|
||||
#[smi] buffer: ResourceId,
|
||||
#[number] buffer_offset: u64,
|
||||
#[number] data_offset: usize,
|
||||
#[number] size: Option<usize>,
|
||||
#[buffer] buf: &[u8],
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<Instance>();
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(buffer)?;
|
||||
let buffer = buffer_resource.1;
|
||||
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
|
||||
let queue = queue_resource.1;
|
||||
|
||||
let data = match size {
|
||||
Some(size) => &buf[data_offset..(data_offset + size)],
|
||||
None => &buf[data_offset..],
|
||||
};
|
||||
let maybe_err = instance
|
||||
.queue_write_buffer(queue, buffer, buffer_offset, data)
|
||||
.err();
|
||||
|
||||
Ok(WebGpuResult::maybe_err(maybe_err))
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_write_texture(
|
||||
state: &mut OpState,
|
||||
#[smi] queue_rid: ResourceId,
|
||||
#[serde] destination: super::command_encoder::GpuTexelCopyTextureInfo,
|
||||
#[serde] data_layout: GpuTexelCopyBufferLayout,
|
||||
#[serde] size: wgpu_types::Extent3d,
|
||||
#[buffer] buf: &[u8],
|
||||
) -> Result<WebGpuResult, AnyError> {
|
||||
let instance = state.borrow::<Instance>();
|
||||
let texture_resource = state
|
||||
.resource_table
|
||||
.get::<super::texture::WebGpuTexture>(destination.texture)?;
|
||||
let queue_resource = state.resource_table.get::<WebGpuQueue>(queue_rid)?;
|
||||
let queue = queue_resource.1;
|
||||
|
||||
let destination = wgpu_core::command::TexelCopyTextureInfo {
|
||||
texture: texture_resource.id,
|
||||
mip_level: destination.mip_level,
|
||||
origin: destination.origin,
|
||||
aspect: destination.aspect,
|
||||
};
|
||||
let data_layout = data_layout.into();
|
||||
|
||||
gfx_ok!(instance.queue_write_texture(queue, &destination, buf, &data_layout, &size))
|
||||
}
|
||||
|
409
deno_webgpu/render_bundle.rs
Normal file
409
deno_webgpu/render_bundle.rs
Normal file
@ -0,0 +1,409 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZeroU64;
|
||||
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::webidl::IntOptions;
|
||||
use deno_core::webidl::Nullable;
|
||||
use deno_core::webidl::WebIdlConverter;
|
||||
use deno_core::webidl::WebIdlError;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use deno_error::JsErrorBox;
|
||||
|
||||
use crate::buffer::GPUBuffer;
|
||||
use crate::texture::GPUTextureFormat;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPURenderBundleEncoder {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
pub encoder: RefCell<Option<wgpu_core::command::RenderBundleEncoder>>,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPURenderBundleEncoder {}
|
||||
|
||||
#[op2]
|
||||
impl GPURenderBundleEncoder {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
#[cppgc]
|
||||
fn finish(&self, #[webidl] descriptor: GPURenderBundleDescriptor) -> GPURenderBundle {
|
||||
let wgpu_descriptor = wgpu_core::command::RenderBundleDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
};
|
||||
|
||||
let (id, err) = self.instance.render_bundle_encoder_finish(
|
||||
self.encoder.borrow_mut().take().unwrap(),
|
||||
&wgpu_descriptor,
|
||||
None,
|
||||
);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
GPURenderBundle {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_debug_group(&self, #[webidl] group_label: String) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
let label = std::ffi::CString::new(group_label).unwrap();
|
||||
// SAFETY: the string the raw pointer points to lives longer than the below
|
||||
// function invocation.
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group(
|
||||
encoder,
|
||||
label.as_ptr(),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn pop_debug_group(&self) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(encoder);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn insert_debug_marker(&self, #[webidl] marker_label: String) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
let label = std::ffi::CString::new(marker_label).unwrap();
|
||||
|
||||
// SAFETY: the string the raw pointer points to lives longer than the below
|
||||
// function invocation.
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker(
|
||||
encoder,
|
||||
label.as_ptr(),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_bind_group<'a>(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
#[webidl(options(enforce_range = true))] index: u32,
|
||||
#[webidl] bind_group: Nullable<Ptr<crate::bind_group::GPUBindGroup>>,
|
||||
dynamic_offsets: v8::Local<'a, v8::Value>,
|
||||
dynamic_offsets_data_start: v8::Local<'a, v8::Value>,
|
||||
dynamic_offsets_data_length: v8::Local<'a, v8::Value>,
|
||||
) -> Result<(), SetBindGroupError> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'";
|
||||
if let Ok(uint_32) = dynamic_offsets.try_cast::<v8::Uint32Array>() {
|
||||
let start = u64::convert(
|
||||
scope,
|
||||
dynamic_offsets_data_start,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 4")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)? as usize;
|
||||
let len = u32::convert(
|
||||
scope,
|
||||
dynamic_offsets_data_length,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 5")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)? as usize;
|
||||
|
||||
let ab = uint_32.buffer(scope).unwrap();
|
||||
let ptr = ab.data().unwrap();
|
||||
let ab_len = ab.byte_length() / 4;
|
||||
|
||||
// SAFETY: created from an array buffer, slice is dropped at end of function call
|
||||
let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) };
|
||||
|
||||
let offsets = &data[start..(start + len)];
|
||||
|
||||
// SAFETY: wgpu FFI call
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
|
||||
encoder,
|
||||
index,
|
||||
bind_group.into_option().map(|bind_group| bind_group.id),
|
||||
offsets.as_ptr(),
|
||||
offsets.len(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let offsets = <Option<Vec<u32>>>::convert(
|
||||
scope,
|
||||
dynamic_offsets,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 3")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)?
|
||||
.unwrap_or_default();
|
||||
|
||||
// SAFETY: wgpu FFI call
|
||||
unsafe {
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group(
|
||||
encoder,
|
||||
index,
|
||||
bind_group.into_option().map(|bind_group| bind_group.id),
|
||||
offsets.as_ptr(),
|
||||
offsets.len(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pipeline(
|
||||
&self,
|
||||
#[webidl] pipeline: Ptr<crate::render_pipeline::GPURenderPipeline>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(encoder, pipeline.id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn set_index_buffer(
|
||||
&self,
|
||||
#[webidl] buffer: Ptr<GPUBuffer>,
|
||||
#[webidl] index_format: crate::render_pipeline::GPUIndexFormat,
|
||||
#[webidl(default = 0, options(enforce_range = true))] offset: u64,
|
||||
#[webidl(options(enforce_range = true))] size: Option<u64>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
encoder.set_index_buffer(
|
||||
buffer.id,
|
||||
index_format.into(),
|
||||
offset,
|
||||
size.and_then(NonZeroU64::new),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn set_vertex_buffer(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] slot: u32,
|
||||
#[webidl] buffer: Ptr<GPUBuffer>, // TODO(wgpu): support nullable buffer
|
||||
#[webidl(default = 0, options(enforce_range = true))] offset: u64,
|
||||
#[webidl(options(enforce_range = true))] size: Option<u64>,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer(
|
||||
encoder,
|
||||
slot,
|
||||
buffer.id,
|
||||
offset,
|
||||
size.and_then(NonZeroU64::new),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn draw(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] vertex_count: u32,
|
||||
#[webidl(default = 1, options(enforce_range = true))] instance_count: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_vertex: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_instance: u32,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw(
|
||||
encoder,
|
||||
vertex_count,
|
||||
instance_count,
|
||||
first_vertex,
|
||||
first_instance,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn draw_indexed(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] index_count: u32,
|
||||
#[webidl(default = 1, options(enforce_range = true))] instance_count: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_index: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] base_vertex: i32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_instance: u32,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed(
|
||||
encoder,
|
||||
index_count,
|
||||
instance_count,
|
||||
first_index,
|
||||
base_vertex,
|
||||
first_instance,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn draw_indirect(
|
||||
&self,
|
||||
#[webidl] indirect_buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] indirect_offset: u64,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect(
|
||||
encoder,
|
||||
indirect_buffer.id,
|
||||
indirect_offset,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn draw_indexed_indirect(
|
||||
&self,
|
||||
#[webidl] indirect_buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] indirect_offset: u64,
|
||||
) -> Result<(), JsErrorBox> {
|
||||
let mut encoder = self.encoder.borrow_mut();
|
||||
let encoder = encoder
|
||||
.as_mut()
|
||||
.ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?;
|
||||
|
||||
wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed_indirect(
|
||||
encoder,
|
||||
indirect_buffer.id,
|
||||
indirect_offset,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURenderBundleEncoderDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub color_formats: Vec<Nullable<GPUTextureFormat>>,
|
||||
pub depth_stencil_format: Option<GPUTextureFormat>,
|
||||
#[webidl(default = 1)]
|
||||
#[options(enforce_range = true)]
|
||||
pub sample_count: u32,
|
||||
|
||||
#[webidl(default = false)]
|
||||
pub depth_read_only: bool,
|
||||
#[webidl(default = false)]
|
||||
pub stencil_read_only: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
enum SetBindGroupError {
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
WebIDL(#[from] WebIdlError),
|
||||
#[class(inherit)]
|
||||
#[error(transparent)]
|
||||
Other(#[from] JsErrorBox),
|
||||
}
|
||||
|
||||
pub struct GPURenderBundle {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::RenderBundleId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPURenderBundle {
|
||||
fn drop(&mut self) {
|
||||
self.instance.render_bundle_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPURenderBundle {
|
||||
const NAME: &'static str = "GPURenderBundle";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPURenderBundle {}
|
||||
|
||||
#[op2]
|
||||
impl GPURenderBundle {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURenderBundleDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
}
|
@ -1,509 +1,476 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZeroU64;
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::v8;
|
||||
use deno_core::webidl::IntOptions;
|
||||
use deno_core::webidl::Nullable;
|
||||
use deno_core::webidl::WebIdlConverter;
|
||||
use deno_core::webidl::WebIdlError;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum RenderPassError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[error("size must be larger than 0")]
|
||||
InvalidSize,
|
||||
#[error(transparent)]
|
||||
RenderPass(#[from] wgpu_core::command::RenderPassError),
|
||||
use crate::buffer::GPUBuffer;
|
||||
use crate::render_bundle::GPURenderBundle;
|
||||
use crate::texture::GPUTextureView;
|
||||
use crate::webidl::GPUColor;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPURenderPassEncoder {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
pub render_pass: RefCell<wgpu_core::command::RenderPass>,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuRenderPass(pub(crate) RefCell<wgpu_core::command::RenderPass>);
|
||||
impl Resource for WebGpuRenderPass {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPURenderPass".into()
|
||||
impl GarbageCollected for GPURenderPassEncoder {}
|
||||
|
||||
#[op2]
|
||||
impl GPURenderPassEncoder {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
#[required(6)]
|
||||
fn set_viewport(
|
||||
&self,
|
||||
#[webidl] x: f32,
|
||||
#[webidl] y: f32,
|
||||
#[webidl] width: f32,
|
||||
#[webidl] height: f32,
|
||||
#[webidl] min_depth: f32,
|
||||
#[webidl] max_depth: f32,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_set_viewport(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
min_depth,
|
||||
max_depth,
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(4)]
|
||||
fn set_scissor_rect(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] x: u32,
|
||||
#[webidl(options(enforce_range = true))] y: u32,
|
||||
#[webidl(options(enforce_range = true))] width: u32,
|
||||
#[webidl(options(enforce_range = true))] height: u32,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_set_scissor_rect(&mut self.render_pass.borrow_mut(), x, y, width, height)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn set_blend_constant(&self, #[webidl] color: GPUColor) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_set_blend_constant(&mut self.render_pass.borrow_mut(), color.into())
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn set_stencil_reference(&self, #[webidl(options(enforce_range = true))] reference: u32) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_set_stencil_reference(&mut self.render_pass.borrow_mut(), reference)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn begin_occlusion_query(&self, #[webidl(options(enforce_range = true))] query_index: u32) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_begin_occlusion_query(&mut self.render_pass.borrow_mut(), query_index)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn end_occlusion_query(&self) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_end_occlusion_query(&mut self.render_pass.borrow_mut())
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn execute_bundles(&self, #[webidl] bundles: Vec<Ptr<GPURenderBundle>>) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_execute_bundles(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
&bundles
|
||||
.into_iter()
|
||||
.map(|bundle| bundle.id)
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn end(&self) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_end(&mut self.render_pass.borrow_mut())
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn push_debug_group(&self, #[webidl] group_label: String) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_push_debug_group(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
&group_label,
|
||||
0, // wgpu#975
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn pop_debug_group(&self) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_pop_debug_group(&mut self.render_pass.borrow_mut())
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn insert_debug_marker(&self, #[webidl] marker_label: String) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_insert_debug_marker(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
&marker_label,
|
||||
0, // wgpu#975
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
fn set_bind_group<'a>(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
#[webidl(options(enforce_range = true))] index: u32,
|
||||
#[webidl] bind_group: Nullable<Ptr<crate::bind_group::GPUBindGroup>>,
|
||||
dynamic_offsets: v8::Local<'a, v8::Value>,
|
||||
dynamic_offsets_data_start: v8::Local<'a, v8::Value>,
|
||||
dynamic_offsets_data_length: v8::Local<'a, v8::Value>,
|
||||
) -> Result<(), WebIdlError> {
|
||||
const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'";
|
||||
|
||||
let err = if let Ok(uint_32) = dynamic_offsets.try_cast::<v8::Uint32Array>() {
|
||||
let start = u64::convert(
|
||||
scope,
|
||||
dynamic_offsets_data_start,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 4")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)? as usize;
|
||||
let len = u32::convert(
|
||||
scope,
|
||||
dynamic_offsets_data_length,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 5")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)? as usize;
|
||||
|
||||
let ab = uint_32.buffer(scope).unwrap();
|
||||
let ptr = ab.data().unwrap();
|
||||
let ab_len = ab.byte_length() / 4;
|
||||
|
||||
// SAFETY: created from an array buffer, slice is dropped at end of function call
|
||||
let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) };
|
||||
|
||||
let offsets = &data[start..(start + len)];
|
||||
|
||||
self.instance
|
||||
.render_pass_set_bind_group(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
index,
|
||||
bind_group.into_option().map(|bind_group| bind_group.id),
|
||||
offsets,
|
||||
)
|
||||
.err()
|
||||
} else {
|
||||
let offsets = <Option<Vec<u32>>>::convert(
|
||||
scope,
|
||||
dynamic_offsets,
|
||||
Cow::Borrowed(PREFIX),
|
||||
(|| Cow::Borrowed("Argument 3")).into(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)?
|
||||
.unwrap_or_default();
|
||||
|
||||
self.instance
|
||||
.render_pass_set_bind_group(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
index,
|
||||
bind_group.into_option().map(|bind_group| bind_group.id),
|
||||
&offsets,
|
||||
)
|
||||
.err()
|
||||
};
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_pipeline(&self, #[webidl] pipeline: Ptr<crate::render_pipeline::GPURenderPipeline>) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_set_pipeline(&mut self.render_pass.borrow_mut(), pipeline.id)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn set_index_buffer(
|
||||
&self,
|
||||
#[webidl] buffer: Ptr<GPUBuffer>,
|
||||
#[webidl] index_format: crate::render_pipeline::GPUIndexFormat,
|
||||
#[webidl(default = 0, options(enforce_range = true))] offset: u64,
|
||||
#[webidl(options(enforce_range = true))] size: Option<u64>,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_set_index_buffer(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
buffer.id,
|
||||
index_format.into(),
|
||||
offset,
|
||||
size.and_then(NonZeroU64::new),
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn set_vertex_buffer(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] slot: u32,
|
||||
#[webidl] buffer: Ptr<GPUBuffer>, // TODO(wgpu): support nullable buffer
|
||||
#[webidl(default = 0, options(enforce_range = true))] offset: u64,
|
||||
#[webidl(options(enforce_range = true))] size: Option<u64>,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_set_vertex_buffer(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
slot,
|
||||
buffer.id,
|
||||
offset,
|
||||
size.and_then(NonZeroU64::new),
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn draw(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] vertex_count: u32,
|
||||
#[webidl(default = 1, options(enforce_range = true))] instance_count: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_vertex: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_instance: u32,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_draw(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
vertex_count,
|
||||
instance_count,
|
||||
first_vertex,
|
||||
first_instance,
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(1)]
|
||||
fn draw_indexed(
|
||||
&self,
|
||||
#[webidl(options(enforce_range = true))] index_count: u32,
|
||||
#[webidl(default = 1, options(enforce_range = true))] instance_count: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_index: u32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] base_vertex: i32,
|
||||
#[webidl(default = 0, options(enforce_range = true))] first_instance: u32,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_draw_indexed(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
index_count,
|
||||
instance_count,
|
||||
first_index,
|
||||
base_vertex,
|
||||
first_instance,
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn draw_indirect(
|
||||
&self,
|
||||
#[webidl] indirect_buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] indirect_offset: u64,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_draw_indirect(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
indirect_buffer.id,
|
||||
indirect_offset,
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
|
||||
#[required(2)]
|
||||
fn draw_indexed_indirect(
|
||||
&self,
|
||||
#[webidl] indirect_buffer: Ptr<GPUBuffer>,
|
||||
#[webidl(options(enforce_range = true))] indirect_offset: u64,
|
||||
) {
|
||||
let err = self
|
||||
.instance
|
||||
.render_pass_draw_indexed_indirect(
|
||||
&mut self.render_pass.borrow_mut(),
|
||||
indirect_buffer.id,
|
||||
indirect_offset,
|
||||
)
|
||||
.err();
|
||||
self.error_handler.push_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RenderPassSetViewportArgs {
|
||||
render_pass_rid: ResourceId,
|
||||
x: f32,
|
||||
y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
min_depth: f32,
|
||||
max_depth: f32,
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURenderPassDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub color_attachments: Vec<Nullable<GPURenderPassColorAttachment>>,
|
||||
pub depth_stencil_attachment: Option<GPURenderPassDepthStencilAttachment>,
|
||||
pub occlusion_query_set: Option<Ptr<crate::query_set::GPUQuerySet>>,
|
||||
pub timestamp_writes: Option<GPURenderPassTimestampWrites>,
|
||||
/*#[webidl(default = 50000000)]
|
||||
#[options(enforce_range = true)]
|
||||
pub max_draw_count: u64,*/
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_viewport(
|
||||
state: &mut OpState,
|
||||
#[serde] args: RenderPassSetViewportArgs,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(args.render_pass_rid)?;
|
||||
|
||||
state.borrow::<super::Instance>().render_pass_set_viewport(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
args.x,
|
||||
args.y,
|
||||
args.width,
|
||||
args.height,
|
||||
args.min_depth,
|
||||
args.max_depth,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURenderPassColorAttachment {
|
||||
pub view: Ptr<GPUTextureView>,
|
||||
/*#[options(enforce_range = true)]
|
||||
pub depth_slice: Option<u32>,*/
|
||||
pub resolve_target: Option<Ptr<GPUTextureView>>,
|
||||
pub clear_value: Option<GPUColor>,
|
||||
pub load_op: GPULoadOp,
|
||||
pub store_op: GPUStoreOp,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_scissor_rect(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPULoadOp {
|
||||
Load,
|
||||
Clear,
|
||||
}
|
||||
impl GPULoadOp {
|
||||
pub fn with_default_value<V: Default>(self, val: Option<V>) -> wgpu_core::command::LoadOp<V> {
|
||||
match self {
|
||||
GPULoadOp::Load => wgpu_core::command::LoadOp::Load,
|
||||
GPULoadOp::Clear => wgpu_core::command::LoadOp::Clear(val.unwrap_or_default()),
|
||||
}
|
||||
}
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_set_scissor_rect(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
pub fn with_value<V>(self, val: V) -> wgpu_core::command::LoadOp<V> {
|
||||
match self {
|
||||
GPULoadOp::Load => wgpu_core::command::LoadOp::Load,
|
||||
GPULoadOp::Clear => wgpu_core::command::LoadOp::Clear(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_blend_constant(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
#[serde] color: wgpu_types::Color,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_set_blend_constant(&mut render_pass_resource.0.borrow_mut(), color)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUStoreOp {
|
||||
Store,
|
||||
Discard,
|
||||
}
|
||||
impl From<GPUStoreOp> for wgpu_core::command::StoreOp {
|
||||
fn from(value: GPUStoreOp) -> Self {
|
||||
match value {
|
||||
GPUStoreOp::Store => Self::Store,
|
||||
GPUStoreOp::Discard => Self::Discard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_stencil_reference(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
reference: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_set_stencil_reference(&mut render_pass_resource.0.borrow_mut(), reference)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURenderPassDepthStencilAttachment {
|
||||
pub view: Ptr<GPUTextureView>,
|
||||
pub depth_clear_value: Option<f32>,
|
||||
pub depth_load_op: Option<GPULoadOp>,
|
||||
pub depth_store_op: Option<GPUStoreOp>,
|
||||
#[webidl(default = false)]
|
||||
pub depth_read_only: bool,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
pub stencil_clear_value: u32,
|
||||
pub stencil_load_op: Option<GPULoadOp>,
|
||||
pub stencil_store_op: Option<GPUStoreOp>,
|
||||
#[webidl(default = false)]
|
||||
pub stencil_read_only: bool,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_begin_occlusion_query(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
query_index: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_begin_occlusion_query(&mut render_pass_resource.0.borrow_mut(), query_index)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_end_occlusion_query(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_end_occlusion_query(&mut render_pass_resource.0.borrow_mut())?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_execute_bundles(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
#[serde] bundles: Vec<u32>,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let bundles = bundles
|
||||
.iter()
|
||||
.map(|rid| {
|
||||
let render_bundle_resource = state
|
||||
.resource_table
|
||||
.get::<super::bundle::WebGpuRenderBundle>(*rid)?;
|
||||
Ok(render_bundle_resource.1)
|
||||
})
|
||||
.collect::<Result<Vec<_>, deno_core::error::AnyError>>()?;
|
||||
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_execute_bundles(&mut render_pass_resource.0.borrow_mut(), &bundles)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_end(
|
||||
state: &mut OpState,
|
||||
#[smi] _command_encoder_rid: ResourceId,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.take::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_end(&mut render_pass_resource.0.borrow_mut())?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_bind_group(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
index: u32,
|
||||
bind_group: u32,
|
||||
#[buffer] dynamic_offsets_data: &[u32],
|
||||
#[number] dynamic_offsets_data_start: usize,
|
||||
#[number] dynamic_offsets_data_length: usize,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let bind_group_resource = state
|
||||
.resource_table
|
||||
.get::<super::binding::WebGpuBindGroup>(bind_group)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
let start = dynamic_offsets_data_start;
|
||||
let len = dynamic_offsets_data_length;
|
||||
|
||||
// Assert that length and start are both in bounds
|
||||
assert!(start <= dynamic_offsets_data.len());
|
||||
assert!(len <= dynamic_offsets_data.len() - start);
|
||||
|
||||
let dynamic_offsets_data: &[u32] = &dynamic_offsets_data[start..start + len];
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_set_bind_group(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
index,
|
||||
Some(bind_group_resource.1),
|
||||
dynamic_offsets_data,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_push_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
#[string] group_label: &str,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_push_debug_group(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
group_label,
|
||||
0, // wgpu#975
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_pop_debug_group(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_pop_debug_group(&mut render_pass_resource.0.borrow_mut())?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_insert_debug_marker(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
#[string] marker_label: &str,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_insert_debug_marker(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
marker_label,
|
||||
0, // wgpu#975
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_pipeline(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
pipeline: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pipeline_resource = state
|
||||
.resource_table
|
||||
.get::<super::pipeline::WebGpuRenderPipeline>(pipeline)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state.borrow::<super::Instance>().render_pass_set_pipeline(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
render_pipeline_resource.1,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_index_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
buffer: u32,
|
||||
#[serde] index_format: wgpu_types::IndexFormat,
|
||||
#[number] offset: u64,
|
||||
#[number] size: Option<u64>,
|
||||
) -> Result<WebGpuResult, RenderPassError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(buffer)
|
||||
.map_err(RenderPassError::Resource)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)
|
||||
.map_err(RenderPassError::Resource)?;
|
||||
|
||||
let size = if let Some(size) = size {
|
||||
Some(std::num::NonZeroU64::new(size).ok_or(RenderPassError::InvalidSize)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_set_index_buffer(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
buffer_resource.1,
|
||||
index_format,
|
||||
offset,
|
||||
size,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_set_vertex_buffer(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
slot: u32,
|
||||
buffer: u32,
|
||||
#[number] offset: u64,
|
||||
#[number] size: Option<u64>,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(buffer)
|
||||
.map_err(RenderPassError::Resource)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)
|
||||
.map_err(RenderPassError::Resource)?;
|
||||
|
||||
let size = if let Some(size) = size {
|
||||
Some(std::num::NonZeroU64::new(size).ok_or(RenderPassError::InvalidSize)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_set_vertex_buffer(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
slot,
|
||||
buffer_resource.1,
|
||||
offset,
|
||||
size,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_draw(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
vertex_count: u32,
|
||||
instance_count: u32,
|
||||
first_vertex: u32,
|
||||
first_instance: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state.borrow::<super::Instance>().render_pass_draw(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
vertex_count,
|
||||
instance_count,
|
||||
first_vertex,
|
||||
first_instance,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_draw_indexed(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
first_index: u32,
|
||||
base_vertex: i32,
|
||||
first_instance: u32,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state.borrow::<super::Instance>().render_pass_draw_indexed(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
index_count,
|
||||
instance_count,
|
||||
first_index,
|
||||
base_vertex,
|
||||
first_instance,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_draw_indirect(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
indirect_buffer: u32,
|
||||
#[number] indirect_offset: u64,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_draw_indirect(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
buffer_resource.1,
|
||||
indirect_offset,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_render_pass_draw_indexed_indirect(
|
||||
state: &mut OpState,
|
||||
#[smi] render_pass_rid: ResourceId,
|
||||
indirect_buffer: u32,
|
||||
#[number] indirect_offset: u64,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let buffer_resource = state
|
||||
.resource_table
|
||||
.get::<super::buffer::WebGpuBuffer>(indirect_buffer)?;
|
||||
let render_pass_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuRenderPass>(render_pass_rid)?;
|
||||
|
||||
state
|
||||
.borrow::<super::Instance>()
|
||||
.render_pass_draw_indexed_indirect(
|
||||
&mut render_pass_resource.0.borrow_mut(),
|
||||
buffer_resource.1,
|
||||
indirect_offset,
|
||||
)?;
|
||||
|
||||
Ok(WebGpuResult::empty())
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURenderPassTimestampWrites {
|
||||
pub query_set: Ptr<crate::query_set::GPUQuerySet>,
|
||||
#[options(enforce_range = true)]
|
||||
pub beginning_of_pass_write_index: Option<u32>,
|
||||
#[options(enforce_range = true)]
|
||||
pub end_of_pass_write_index: Option<u32>,
|
||||
}
|
||||
|
550
deno_webgpu/render_pipeline.rs
Normal file
550
deno_webgpu/render_pipeline.rs
Normal file
@ -0,0 +1,550 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::op2;
|
||||
use deno_core::webidl::Nullable;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::bind_group_layout::GPUBindGroupLayout;
|
||||
use crate::sampler::GPUCompareFunction;
|
||||
use crate::shader::GPUShaderModule;
|
||||
use crate::texture::GPUTextureFormat;
|
||||
use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode;
|
||||
use crate::Instance;
|
||||
|
||||
pub struct GPURenderPipeline {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
pub id: wgpu_core::id::RenderPipelineId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPURenderPipeline {
|
||||
fn drop(&mut self) {
|
||||
self.instance.render_pipeline_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPURenderPipeline {
|
||||
const NAME: &'static str = "GPURenderPipeline";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPURenderPipeline {}
|
||||
|
||||
#[op2]
|
||||
impl GPURenderPipeline {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
#[cppgc]
|
||||
fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout {
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.render_pipeline_get_bind_group_layout(self.id, index, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
// TODO(wgpu): needs to add a way to retrieve the label
|
||||
GPUBindGroupLayout {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPURenderPipelineDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
|
||||
pub vertex: GPUVertexState,
|
||||
pub primitive: GPUPrimitiveState,
|
||||
pub depth_stencil: Option<GPUDepthStencilState>,
|
||||
pub multisample: GPUMultisampleState,
|
||||
pub fragment: Option<GPUFragmentState>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUMultisampleState {
|
||||
#[webidl(default = 1)]
|
||||
#[options(enforce_range = true)]
|
||||
pub count: u32,
|
||||
#[webidl(default = 0xFFFFFFFF)]
|
||||
#[options(enforce_range = true)]
|
||||
pub mask: u32,
|
||||
#[webidl(default = false)]
|
||||
pub alpha_to_coverage_enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUDepthStencilState {
|
||||
pub format: GPUTextureFormat,
|
||||
pub depth_write_enabled: Option<bool>,
|
||||
pub depth_compare: Option<GPUCompareFunction>,
|
||||
pub stencil_front: GPUStencilFaceState,
|
||||
pub stencil_back: GPUStencilFaceState,
|
||||
#[webidl(default = 0xFFFFFFFF)]
|
||||
#[options(enforce_range = true)]
|
||||
pub stencil_read_mask: u32,
|
||||
#[webidl(default = 0xFFFFFFFF)]
|
||||
#[options(enforce_range = true)]
|
||||
pub stencil_write_mask: u32,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
pub depth_bias: i32,
|
||||
#[webidl(default = 0.0)]
|
||||
pub depth_bias_slope_scale: f32,
|
||||
#[webidl(default = 0.0)]
|
||||
pub depth_bias_clamp: f32,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUStencilFaceState {
|
||||
#[webidl(default = GPUCompareFunction::Always)]
|
||||
pub compare: GPUCompareFunction,
|
||||
#[webidl(default = GPUStencilOperation::Keep)]
|
||||
pub fail_op: GPUStencilOperation,
|
||||
#[webidl(default = GPUStencilOperation::Keep)]
|
||||
pub depth_fail_op: GPUStencilOperation,
|
||||
#[webidl(default = GPUStencilOperation::Keep)]
|
||||
pub pass_op: GPUStencilOperation,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUStencilOperation {
|
||||
Keep,
|
||||
Zero,
|
||||
Replace,
|
||||
Invert,
|
||||
IncrementClamp,
|
||||
DecrementClamp,
|
||||
IncrementWrap,
|
||||
DecrementWrap,
|
||||
}
|
||||
|
||||
impl From<GPUStencilOperation> for wgpu_types::StencilOperation {
|
||||
fn from(value: GPUStencilOperation) -> Self {
|
||||
match value {
|
||||
GPUStencilOperation::Keep => Self::Keep,
|
||||
GPUStencilOperation::Zero => Self::Zero,
|
||||
GPUStencilOperation::Replace => Self::Replace,
|
||||
GPUStencilOperation::Invert => Self::Invert,
|
||||
GPUStencilOperation::IncrementClamp => Self::IncrementClamp,
|
||||
GPUStencilOperation::DecrementClamp => Self::DecrementClamp,
|
||||
GPUStencilOperation::IncrementWrap => Self::IncrementWrap,
|
||||
GPUStencilOperation::DecrementWrap => Self::DecrementWrap,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUVertexState {
|
||||
pub module: Ptr<GPUShaderModule>,
|
||||
pub entry_point: Option<String>,
|
||||
#[webidl(default = Default::default())]
|
||||
pub constants: IndexMap<String, f64>,
|
||||
#[webidl(default = vec![])]
|
||||
pub buffers: Vec<Nullable<GPUVertexBufferLayout>>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUFragmentState {
|
||||
pub module: Ptr<GPUShaderModule>,
|
||||
pub entry_point: Option<String>,
|
||||
#[webidl(default = Default::default())]
|
||||
pub constants: IndexMap<String, f64>,
|
||||
pub targets: Vec<Nullable<GPUColorTargetState>>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUColorTargetState {
|
||||
pub format: GPUTextureFormat,
|
||||
pub blend: Option<GPUBlendState>,
|
||||
#[webidl(default = 0xF)]
|
||||
#[options(enforce_range = true)]
|
||||
pub write_mask: u32,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBlendState {
|
||||
pub color: GPUBlendComponent,
|
||||
pub alpha: GPUBlendComponent,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUBlendComponent {
|
||||
#[webidl(default = GPUBlendOperation::Add)]
|
||||
pub operation: GPUBlendOperation,
|
||||
#[webidl(default = GPUBlendFactor::One)]
|
||||
pub src_factor: GPUBlendFactor,
|
||||
#[webidl(default = GPUBlendFactor::Zero)]
|
||||
pub dst_factor: GPUBlendFactor,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUBlendOperation {
|
||||
Add,
|
||||
Subtract,
|
||||
ReverseSubtract,
|
||||
Min,
|
||||
Max,
|
||||
}
|
||||
|
||||
impl From<GPUBlendOperation> for wgpu_types::BlendOperation {
|
||||
fn from(value: GPUBlendOperation) -> Self {
|
||||
match value {
|
||||
GPUBlendOperation::Add => Self::Add,
|
||||
GPUBlendOperation::Subtract => Self::Subtract,
|
||||
GPUBlendOperation::ReverseSubtract => Self::ReverseSubtract,
|
||||
GPUBlendOperation::Min => Self::Min,
|
||||
GPUBlendOperation::Max => Self::Max,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUBlendFactor {
|
||||
#[webidl(rename = "zero")]
|
||||
Zero,
|
||||
#[webidl(rename = "one")]
|
||||
One,
|
||||
#[webidl(rename = "src")]
|
||||
Src,
|
||||
#[webidl(rename = "one-minus-src")]
|
||||
OneMinusSrc,
|
||||
#[webidl(rename = "src-alpha")]
|
||||
SrcAlpha,
|
||||
#[webidl(rename = "one-minus-src-alpha")]
|
||||
OneMinusSrcAlpha,
|
||||
#[webidl(rename = "dst")]
|
||||
Dst,
|
||||
#[webidl(rename = "one-minus-dst")]
|
||||
OneMinusDst,
|
||||
#[webidl(rename = "dst-alpha")]
|
||||
DstAlpha,
|
||||
#[webidl(rename = "one-minus-dst-alpha")]
|
||||
OneMinusDstAlpha,
|
||||
#[webidl(rename = "src-alpha-saturated")]
|
||||
SrcAlphaSaturated,
|
||||
#[webidl(rename = "constant")]
|
||||
Constant,
|
||||
#[webidl(rename = "one-minus-constant")]
|
||||
OneMinusConstant,
|
||||
#[webidl(rename = "src1")]
|
||||
Src1,
|
||||
#[webidl(rename = "one-minus-src1")]
|
||||
OneMinusSrc1,
|
||||
#[webidl(rename = "src1-alpha")]
|
||||
Src1Alpha,
|
||||
#[webidl(rename = "one-minus-src1-alpha")]
|
||||
OneMinusSrc1Alpha,
|
||||
}
|
||||
|
||||
impl From<GPUBlendFactor> for wgpu_types::BlendFactor {
|
||||
fn from(value: GPUBlendFactor) -> Self {
|
||||
match value {
|
||||
GPUBlendFactor::Zero => Self::Zero,
|
||||
GPUBlendFactor::One => Self::One,
|
||||
GPUBlendFactor::Src => Self::Src,
|
||||
GPUBlendFactor::OneMinusSrc => Self::OneMinusSrc,
|
||||
GPUBlendFactor::SrcAlpha => Self::SrcAlpha,
|
||||
GPUBlendFactor::OneMinusSrcAlpha => Self::OneMinusSrcAlpha,
|
||||
GPUBlendFactor::Dst => Self::Dst,
|
||||
GPUBlendFactor::OneMinusDst => Self::OneMinusDst,
|
||||
GPUBlendFactor::DstAlpha => Self::DstAlpha,
|
||||
GPUBlendFactor::OneMinusDstAlpha => Self::OneMinusDstAlpha,
|
||||
GPUBlendFactor::SrcAlphaSaturated => Self::SrcAlphaSaturated,
|
||||
GPUBlendFactor::Constant => Self::Constant,
|
||||
GPUBlendFactor::OneMinusConstant => Self::OneMinusConstant,
|
||||
GPUBlendFactor::Src1 => Self::Src1,
|
||||
GPUBlendFactor::OneMinusSrc1 => Self::OneMinusSrc1,
|
||||
GPUBlendFactor::Src1Alpha => Self::Src1Alpha,
|
||||
GPUBlendFactor::OneMinusSrc1Alpha => Self::OneMinusSrc1Alpha,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUPrimitiveState {
|
||||
#[webidl(default = GPUPrimitiveTopology::TriangleList)]
|
||||
pub topology: GPUPrimitiveTopology,
|
||||
pub strip_index_format: Option<GPUIndexFormat>,
|
||||
#[webidl(default = GPUFrontFace::Ccw)]
|
||||
pub front_face: GPUFrontFace,
|
||||
#[webidl(default = GPUCullMode::None)]
|
||||
pub cull_mode: GPUCullMode,
|
||||
#[webidl(default = false)]
|
||||
pub unclipped_depth: bool,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUPrimitiveTopology {
|
||||
PointList,
|
||||
LineList,
|
||||
LineStrip,
|
||||
TriangleList,
|
||||
TriangleStrip,
|
||||
}
|
||||
|
||||
impl From<GPUPrimitiveTopology> for wgpu_types::PrimitiveTopology {
|
||||
fn from(value: GPUPrimitiveTopology) -> Self {
|
||||
match value {
|
||||
GPUPrimitiveTopology::PointList => Self::PointList,
|
||||
GPUPrimitiveTopology::LineList => Self::LineList,
|
||||
GPUPrimitiveTopology::LineStrip => Self::LineStrip,
|
||||
GPUPrimitiveTopology::TriangleList => Self::TriangleList,
|
||||
GPUPrimitiveTopology::TriangleStrip => Self::TriangleStrip,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUIndexFormat {
|
||||
#[webidl(rename = "uint16")]
|
||||
Uint16,
|
||||
#[webidl(rename = "uint32")]
|
||||
Uint32,
|
||||
}
|
||||
|
||||
impl From<GPUIndexFormat> for wgpu_types::IndexFormat {
|
||||
fn from(value: GPUIndexFormat) -> Self {
|
||||
match value {
|
||||
GPUIndexFormat::Uint16 => Self::Uint16,
|
||||
GPUIndexFormat::Uint32 => Self::Uint32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUFrontFace {
|
||||
Ccw,
|
||||
Cw,
|
||||
}
|
||||
|
||||
impl From<GPUFrontFace> for wgpu_types::FrontFace {
|
||||
fn from(value: GPUFrontFace) -> Self {
|
||||
match value {
|
||||
GPUFrontFace::Ccw => Self::Ccw,
|
||||
GPUFrontFace::Cw => Self::Cw,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUCullMode {
|
||||
None,
|
||||
Front,
|
||||
Back,
|
||||
}
|
||||
|
||||
impl From<GPUCullMode> for Option<wgpu_types::Face> {
|
||||
fn from(value: GPUCullMode) -> Self {
|
||||
match value {
|
||||
GPUCullMode::None => None,
|
||||
GPUCullMode::Front => Some(wgpu_types::Face::Front),
|
||||
GPUCullMode::Back => Some(wgpu_types::Face::Back),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUVertexBufferLayout {
|
||||
#[options(enforce_range = true)]
|
||||
pub array_stride: u64,
|
||||
#[webidl(default = GPUVertexStepMode::Vertex)]
|
||||
pub step_mode: GPUVertexStepMode,
|
||||
pub attributes: Vec<GPUVertexAttribute>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUVertexStepMode {
|
||||
Vertex,
|
||||
Instance,
|
||||
}
|
||||
|
||||
impl From<GPUVertexStepMode> for wgpu_types::VertexStepMode {
|
||||
fn from(value: GPUVertexStepMode) -> Self {
|
||||
match value {
|
||||
GPUVertexStepMode::Vertex => Self::Vertex,
|
||||
GPUVertexStepMode::Instance => Self::Instance,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUVertexAttribute {
|
||||
pub format: GPUVertexFormat,
|
||||
#[options(enforce_range = true)]
|
||||
pub offset: u64,
|
||||
#[options(enforce_range = true)]
|
||||
pub shader_location: u32,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUVertexFormat {
|
||||
// #[webidl(rename = "uint8")]
|
||||
// Uint8,
|
||||
#[webidl(rename = "uint8x2")]
|
||||
Uint8x2,
|
||||
#[webidl(rename = "uint8x4")]
|
||||
Uint8x4,
|
||||
// #[webidl(rename = "sint8")]
|
||||
// Sint8,
|
||||
#[webidl(rename = "sint8x2")]
|
||||
Sint8x2,
|
||||
#[webidl(rename = "sint8x4")]
|
||||
Sint8x4,
|
||||
// #[webidl(rename = "unorm8")]
|
||||
// Unorm8,
|
||||
#[webidl(rename = "unorm8x2")]
|
||||
Unorm8x2,
|
||||
#[webidl(rename = "unorm8x4")]
|
||||
Unorm8x4,
|
||||
// #[webidl(rename = "snorm8")]
|
||||
// Snorm8,
|
||||
#[webidl(rename = "snorm8x2")]
|
||||
Snorm8x2,
|
||||
#[webidl(rename = "snorm8x4")]
|
||||
Snorm8x4,
|
||||
// #[webidl(rename = "uint16")]
|
||||
// Uint16,
|
||||
#[webidl(rename = "uint16x2")]
|
||||
Uint16x2,
|
||||
#[webidl(rename = "uint16x4")]
|
||||
Uint16x4,
|
||||
// #[webidl(rename = "sint16")]
|
||||
// Sint16,
|
||||
#[webidl(rename = "sint16x2")]
|
||||
Sint16x2,
|
||||
#[webidl(rename = "sint16x4")]
|
||||
Sint16x4,
|
||||
// #[webidl(rename = "unorm16")]
|
||||
// Unorm16,
|
||||
#[webidl(rename = "unorm16x2")]
|
||||
Unorm16x2,
|
||||
#[webidl(rename = "unorm16x4")]
|
||||
Unorm16x4,
|
||||
// #[webidl(rename = "snorm16")]
|
||||
// Snorm16,
|
||||
#[webidl(rename = "snorm16x2")]
|
||||
Snorm16x2,
|
||||
#[webidl(rename = "snorm16x4")]
|
||||
Snorm16x4,
|
||||
// #[webidl(rename = "float16")]
|
||||
// Float16,
|
||||
#[webidl(rename = "float16x2")]
|
||||
Float16x2,
|
||||
#[webidl(rename = "float16x4")]
|
||||
Float16x4,
|
||||
#[webidl(rename = "float32")]
|
||||
Float32,
|
||||
#[webidl(rename = "float32x2")]
|
||||
Float32x2,
|
||||
#[webidl(rename = "float32x3")]
|
||||
Float32x3,
|
||||
#[webidl(rename = "float32x4")]
|
||||
Float32x4,
|
||||
#[webidl(rename = "uint32")]
|
||||
Uint32,
|
||||
#[webidl(rename = "uint32x2")]
|
||||
Uint32x2,
|
||||
#[webidl(rename = "uint32x3")]
|
||||
Uint32x3,
|
||||
#[webidl(rename = "uint32x4")]
|
||||
Uint32x4,
|
||||
#[webidl(rename = "sint32")]
|
||||
Sint32,
|
||||
#[webidl(rename = "sint32x2")]
|
||||
Sint32x2,
|
||||
#[webidl(rename = "sint32x3")]
|
||||
Sint32x3,
|
||||
#[webidl(rename = "sint32x4")]
|
||||
Sint32x4,
|
||||
#[webidl(rename = "unorm10-10-10-2")]
|
||||
Unorm1010102,
|
||||
// #[webidl(rename = "unorm8x4-bgra")]
|
||||
// Unorm8x4Bgra,
|
||||
}
|
||||
|
||||
impl From<GPUVertexFormat> for wgpu_types::VertexFormat {
|
||||
fn from(value: GPUVertexFormat) -> Self {
|
||||
match value {
|
||||
//GPUVertexFormat::Uint8 => Self::Uint8,
|
||||
GPUVertexFormat::Uint8x2 => Self::Uint8x2,
|
||||
GPUVertexFormat::Uint8x4 => Self::Uint8x4,
|
||||
//GPUVertexFormat::Sint8 => Self::Sint8,
|
||||
GPUVertexFormat::Sint8x2 => Self::Sint8x2,
|
||||
GPUVertexFormat::Sint8x4 => Self::Sint8x4,
|
||||
//GPUVertexFormat::Unorm8 => Self::Unorm8,
|
||||
GPUVertexFormat::Unorm8x2 => Self::Unorm8x2,
|
||||
GPUVertexFormat::Unorm8x4 => Self::Unorm8x4,
|
||||
//GPUVertexFormat::Snorm8 => Self::Snorm8,
|
||||
GPUVertexFormat::Snorm8x2 => Self::Snorm8x2,
|
||||
GPUVertexFormat::Snorm8x4 => Self::Snorm8x4,
|
||||
//GPUVertexFormat::Uint16 => Self::Uint16,
|
||||
GPUVertexFormat::Uint16x2 => Self::Uint16x2,
|
||||
GPUVertexFormat::Uint16x4 => Self::Uint16x4,
|
||||
//GPUVertexFormat::Sint16 => Self::Sint16,
|
||||
GPUVertexFormat::Sint16x2 => Self::Sint16x2,
|
||||
GPUVertexFormat::Sint16x4 => Self::Sint16x4,
|
||||
//GPUVertexFormat::Unorm16 => Self::Unorm16,
|
||||
GPUVertexFormat::Unorm16x2 => Self::Unorm16x2,
|
||||
GPUVertexFormat::Unorm16x4 => Self::Unorm16x4,
|
||||
//GPUVertexFormat::Snorm16 => Self::Snorm16,
|
||||
GPUVertexFormat::Snorm16x2 => Self::Snorm16x2,
|
||||
GPUVertexFormat::Snorm16x4 => Self::Snorm16x4,
|
||||
//GPUVertexFormat::Float16 => Self::Float16,
|
||||
GPUVertexFormat::Float16x2 => Self::Float16x2,
|
||||
GPUVertexFormat::Float16x4 => Self::Float16x4,
|
||||
GPUVertexFormat::Float32 => Self::Float32,
|
||||
GPUVertexFormat::Float32x2 => Self::Float32x2,
|
||||
GPUVertexFormat::Float32x3 => Self::Float32x3,
|
||||
GPUVertexFormat::Float32x4 => Self::Float32x4,
|
||||
GPUVertexFormat::Uint32 => Self::Uint32,
|
||||
GPUVertexFormat::Uint32x2 => Self::Uint32x2,
|
||||
GPUVertexFormat::Uint32x3 => Self::Uint32x3,
|
||||
GPUVertexFormat::Uint32x4 => Self::Uint32x4,
|
||||
GPUVertexFormat::Sint32 => Self::Sint32,
|
||||
GPUVertexFormat::Sint32x2 => Self::Sint32x2,
|
||||
GPUVertexFormat::Sint32x3 => Self::Sint32x3,
|
||||
GPUVertexFormat::Sint32x4 => Self::Sint32x4,
|
||||
GPUVertexFormat::Unorm1010102 => Self::Unorm10_10_10_2,
|
||||
//GPUVertexFormat::Unorm8x4Bgra => Self::Unorm8x4Bgra,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,78 +1,134 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use crate::Instance;
|
||||
|
||||
pub(crate) struct WebGpuSampler(
|
||||
pub(crate) crate::Instance,
|
||||
pub(crate) wgpu_core::id::SamplerId,
|
||||
);
|
||||
impl Resource for WebGpuSampler {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUSampler".into()
|
||||
}
|
||||
pub struct GPUSampler {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::SamplerId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.sampler_drop(self.1);
|
||||
impl Drop for GPUSampler {
|
||||
fn drop(&mut self) {
|
||||
self.instance.sampler_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateSamplerArgs {
|
||||
device_rid: ResourceId,
|
||||
label: String,
|
||||
address_mode_u: wgpu_types::AddressMode,
|
||||
address_mode_v: wgpu_types::AddressMode,
|
||||
address_mode_w: wgpu_types::AddressMode,
|
||||
mag_filter: wgpu_types::FilterMode,
|
||||
min_filter: wgpu_types::FilterMode,
|
||||
mipmap_filter: wgpu_types::FilterMode, // TODO: GPUMipmapFilterMode
|
||||
lod_min_clamp: f32,
|
||||
lod_max_clamp: f32,
|
||||
compare: Option<wgpu_types::CompareFunction>,
|
||||
max_anisotropy: u16,
|
||||
impl WebIdlInterfaceConverter for GPUSampler {
|
||||
const NAME: &'static str = "GPUSampler";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUSampler {}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_sampler(
|
||||
state: &mut OpState,
|
||||
#[serde] args: CreateSamplerArgs,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(args.device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let descriptor = wgpu_core::resource::SamplerDescriptor {
|
||||
label: Some(Cow::Owned(args.label)),
|
||||
address_modes: [
|
||||
args.address_mode_u,
|
||||
args.address_mode_v,
|
||||
args.address_mode_w,
|
||||
],
|
||||
mag_filter: args.mag_filter,
|
||||
min_filter: args.min_filter,
|
||||
mipmap_filter: args.mipmap_filter,
|
||||
lod_min_clamp: args.lod_min_clamp,
|
||||
lod_max_clamp: args.lod_max_clamp,
|
||||
compare: args.compare,
|
||||
anisotropy_clamp: args.max_anisotropy,
|
||||
border_color: None, // native-only
|
||||
};
|
||||
|
||||
gfx_put!(instance.device_create_sampler(
|
||||
device,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, WebGpuSampler)
|
||||
impl GPUSampler {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(super) struct GPUSamplerDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
#[webidl(default = GPUAddressMode::ClampToEdge)]
|
||||
pub address_mode_u: GPUAddressMode,
|
||||
#[webidl(default = GPUAddressMode::ClampToEdge)]
|
||||
pub address_mode_v: GPUAddressMode,
|
||||
#[webidl(default = GPUAddressMode::ClampToEdge)]
|
||||
pub address_mode_w: GPUAddressMode,
|
||||
#[webidl(default = GPUFilterMode::Nearest)]
|
||||
pub mag_filter: GPUFilterMode,
|
||||
#[webidl(default = GPUFilterMode::Nearest)]
|
||||
pub min_filter: GPUFilterMode,
|
||||
#[webidl(default = GPUFilterMode::Nearest)]
|
||||
pub mipmap_filter: GPUFilterMode,
|
||||
|
||||
#[webidl(default = 0.0)]
|
||||
pub lod_min_clamp: f32,
|
||||
#[webidl(default = 32.0)]
|
||||
pub lod_max_clamp: f32,
|
||||
|
||||
pub compare: Option<GPUCompareFunction>,
|
||||
|
||||
#[webidl(default = 1)]
|
||||
#[options(clamp = true)]
|
||||
pub max_anisotropy: u16,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUAddressMode {
|
||||
ClampToEdge,
|
||||
Repeat,
|
||||
MirrorRepeat,
|
||||
}
|
||||
|
||||
impl From<GPUAddressMode> for wgpu_types::AddressMode {
|
||||
fn from(value: GPUAddressMode) -> Self {
|
||||
match value {
|
||||
GPUAddressMode::ClampToEdge => Self::ClampToEdge,
|
||||
GPUAddressMode::Repeat => Self::Repeat,
|
||||
GPUAddressMode::MirrorRepeat => Self::MirrorRepeat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Same as GPUMipmapFilterMode
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUFilterMode {
|
||||
Nearest,
|
||||
Linear,
|
||||
}
|
||||
|
||||
impl From<GPUFilterMode> for wgpu_types::FilterMode {
|
||||
fn from(value: GPUFilterMode) -> Self {
|
||||
match value {
|
||||
GPUFilterMode::Nearest => Self::Nearest,
|
||||
GPUFilterMode::Linear => Self::Linear,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUCompareFunction {
|
||||
Never,
|
||||
Less,
|
||||
Equal,
|
||||
LessEqual,
|
||||
Greater,
|
||||
NotEqual,
|
||||
GreaterEqual,
|
||||
Always,
|
||||
}
|
||||
|
||||
impl From<GPUCompareFunction> for wgpu_types::CompareFunction {
|
||||
fn from(value: GPUCompareFunction) -> Self {
|
||||
match value {
|
||||
GPUCompareFunction::Never => Self::Never,
|
||||
GPUCompareFunction::Less => Self::Less,
|
||||
GPUCompareFunction::Equal => Self::Equal,
|
||||
GPUCompareFunction::LessEqual => Self::LessEqual,
|
||||
GPUCompareFunction::Greater => Self::Greater,
|
||||
GPUCompareFunction::NotEqual => Self::NotEqual,
|
||||
GPUCompareFunction::GreaterEqual => Self::GreaterEqual,
|
||||
GPUCompareFunction::Always => Self::Always,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +1,49 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
use crate::Instance;
|
||||
|
||||
pub(crate) struct WebGpuShaderModule(
|
||||
pub(crate) super::Instance,
|
||||
pub(crate) wgpu_core::id::ShaderModuleId,
|
||||
);
|
||||
impl Resource for WebGpuShaderModule {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUShaderModule".into()
|
||||
}
|
||||
pub struct GPUShaderModule {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::ShaderModuleId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.shader_module_drop(self.1);
|
||||
impl Drop for GPUShaderModule {
|
||||
fn drop(&mut self) {
|
||||
self.instance.shader_module_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPUShaderModule {
|
||||
const NAME: &'static str = "GPUShaderModule";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUShaderModule {}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_shader_module(
|
||||
state: &mut OpState,
|
||||
#[smi] device_rid: ResourceId,
|
||||
#[string] label: Cow<str>,
|
||||
#[string] code: Cow<str>,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let source = wgpu_core::pipeline::ShaderModuleSource::Wgsl(code);
|
||||
|
||||
let descriptor = wgpu_core::pipeline::ShaderModuleDescriptor {
|
||||
label: Some(label),
|
||||
runtime_checks: wgpu_types::ShaderRuntimeChecks::default(),
|
||||
};
|
||||
|
||||
gfx_put!(instance.device_create_shader_module(
|
||||
device,
|
||||
&descriptor,
|
||||
source,
|
||||
None
|
||||
) => state, WebGpuShaderModule)
|
||||
impl GPUShaderModule {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUShaderModuleDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub code: String,
|
||||
}
|
||||
|
@ -1,128 +1,229 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
use super::WebGpuResult;
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use deno_core::v8;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use deno_core::_ops::make_cppgc_object;
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_error::JsErrorBox;
|
||||
use wgpu_types::SurfaceStatus;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
use crate::device::GPUDevice;
|
||||
use crate::texture::GPUTexture;
|
||||
use crate::texture::GPUTextureFormat;
|
||||
|
||||
#[derive(Debug, thiserror::Error, deno_error::JsError)]
|
||||
pub enum SurfaceError {
|
||||
#[error(transparent)]
|
||||
Resource(deno_core::error::AnyError),
|
||||
#[class("DOMExceptionInvalidStateError")]
|
||||
#[error("Context is not configured")]
|
||||
UnconfiguredContext,
|
||||
#[class(generic)]
|
||||
#[error("Invalid Surface Status")]
|
||||
InvalidStatus,
|
||||
#[class(generic)]
|
||||
#[error(transparent)]
|
||||
Surface(wgpu_core::present::SurfaceError),
|
||||
Surface(#[from] wgpu_core::present::SurfaceError),
|
||||
}
|
||||
|
||||
pub struct WebGpuSurface(pub crate::Instance, pub wgpu_core::id::SurfaceId);
|
||||
impl Resource for WebGpuSurface {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUSurface".into()
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.surface_drop(self.1);
|
||||
}
|
||||
pub struct Configuration {
|
||||
pub device: Ptr<GPUDevice>,
|
||||
pub usage: u32,
|
||||
pub format: GPUTextureFormat,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SurfaceConfigureArgs {
|
||||
surface_rid: ResourceId,
|
||||
device_rid: ResourceId,
|
||||
format: wgpu_types::TextureFormat,
|
||||
usage: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
present_mode: Option<wgpu_types::PresentMode>,
|
||||
alpha_mode: wgpu_types::CompositeAlphaMode,
|
||||
view_formats: Vec<wgpu_types::TextureFormat>,
|
||||
pub struct GPUCanvasContext {
|
||||
pub surface_id: wgpu_core::id::SurfaceId,
|
||||
pub width: RefCell<u32>,
|
||||
pub height: RefCell<u32>,
|
||||
|
||||
pub config: RefCell<Option<Configuration>>,
|
||||
pub texture: RefCell<Option<v8::Global<v8::Object>>>,
|
||||
|
||||
pub canvas: v8::Global<v8::Object>,
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUCanvasContext {}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_surface_configure(
|
||||
state: &mut OpState,
|
||||
#[serde] args: SurfaceConfigureArgs,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(args.device_rid)?;
|
||||
let device = device_resource.1;
|
||||
let surface_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuSurface>(args.surface_rid)?;
|
||||
let surface = surface_resource.1;
|
||||
impl GPUCanvasContext {
|
||||
#[getter]
|
||||
#[global]
|
||||
fn canvas(&self) -> v8::Global<v8::Object> {
|
||||
self.canvas.clone()
|
||||
}
|
||||
|
||||
let conf = wgpu_types::SurfaceConfiguration::<Vec<wgpu_types::TextureFormat>> {
|
||||
usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
|
||||
format: args.format,
|
||||
width: args.width,
|
||||
height: args.height,
|
||||
present_mode: args.present_mode.unwrap_or_default(),
|
||||
alpha_mode: args.alpha_mode,
|
||||
view_formats: args.view_formats,
|
||||
desired_maximum_frame_latency: 2,
|
||||
};
|
||||
fn configure(&self, #[webidl] configuration: GPUCanvasConfiguration) -> Result<(), JsErrorBox> {
|
||||
let usage = wgpu_types::TextureUsages::from_bits(configuration.usage)
|
||||
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?;
|
||||
let format = configuration.format.clone().into();
|
||||
let conf = wgpu_types::SurfaceConfiguration {
|
||||
usage,
|
||||
format,
|
||||
width: *self.width.borrow(),
|
||||
height: *self.height.borrow(),
|
||||
present_mode: configuration
|
||||
.present_mode
|
||||
.map(Into::into)
|
||||
.unwrap_or_default(),
|
||||
alpha_mode: configuration.alpha_mode.into(),
|
||||
view_formats: configuration
|
||||
.view_formats
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect(),
|
||||
desired_maximum_frame_latency: 2,
|
||||
};
|
||||
|
||||
let err = instance.surface_configure(surface, device, &conf);
|
||||
let device = configuration.device;
|
||||
|
||||
Ok(WebGpuResult::maybe_err(err))
|
||||
}
|
||||
let err = device
|
||||
.instance
|
||||
.surface_configure(self.surface_id, device.id, &conf);
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_surface_get_current_texture(
|
||||
state: &mut OpState,
|
||||
#[smi] _device_rid: ResourceId,
|
||||
#[smi] surface_rid: ResourceId,
|
||||
) -> Result<WebGpuResult, SurfaceError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let surface_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuSurface>(surface_rid)
|
||||
.map_err(SurfaceError::Resource)?;
|
||||
let surface = surface_resource.1;
|
||||
device.error_handler.push_error(err);
|
||||
|
||||
let output = instance
|
||||
.surface_get_current_texture(surface, None)
|
||||
.map_err(SurfaceError::Surface)?;
|
||||
self.config.borrow_mut().replace(Configuration {
|
||||
device,
|
||||
usage: configuration.usage,
|
||||
format: configuration.format,
|
||||
});
|
||||
|
||||
match output.status {
|
||||
wgpu_types::SurfaceStatus::Good | wgpu_types::SurfaceStatus::Suboptimal => {
|
||||
let id = output.texture.unwrap();
|
||||
let rid = state.resource_table.add(crate::texture::WebGpuTexture {
|
||||
instance: instance.clone(),
|
||||
id,
|
||||
});
|
||||
Ok(WebGpuResult::rid(rid))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[fast]
|
||||
fn unconfigure(&self) {
|
||||
*self.config.borrow_mut() = None;
|
||||
}
|
||||
|
||||
#[global]
|
||||
fn get_current_texture(
|
||||
&self,
|
||||
scope: &mut v8::HandleScope,
|
||||
) -> Result<v8::Global<v8::Object>, SurfaceError> {
|
||||
let config = self.config.borrow();
|
||||
let Some(config) = config.as_ref() else {
|
||||
return Err(SurfaceError::UnconfiguredContext);
|
||||
};
|
||||
|
||||
{
|
||||
if let Some(obj) = self.texture.borrow().as_ref() {
|
||||
return Ok(obj.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let output = config
|
||||
.device
|
||||
.instance
|
||||
.surface_get_current_texture(self.surface_id, None)?;
|
||||
|
||||
match output.status {
|
||||
SurfaceStatus::Good | SurfaceStatus::Suboptimal => {
|
||||
let id = output.texture.unwrap();
|
||||
|
||||
let texture = GPUTexture {
|
||||
instance: config.device.instance.clone(),
|
||||
error_handler: config.device.error_handler.clone(),
|
||||
id,
|
||||
label: "".to_string(),
|
||||
size: wgpu_types::Extent3d {
|
||||
width: *self.width.borrow(),
|
||||
height: *self.height.borrow(),
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 0,
|
||||
sample_count: 0,
|
||||
dimension: crate::texture::GPUTextureDimension::D2,
|
||||
format: config.format.clone(),
|
||||
usage: config.usage,
|
||||
};
|
||||
let obj = make_cppgc_object(scope, texture);
|
||||
let obj = v8::Global::new(scope, obj);
|
||||
*self.texture.borrow_mut() = Some(obj.clone());
|
||||
|
||||
Ok(obj)
|
||||
}
|
||||
_ => Err(SurfaceError::InvalidStatus),
|
||||
}
|
||||
_ => Err(SurfaceError::InvalidStatus),
|
||||
}
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
pub fn op_webgpu_surface_present(
|
||||
state: &mut OpState,
|
||||
#[smi] _device_rid: ResourceId,
|
||||
#[smi] surface_rid: ResourceId,
|
||||
) -> Result<(), SurfaceError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let surface_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuSurface>(surface_rid)
|
||||
.map_err(SurfaceError::Resource)?;
|
||||
let surface = surface_resource.1;
|
||||
impl GPUCanvasContext {
|
||||
pub fn present(&self) -> Result<(), SurfaceError> {
|
||||
let config = self.config.borrow();
|
||||
let Some(config) = config.as_ref() else {
|
||||
return Err(SurfaceError::UnconfiguredContext);
|
||||
};
|
||||
|
||||
instance
|
||||
.surface_present(surface)
|
||||
.map_err(SurfaceError::Surface)?;
|
||||
config.device.instance.surface_present(self.surface_id)?;
|
||||
|
||||
Ok(())
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
struct GPUCanvasConfiguration {
|
||||
device: Ptr<GPUDevice>,
|
||||
format: GPUTextureFormat,
|
||||
#[webidl(default = wgpu_types::TextureUsages::RENDER_ATTACHMENT.bits())]
|
||||
#[options(enforce_range = true)]
|
||||
usage: u32,
|
||||
#[webidl(default = GPUCanvasAlphaMode::Opaque)]
|
||||
alpha_mode: GPUCanvasAlphaMode,
|
||||
|
||||
// Extended from spec
|
||||
present_mode: Option<GPUPresentMode>,
|
||||
#[webidl(default = vec![])]
|
||||
view_formats: Vec<GPUTextureFormat>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
enum GPUCanvasAlphaMode {
|
||||
Opaque,
|
||||
Premultiplied,
|
||||
}
|
||||
|
||||
impl From<GPUCanvasAlphaMode> for wgpu_types::CompositeAlphaMode {
|
||||
fn from(value: GPUCanvasAlphaMode) -> Self {
|
||||
match value {
|
||||
GPUCanvasAlphaMode::Opaque => Self::Opaque,
|
||||
GPUCanvasAlphaMode::Premultiplied => Self::PreMultiplied,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extended from spec
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
enum GPUPresentMode {
|
||||
#[webidl(rename = "autoVsync")]
|
||||
AutoVsync,
|
||||
#[webidl(rename = "autoNoVsync")]
|
||||
AutoNoVsync,
|
||||
#[webidl(rename = "fifo")]
|
||||
Fifo,
|
||||
#[webidl(rename = "fifoRelaxed")]
|
||||
FifoRelaxed,
|
||||
#[webidl(rename = "immediate")]
|
||||
Immediate,
|
||||
#[webidl(rename = "mailbox")]
|
||||
Mailbox,
|
||||
}
|
||||
|
||||
impl From<GPUPresentMode> for wgpu_types::PresentMode {
|
||||
fn from(value: GPUPresentMode) -> Self {
|
||||
match value {
|
||||
GPUPresentMode::AutoVsync => Self::AutoVsync,
|
||||
GPUPresentMode::AutoNoVsync => Self::AutoNoVsync,
|
||||
GPUPresentMode::Fifo => Self::Fifo,
|
||||
GPUPresentMode::FifoRelaxed => Self::FifoRelaxed,
|
||||
GPUPresentMode::Immediate => Self::Immediate,
|
||||
GPUPresentMode::Mailbox => Self::Mailbox,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,125 +1,664 @@
|
||||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use deno_core::op2;
|
||||
use deno_core::OpState;
|
||||
use deno_core::Resource;
|
||||
use deno_core::ResourceId;
|
||||
use serde::Deserialize;
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
use deno_core::webidl::WebIdlInterfaceConverter;
|
||||
use deno_core::GarbageCollected;
|
||||
use deno_core::WebIDL;
|
||||
use deno_error::JsErrorBox;
|
||||
use wgpu_types::AstcBlock;
|
||||
use wgpu_types::AstcChannel;
|
||||
use wgpu_types::Extent3d;
|
||||
use wgpu_types::TextureAspect;
|
||||
use wgpu_types::TextureDimension;
|
||||
use wgpu_types::TextureFormat;
|
||||
use wgpu_types::TextureViewDimension;
|
||||
|
||||
use super::error::WebGpuResult;
|
||||
pub(crate) struct WebGpuTexture {
|
||||
pub(crate) instance: crate::Instance,
|
||||
pub(crate) id: wgpu_core::id::TextureId,
|
||||
use crate::Instance;
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUTextureDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
pub label: String,
|
||||
|
||||
pub size: super::webidl::GPUExtent3D,
|
||||
#[webidl(default = 1)]
|
||||
#[options(enforce_range = true)]
|
||||
pub mip_level_count: u32,
|
||||
#[webidl(default = 1)]
|
||||
#[options(enforce_range = true)]
|
||||
pub sample_count: u32,
|
||||
#[webidl(default = GPUTextureDimension::D2)]
|
||||
pub dimension: GPUTextureDimension,
|
||||
pub format: GPUTextureFormat,
|
||||
#[options(enforce_range = true)]
|
||||
pub usage: u32,
|
||||
#[webidl(default = vec![])]
|
||||
pub view_formats: Vec<GPUTextureFormat>,
|
||||
}
|
||||
|
||||
impl Resource for WebGpuTexture {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUTexture".into()
|
||||
}
|
||||
pub struct GPUTexture {
|
||||
pub instance: Instance,
|
||||
pub error_handler: super::error::ErrorHandler,
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
let instance = &self.instance;
|
||||
instance.texture_drop(self.id);
|
||||
pub id: wgpu_core::id::TextureId,
|
||||
|
||||
pub label: String,
|
||||
|
||||
pub size: Extent3d,
|
||||
pub mip_level_count: u32,
|
||||
pub sample_count: u32,
|
||||
pub dimension: GPUTextureDimension,
|
||||
pub format: GPUTextureFormat,
|
||||
pub usage: u32,
|
||||
}
|
||||
|
||||
impl Drop for GPUTexture {
|
||||
fn drop(&mut self) {
|
||||
self.instance.texture_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct WebGpuTextureView(
|
||||
pub(crate) crate::Instance,
|
||||
pub(crate) wgpu_core::id::TextureViewId,
|
||||
);
|
||||
impl Resource for WebGpuTextureView {
|
||||
fn name(&self) -> Cow<str> {
|
||||
"webGPUTextureView".into()
|
||||
impl WebIdlInterfaceConverter for GPUTexture {
|
||||
const NAME: &'static str = "GPUTexture";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUTexture {}
|
||||
|
||||
#[op2]
|
||||
impl GPUTexture {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
|
||||
fn close(self: Rc<Self>) {
|
||||
self.0.texture_view_drop(self.1).unwrap();
|
||||
#[getter]
|
||||
fn width(&self) -> u32 {
|
||||
self.size.width
|
||||
}
|
||||
#[getter]
|
||||
fn height(&self) -> u32 {
|
||||
self.size.height
|
||||
}
|
||||
#[getter]
|
||||
fn depth_or_array_layers(&self) -> u32 {
|
||||
self.size.depth_or_array_layers
|
||||
}
|
||||
#[getter]
|
||||
fn mip_level_count(&self) -> u32 {
|
||||
self.mip_level_count
|
||||
}
|
||||
#[getter]
|
||||
fn sample_count(&self) -> u32 {
|
||||
self.sample_count
|
||||
}
|
||||
#[getter]
|
||||
#[string]
|
||||
fn dimension(&self) -> &'static str {
|
||||
self.dimension.as_str()
|
||||
}
|
||||
#[getter]
|
||||
#[string]
|
||||
fn format(&self) -> &'static str {
|
||||
self.format.as_str()
|
||||
}
|
||||
#[getter]
|
||||
fn usage(&self) -> u32 {
|
||||
self.usage
|
||||
}
|
||||
#[fast]
|
||||
fn destroy(&self) -> Result<(), JsErrorBox> {
|
||||
self.instance
|
||||
.texture_destroy(self.id)
|
||||
.map_err(|e| JsErrorBox::generic(e.to_string()))
|
||||
}
|
||||
|
||||
#[cppgc]
|
||||
fn create_view(
|
||||
&self,
|
||||
#[webidl] descriptor: GPUTextureViewDescriptor,
|
||||
) -> Result<GPUTextureView, JsErrorBox> {
|
||||
let wgpu_descriptor = wgpu_core::resource::TextureViewDescriptor {
|
||||
label: crate::transform_label(descriptor.label.clone()),
|
||||
format: descriptor.format.map(Into::into),
|
||||
dimension: descriptor.dimension.map(Into::into),
|
||||
usage: Some(
|
||||
wgpu_types::TextureUsages::from_bits(descriptor.usage)
|
||||
.ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?,
|
||||
),
|
||||
range: wgpu_types::ImageSubresourceRange {
|
||||
aspect: descriptor.aspect.into(),
|
||||
base_mip_level: descriptor.base_mip_level,
|
||||
mip_level_count: descriptor.mip_level_count,
|
||||
base_array_layer: descriptor.base_array_layer,
|
||||
array_layer_count: descriptor.array_layer_count,
|
||||
},
|
||||
};
|
||||
|
||||
let (id, err) = self
|
||||
.instance
|
||||
.texture_create_view(self.id, &wgpu_descriptor, None);
|
||||
|
||||
self.error_handler.push_error(err);
|
||||
|
||||
Ok(GPUTextureView {
|
||||
instance: self.instance.clone(),
|
||||
id,
|
||||
label: descriptor.label,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateTextureArgs {
|
||||
device_rid: ResourceId,
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
struct GPUTextureViewDescriptor {
|
||||
#[webidl(default = String::new())]
|
||||
label: String,
|
||||
size: wgpu_types::Extent3d,
|
||||
mip_level_count: u32,
|
||||
sample_count: u32,
|
||||
dimension: wgpu_types::TextureDimension,
|
||||
format: wgpu_types::TextureFormat,
|
||||
|
||||
format: Option<GPUTextureFormat>,
|
||||
dimension: Option<GPUTextureViewDimension>,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
usage: u32,
|
||||
view_formats: Vec<wgpu_types::TextureFormat>,
|
||||
#[webidl(default = GPUTextureAspect::All)]
|
||||
aspect: GPUTextureAspect,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
base_mip_level: u32,
|
||||
#[options(enforce_range = true)]
|
||||
mip_level_count: Option<u32>,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
base_array_layer: u32,
|
||||
#[options(enforce_range = true)]
|
||||
array_layer_count: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUTextureViewDimension {
|
||||
#[webidl(rename = "1d")]
|
||||
D1,
|
||||
#[webidl(rename = "2d")]
|
||||
D2,
|
||||
#[webidl(rename = "2d-array")]
|
||||
D2Array,
|
||||
#[webidl(rename = "cube")]
|
||||
Cube,
|
||||
#[webidl(rename = "cube-array")]
|
||||
CubeArray,
|
||||
#[webidl(rename = "3d")]
|
||||
D3,
|
||||
}
|
||||
|
||||
impl From<GPUTextureViewDimension> for TextureViewDimension {
|
||||
fn from(value: GPUTextureViewDimension) -> Self {
|
||||
match value {
|
||||
GPUTextureViewDimension::D1 => Self::D1,
|
||||
GPUTextureViewDimension::D2 => Self::D2,
|
||||
GPUTextureViewDimension::D3 => Self::D3,
|
||||
GPUTextureViewDimension::D2Array => Self::D2Array,
|
||||
GPUTextureViewDimension::Cube => Self::Cube,
|
||||
GPUTextureViewDimension::CubeArray => Self::CubeArray,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub enum GPUTextureAspect {
|
||||
All,
|
||||
StencilOnly,
|
||||
DepthOnly,
|
||||
}
|
||||
|
||||
impl From<GPUTextureAspect> for TextureAspect {
|
||||
fn from(value: GPUTextureAspect) -> Self {
|
||||
match value {
|
||||
GPUTextureAspect::All => Self::All,
|
||||
GPUTextureAspect::StencilOnly => Self::StencilOnly,
|
||||
GPUTextureAspect::DepthOnly => Self::DepthOnly,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GPUTextureView {
|
||||
pub instance: Instance,
|
||||
pub id: wgpu_core::id::TextureViewId,
|
||||
pub label: String,
|
||||
}
|
||||
|
||||
impl Drop for GPUTextureView {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.instance.texture_view_drop(self.id);
|
||||
}
|
||||
}
|
||||
|
||||
impl WebIdlInterfaceConverter for GPUTextureView {
|
||||
const NAME: &'static str = "GPUTextureView";
|
||||
}
|
||||
|
||||
impl GarbageCollected for GPUTextureView {}
|
||||
// TODO(@crowlKats): weakref in texture for view
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_texture(
|
||||
state: &mut OpState,
|
||||
#[serde] args: CreateTextureArgs,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let device_resource = state
|
||||
.resource_table
|
||||
.get::<super::WebGpuDevice>(args.device_rid)?;
|
||||
let device = device_resource.1;
|
||||
|
||||
let descriptor = wgpu_core::resource::TextureDescriptor {
|
||||
label: Some(Cow::Owned(args.label)),
|
||||
size: args.size,
|
||||
mip_level_count: args.mip_level_count,
|
||||
sample_count: args.sample_count,
|
||||
dimension: args.dimension,
|
||||
format: args.format,
|
||||
usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
|
||||
view_formats: args.view_formats,
|
||||
};
|
||||
|
||||
let (val, maybe_err) = instance.device_create_texture(device, &descriptor, None);
|
||||
|
||||
let rid = state.resource_table.add(WebGpuTexture {
|
||||
instance: instance.clone(),
|
||||
id: val,
|
||||
});
|
||||
|
||||
Ok(WebGpuResult::rid_err(rid, maybe_err))
|
||||
impl GPUTextureView {
|
||||
#[getter]
|
||||
#[string]
|
||||
fn label(&self) -> String {
|
||||
self.label.clone()
|
||||
}
|
||||
#[setter]
|
||||
#[string]
|
||||
fn label(&self, #[webidl] _label: String) {
|
||||
// TODO(@crowlKats): no-op, needs wpgu to implement changing the label
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CreateTextureViewArgs {
|
||||
texture_rid: ResourceId,
|
||||
label: String,
|
||||
format: Option<wgpu_types::TextureFormat>,
|
||||
dimension: Option<wgpu_types::TextureViewDimension>,
|
||||
#[serde(flatten)]
|
||||
range: wgpu_types::ImageSubresourceRange,
|
||||
#[derive(WebIDL, Clone)]
|
||||
#[webidl(enum)]
|
||||
pub enum GPUTextureDimension {
|
||||
#[webidl(rename = "1d")]
|
||||
D1,
|
||||
#[webidl(rename = "2d")]
|
||||
D2,
|
||||
#[webidl(rename = "3d")]
|
||||
D3,
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
pub fn op_webgpu_create_texture_view(
|
||||
state: &mut OpState,
|
||||
#[serde] args: CreateTextureViewArgs,
|
||||
) -> Result<WebGpuResult, deno_core::error::AnyError> {
|
||||
let instance = state.borrow::<super::Instance>();
|
||||
let texture_resource = state
|
||||
.resource_table
|
||||
.get::<WebGpuTexture>(args.texture_rid)?;
|
||||
let texture = texture_resource.id;
|
||||
|
||||
let descriptor = wgpu_core::resource::TextureViewDescriptor {
|
||||
label: Some(Cow::Owned(args.label)),
|
||||
format: args.format,
|
||||
dimension: args.dimension,
|
||||
range: args.range,
|
||||
usage: None, // FIXME: Obtain actual value from desc
|
||||
};
|
||||
|
||||
gfx_put!(instance.texture_create_view(
|
||||
texture,
|
||||
&descriptor,
|
||||
None
|
||||
) => state, WebGpuTextureView)
|
||||
impl From<GPUTextureDimension> for TextureDimension {
|
||||
fn from(value: GPUTextureDimension) -> Self {
|
||||
match value {
|
||||
GPUTextureDimension::D1 => Self::D1,
|
||||
GPUTextureDimension::D2 => Self::D2,
|
||||
GPUTextureDimension::D3 => Self::D3,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL, Clone)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUTextureFormat {
|
||||
#[webidl(rename = "r8unorm")]
|
||||
R8unorm,
|
||||
#[webidl(rename = "r8snorm")]
|
||||
R8snorm,
|
||||
#[webidl(rename = "r8uint")]
|
||||
R8uint,
|
||||
#[webidl(rename = "r8sint")]
|
||||
R8sint,
|
||||
#[webidl(rename = "r16uint")]
|
||||
R16uint,
|
||||
#[webidl(rename = "r16sint")]
|
||||
R16sint,
|
||||
#[webidl(rename = "r16float")]
|
||||
R16float,
|
||||
#[webidl(rename = "rg8unorm")]
|
||||
Rg8unorm,
|
||||
#[webidl(rename = "rg8snorm")]
|
||||
Rg8snorm,
|
||||
#[webidl(rename = "rg8uint")]
|
||||
Rg8uint,
|
||||
#[webidl(rename = "rg8sint")]
|
||||
Rg8sint,
|
||||
#[webidl(rename = "r32uint")]
|
||||
R32uint,
|
||||
#[webidl(rename = "r32sint")]
|
||||
R32sint,
|
||||
#[webidl(rename = "r32float")]
|
||||
R32float,
|
||||
#[webidl(rename = "rg16uint")]
|
||||
Rg16uint,
|
||||
#[webidl(rename = "rg16sint")]
|
||||
Rg16sint,
|
||||
#[webidl(rename = "rg16float")]
|
||||
Rg16float,
|
||||
#[webidl(rename = "rgba8unorm")]
|
||||
Rgba8unorm,
|
||||
#[webidl(rename = "rgba8unorm-srgb")]
|
||||
Rgba8unormSrgb,
|
||||
#[webidl(rename = "rgba8snorm")]
|
||||
Rgba8snorm,
|
||||
#[webidl(rename = "rgba8uint")]
|
||||
Rgba8uint,
|
||||
#[webidl(rename = "rgba8sint")]
|
||||
Rgba8sint,
|
||||
#[webidl(rename = "bgra8unorm")]
|
||||
Bgra8unorm,
|
||||
#[webidl(rename = "bgra8unorm-srgb")]
|
||||
Bgra8unormSrgb,
|
||||
#[webidl(rename = "rgb9e5ufloat")]
|
||||
Rgb9e5ufloat,
|
||||
#[webidl(rename = "rgb10a2uint")]
|
||||
Rgb10a2uint,
|
||||
#[webidl(rename = "rgb10a2unorm")]
|
||||
Rgb10a2unorm,
|
||||
#[webidl(rename = "rg11b10ufloat")]
|
||||
Rg11b10ufloat,
|
||||
#[webidl(rename = "rg32uint")]
|
||||
Rg32uint,
|
||||
#[webidl(rename = "rg32sint")]
|
||||
Rg32sint,
|
||||
#[webidl(rename = "rg32float")]
|
||||
Rg32float,
|
||||
#[webidl(rename = "rgba16uint")]
|
||||
Rgba16uint,
|
||||
#[webidl(rename = "rgba16sint")]
|
||||
Rgba16sint,
|
||||
#[webidl(rename = "rgba16float")]
|
||||
Rgba16float,
|
||||
#[webidl(rename = "rgba32uint")]
|
||||
Rgba32uint,
|
||||
#[webidl(rename = "rgba32sint")]
|
||||
Rgba32sint,
|
||||
#[webidl(rename = "rgba32float")]
|
||||
Rgba32float,
|
||||
#[webidl(rename = "stencil8")]
|
||||
Stencil8,
|
||||
#[webidl(rename = "depth16unorm")]
|
||||
Depth16unorm,
|
||||
#[webidl(rename = "depth24plus")]
|
||||
Depth24plus,
|
||||
#[webidl(rename = "depth24plus-stencil8")]
|
||||
Depth24plusStencil8,
|
||||
#[webidl(rename = "depth32float")]
|
||||
Depth32float,
|
||||
#[webidl(rename = "depth32float-stencil8")]
|
||||
Depth32floatStencil8,
|
||||
#[webidl(rename = "bc1-rgba-unorm")]
|
||||
Bc1RgbaUnorm,
|
||||
#[webidl(rename = "bc1-rgba-unorm-srgb")]
|
||||
Bc1RgbaUnormSrgb,
|
||||
#[webidl(rename = "bc2-rgba-unorm")]
|
||||
Bc2RgbaUnorm,
|
||||
#[webidl(rename = "bc2-rgba-unorm-srgb")]
|
||||
Bc2RgbaUnormSrgb,
|
||||
#[webidl(rename = "bc3-rgba-unorm")]
|
||||
Bc3RgbaUnorm,
|
||||
#[webidl(rename = "bc3-rgba-unorm-srgb")]
|
||||
Bc3RgbaUnormSrgb,
|
||||
#[webidl(rename = "bc4-r-unorm")]
|
||||
Bc4RUnorm,
|
||||
#[webidl(rename = "bc4-r-snorm")]
|
||||
Bc4RSnorm,
|
||||
#[webidl(rename = "bc5-rg-unorm")]
|
||||
Bc5RgUnorm,
|
||||
#[webidl(rename = "bc5-rg-snorm")]
|
||||
Bc5RgSnorm,
|
||||
#[webidl(rename = "bc6h-rgb-ufloat")]
|
||||
Bc6hRgbUfloat,
|
||||
#[webidl(rename = "bc6h-rgb-float")]
|
||||
Bc6hRgbFloat,
|
||||
#[webidl(rename = "bc7-rgba-unorm")]
|
||||
Bc7RgbaUnorm,
|
||||
#[webidl(rename = "bc7-rgba-unorm-srgb")]
|
||||
Bc7RgbaUnormSrgb,
|
||||
#[webidl(rename = "etc2-rgb8unorm")]
|
||||
Etc2Rgb8unorm,
|
||||
#[webidl(rename = "etc2-rgb8unorm-srgb")]
|
||||
Etc2Rgb8unormSrgb,
|
||||
#[webidl(rename = "etc2-rgb8a1unorm")]
|
||||
Etc2Rgb8a1unorm,
|
||||
#[webidl(rename = "etc2-rgb8a1unorm-srgb")]
|
||||
Etc2Rgb8a1unormSrgb,
|
||||
#[webidl(rename = "etc2-rgba8unorm")]
|
||||
Etc2Rgba8unorm,
|
||||
#[webidl(rename = "etc2-rgba8unorm-srgb")]
|
||||
Etc2Rgba8unormSrgb,
|
||||
#[webidl(rename = "eac-r11unorm")]
|
||||
EacR11unorm,
|
||||
#[webidl(rename = "eac-r11snorm")]
|
||||
EacR11snorm,
|
||||
#[webidl(rename = "eac-rg11unorm")]
|
||||
EacRg11unorm,
|
||||
#[webidl(rename = "eac-rg11snorm")]
|
||||
EacRg11snorm,
|
||||
#[webidl(rename = "astc-4x4-unorm")]
|
||||
Astc4x4Unorm,
|
||||
#[webidl(rename = "astc-4x4-unorm-srgb")]
|
||||
Astc4x4UnormSrgb,
|
||||
#[webidl(rename = "astc-5x4-unorm")]
|
||||
Astc5x4Unorm,
|
||||
#[webidl(rename = "astc-5x4-unorm-srgb")]
|
||||
Astc5x4UnormSrgb,
|
||||
#[webidl(rename = "astc-5x5-unorm")]
|
||||
Astc5x5Unorm,
|
||||
#[webidl(rename = "astc-5x5-unorm-srgb")]
|
||||
Astc5x5UnormSrgb,
|
||||
#[webidl(rename = "astc-6x5-unorm")]
|
||||
Astc6x5Unorm,
|
||||
#[webidl(rename = "astc-6x5-unorm-srgb")]
|
||||
Astc6x5UnormSrgb,
|
||||
#[webidl(rename = "astc-6x6-unorm")]
|
||||
Astc6x6Unorm,
|
||||
#[webidl(rename = "astc-6x6-unorm-srgb")]
|
||||
Astc6x6UnormSrgb,
|
||||
#[webidl(rename = "astc-8x5-unorm")]
|
||||
Astc8x5Unorm,
|
||||
#[webidl(rename = "astc-8x5-unorm-srgb")]
|
||||
Astc8x5UnormSrgb,
|
||||
#[webidl(rename = "astc-8x6-unorm")]
|
||||
Astc8x6Unorm,
|
||||
#[webidl(rename = "astc-8x6-unorm-srgb")]
|
||||
Astc8x6UnormSrgb,
|
||||
#[webidl(rename = "astc-8x8-unorm")]
|
||||
Astc8x8Unorm,
|
||||
#[webidl(rename = "astc-8x8-unorm-srgb")]
|
||||
Astc8x8UnormSrgb,
|
||||
#[webidl(rename = "astc-10x5-unorm")]
|
||||
Astc10x5Unorm,
|
||||
#[webidl(rename = "astc-10x5-unorm-srgb")]
|
||||
Astc10x5UnormSrgb,
|
||||
#[webidl(rename = "astc-10x6-unorm")]
|
||||
Astc10x6Unorm,
|
||||
#[webidl(rename = "astc-10x6-unorm-srgb")]
|
||||
Astc10x6UnormSrgb,
|
||||
#[webidl(rename = "astc-10x8-unorm")]
|
||||
Astc10x8Unorm,
|
||||
#[webidl(rename = "astc-10x8-unorm-srgb")]
|
||||
Astc10x8UnormSrgb,
|
||||
#[webidl(rename = "astc-10x10-unorm")]
|
||||
Astc10x10Unorm,
|
||||
#[webidl(rename = "astc-10x10-unorm-srgb")]
|
||||
Astc10x10UnormSrgb,
|
||||
#[webidl(rename = "astc-12x10-unorm")]
|
||||
Astc12x10Unorm,
|
||||
#[webidl(rename = "astc-12x10-unorm-srgb")]
|
||||
Astc12x10UnormSrgb,
|
||||
#[webidl(rename = "astc-12x12-unorm")]
|
||||
Astc12x12Unorm,
|
||||
#[webidl(rename = "astc-12x12-unorm-srgb")]
|
||||
Astc12x12UnormSrgb,
|
||||
}
|
||||
|
||||
impl From<GPUTextureFormat> for TextureFormat {
|
||||
fn from(value: GPUTextureFormat) -> Self {
|
||||
match value {
|
||||
GPUTextureFormat::R8unorm => Self::R8Unorm,
|
||||
GPUTextureFormat::R8snorm => Self::R8Snorm,
|
||||
GPUTextureFormat::R8uint => Self::R8Uint,
|
||||
GPUTextureFormat::R8sint => Self::R8Sint,
|
||||
GPUTextureFormat::R16uint => Self::R16Uint,
|
||||
GPUTextureFormat::R16sint => Self::R16Sint,
|
||||
GPUTextureFormat::R16float => Self::R16Float,
|
||||
GPUTextureFormat::Rg8unorm => Self::Rg8Unorm,
|
||||
GPUTextureFormat::Rg8snorm => Self::Rg8Snorm,
|
||||
GPUTextureFormat::Rg8uint => Self::Rg8Uint,
|
||||
GPUTextureFormat::Rg8sint => Self::Rg8Sint,
|
||||
GPUTextureFormat::R32uint => Self::R32Uint,
|
||||
GPUTextureFormat::R32sint => Self::R32Sint,
|
||||
GPUTextureFormat::R32float => Self::R32Float,
|
||||
GPUTextureFormat::Rg16uint => Self::Rg16Uint,
|
||||
GPUTextureFormat::Rg16sint => Self::Rg16Sint,
|
||||
GPUTextureFormat::Rg16float => Self::Rg16Float,
|
||||
GPUTextureFormat::Rgba8unorm => Self::Rgba8Unorm,
|
||||
GPUTextureFormat::Rgba8unormSrgb => Self::Rgba8UnormSrgb,
|
||||
GPUTextureFormat::Rgba8snorm => Self::Rgba8Snorm,
|
||||
GPUTextureFormat::Rgba8uint => Self::Rgba8Uint,
|
||||
GPUTextureFormat::Rgba8sint => Self::Rgba8Sint,
|
||||
GPUTextureFormat::Bgra8unorm => Self::Bgra8Unorm,
|
||||
GPUTextureFormat::Bgra8unormSrgb => Self::Bgra8UnormSrgb,
|
||||
GPUTextureFormat::Rgb9e5ufloat => Self::Rgb9e5Ufloat,
|
||||
GPUTextureFormat::Rgb10a2uint => Self::Rgb10a2Uint,
|
||||
GPUTextureFormat::Rgb10a2unorm => Self::Rgb10a2Unorm,
|
||||
GPUTextureFormat::Rg11b10ufloat => Self::Rg11b10Ufloat,
|
||||
GPUTextureFormat::Rg32uint => Self::Rg32Uint,
|
||||
GPUTextureFormat::Rg32sint => Self::Rg32Sint,
|
||||
GPUTextureFormat::Rg32float => Self::Rg32Float,
|
||||
GPUTextureFormat::Rgba16uint => Self::Rgba16Uint,
|
||||
GPUTextureFormat::Rgba16sint => Self::Rgba16Sint,
|
||||
GPUTextureFormat::Rgba16float => Self::Rgba16Float,
|
||||
GPUTextureFormat::Rgba32uint => Self::Rgba32Uint,
|
||||
GPUTextureFormat::Rgba32sint => Self::Rgba32Sint,
|
||||
GPUTextureFormat::Rgba32float => Self::Rgba32Float,
|
||||
GPUTextureFormat::Stencil8 => Self::Stencil8,
|
||||
GPUTextureFormat::Depth16unorm => Self::Depth16Unorm,
|
||||
GPUTextureFormat::Depth24plus => Self::Depth24Plus,
|
||||
GPUTextureFormat::Depth24plusStencil8 => Self::Depth24PlusStencil8,
|
||||
GPUTextureFormat::Depth32float => Self::Depth32Float,
|
||||
GPUTextureFormat::Depth32floatStencil8 => Self::Depth32FloatStencil8,
|
||||
GPUTextureFormat::Bc1RgbaUnorm => Self::Bc1RgbaUnorm,
|
||||
GPUTextureFormat::Bc1RgbaUnormSrgb => Self::Bc1RgbaUnormSrgb,
|
||||
GPUTextureFormat::Bc2RgbaUnorm => Self::Bc2RgbaUnorm,
|
||||
GPUTextureFormat::Bc2RgbaUnormSrgb => Self::Bc2RgbaUnormSrgb,
|
||||
GPUTextureFormat::Bc3RgbaUnorm => Self::Bc3RgbaUnorm,
|
||||
GPUTextureFormat::Bc3RgbaUnormSrgb => Self::Bc3RgbaUnormSrgb,
|
||||
GPUTextureFormat::Bc4RUnorm => Self::Bc4RUnorm,
|
||||
GPUTextureFormat::Bc4RSnorm => Self::Bc4RSnorm,
|
||||
GPUTextureFormat::Bc5RgUnorm => Self::Bc5RgUnorm,
|
||||
GPUTextureFormat::Bc5RgSnorm => Self::Bc5RgSnorm,
|
||||
GPUTextureFormat::Bc6hRgbUfloat => Self::Bc6hRgbUfloat,
|
||||
GPUTextureFormat::Bc6hRgbFloat => Self::Bc6hRgbFloat,
|
||||
GPUTextureFormat::Bc7RgbaUnorm => Self::Bc7RgbaUnorm,
|
||||
GPUTextureFormat::Bc7RgbaUnormSrgb => Self::Bc7RgbaUnormSrgb,
|
||||
GPUTextureFormat::Etc2Rgb8unorm => Self::Etc2Rgb8Unorm,
|
||||
GPUTextureFormat::Etc2Rgb8unormSrgb => Self::Etc2Rgb8UnormSrgb,
|
||||
GPUTextureFormat::Etc2Rgb8a1unorm => Self::Etc2Rgb8A1Unorm,
|
||||
GPUTextureFormat::Etc2Rgb8a1unormSrgb => Self::Etc2Rgb8A1UnormSrgb,
|
||||
GPUTextureFormat::Etc2Rgba8unorm => Self::Etc2Rgba8Unorm,
|
||||
GPUTextureFormat::Etc2Rgba8unormSrgb => Self::Etc2Rgba8UnormSrgb,
|
||||
GPUTextureFormat::EacR11unorm => Self::EacR11Unorm,
|
||||
GPUTextureFormat::EacR11snorm => Self::EacR11Snorm,
|
||||
GPUTextureFormat::EacRg11unorm => Self::EacRg11Unorm,
|
||||
GPUTextureFormat::EacRg11snorm => Self::EacRg11Snorm,
|
||||
GPUTextureFormat::Astc4x4Unorm => Self::Astc {
|
||||
block: AstcBlock::B4x4,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc4x4UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B5x4,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc5x4Unorm => Self::Astc {
|
||||
block: AstcBlock::B5x4,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc5x4UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B5x4,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc5x5Unorm => Self::Astc {
|
||||
block: AstcBlock::B5x5,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc5x5UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B5x5,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc6x5Unorm => Self::Astc {
|
||||
block: AstcBlock::B6x5,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc6x5UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B6x5,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc6x6Unorm => Self::Astc {
|
||||
block: AstcBlock::B6x6,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc6x6UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B6x6,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc8x5Unorm => Self::Astc {
|
||||
block: AstcBlock::B8x5,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc8x5UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B8x5,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc8x6Unorm => Self::Astc {
|
||||
block: AstcBlock::B8x6,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc8x6UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B8x6,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc8x8Unorm => Self::Astc {
|
||||
block: AstcBlock::B8x8,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc8x8UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B8x8,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc10x5Unorm => Self::Astc {
|
||||
block: AstcBlock::B10x5,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc10x5UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B10x5,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc10x6Unorm => Self::Astc {
|
||||
block: AstcBlock::B10x6,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc10x6UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B10x6,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc10x8Unorm => Self::Astc {
|
||||
block: AstcBlock::B10x8,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc10x8UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B10x8,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc10x10Unorm => Self::Astc {
|
||||
block: AstcBlock::B10x10,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc10x10UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B10x10,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc12x10Unorm => Self::Astc {
|
||||
block: AstcBlock::B12x10,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc12x10UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B12x10,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
GPUTextureFormat::Astc12x12Unorm => Self::Astc {
|
||||
block: AstcBlock::B12x12,
|
||||
channel: AstcChannel::Unorm,
|
||||
},
|
||||
GPUTextureFormat::Astc12x12UnormSrgb => Self::Astc {
|
||||
block: AstcBlock::B12x12,
|
||||
channel: AstcChannel::UnormSrgb,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@ interface GPUSupportedLimits {
|
||||
readonly attribute unsigned long long maxBufferSize;
|
||||
readonly attribute unsigned long maxVertexAttributes;
|
||||
readonly attribute unsigned long maxVertexBufferArrayStride;
|
||||
readonly attribute unsigned long maxInterStageShaderComponents;
|
||||
readonly attribute unsigned long maxColorAttachments;
|
||||
readonly attribute unsigned long maxColorAttachmentBytesPerSample;
|
||||
readonly attribute unsigned long maxComputeWorkgroupStorageSize;
|
||||
@ -51,6 +50,8 @@ interface GPUAdapterInfo {
|
||||
readonly attribute DOMString architecture;
|
||||
readonly attribute DOMString device;
|
||||
readonly attribute DOMString description;
|
||||
readonly attribute unsigned long subgroupMinSize;
|
||||
readonly attribute unsigned long subgroupMaxSize;
|
||||
};
|
||||
|
||||
interface mixin NavigatorGPU {
|
||||
@ -78,8 +79,8 @@ enum GPUPowerPreference {
|
||||
[Exposed=(Window, Worker), SecureContext]
|
||||
interface GPUAdapter {
|
||||
[SameObject] readonly attribute GPUSupportedFeatures features;
|
||||
[SameObject] readonly attribute GPUAdapterInfo info;
|
||||
[SameObject] readonly attribute GPUSupportedLimits limits;
|
||||
[SameObject] readonly attribute GPUAdapterInfo info;
|
||||
readonly attribute boolean isFallbackAdapter;
|
||||
|
||||
Promise<GPUDevice> requestDevice(optional GPUDeviceDescriptor descriptor = {});
|
||||
@ -88,7 +89,7 @@ interface GPUAdapter {
|
||||
dictionary GPUDeviceDescriptor
|
||||
: GPUObjectDescriptorBase {
|
||||
sequence<GPUFeatureName> requiredFeatures = [];
|
||||
record<DOMString, GPUSize64> requiredLimits = {};
|
||||
record<DOMString, (GPUSize64 or undefined)> requiredLimits = {};
|
||||
};
|
||||
|
||||
enum GPUFeatureName {
|
||||
@ -104,48 +105,14 @@ enum GPUFeatureName {
|
||||
"rg11b10ufloat-renderable",
|
||||
"bgra8unorm-storage",
|
||||
"float32-filterable",
|
||||
"dual-source-blending",
|
||||
|
||||
// extended from spec
|
||||
|
||||
// texture formats
|
||||
"texture-format-16-bit-norm",
|
||||
"texture-compression-astc-hdr",
|
||||
"texture-adapter-specific-format-features",
|
||||
// api
|
||||
"pipeline-statistics-query",
|
||||
"timestamp-query-inside-passes",
|
||||
"mappable-primary-buffers",
|
||||
"texture-binding-array",
|
||||
"buffer-binding-array",
|
||||
"storage-resource-binding-array",
|
||||
"sampled-texture-and-storage-buffer-array-non-uniform-indexing",
|
||||
"uniform-buffer-and-storage-texture-array-non-uniform-indexing",
|
||||
"partially-bound-binding-array",
|
||||
"multi-draw-indirect",
|
||||
"multi-draw-indirect-count",
|
||||
"push-constants",
|
||||
"address-mode-clamp-to-zero",
|
||||
"address-mode-clamp-to-border",
|
||||
"polygon-mode-line",
|
||||
"polygon-mode-point",
|
||||
"conservative-rasterization",
|
||||
"vertex-writable-storage",
|
||||
"clear-texture",
|
||||
"spirv-shader-passthrough",
|
||||
"multiview",
|
||||
"vertex-attribute-64-bit",
|
||||
// shader
|
||||
"shader-f64",
|
||||
"shader-i16",
|
||||
"shader-primitive-index",
|
||||
"shader-early-depth-test",
|
||||
"subgroups",
|
||||
};
|
||||
|
||||
[Exposed=(Window, Worker), SecureContext]
|
||||
interface GPUDevice : EventTarget {
|
||||
[SameObject] readonly attribute GPUSupportedFeatures features;
|
||||
[SameObject] readonly attribute GPUSupportedLimits limits;
|
||||
[SameObject] readonly attribute GPUAdapterInfo adapterInfo;
|
||||
|
||||
[SameObject] readonly attribute GPUQueue queue;
|
||||
|
||||
@ -275,6 +242,7 @@ dictionary GPUTextureViewDescriptor
|
||||
: GPUObjectDescriptorBase {
|
||||
GPUTextureFormat format;
|
||||
GPUTextureViewDimension dimension;
|
||||
GPUTextureUsageFlags usage = 0;
|
||||
GPUTextureAspect aspect = "all";
|
||||
GPUIntegerCoordinate baseMipLevel = 0;
|
||||
GPUIntegerCoordinate mipLevelCount;
|
||||
@ -636,7 +604,7 @@ interface mixin GPUPipelineBase {
|
||||
dictionary GPUProgrammableStage {
|
||||
required GPUShaderModule module;
|
||||
USVString entryPoint;
|
||||
record<USVString, GPUPipelineConstantValue> constants;
|
||||
record<USVString, GPUPipelineConstantValue> constants = {};
|
||||
};
|
||||
|
||||
typedef double GPUPipelineConstantValue; // May represent WGSL's bool, f32, i32, u32, and f16 if enabled.
|
||||
@ -766,8 +734,8 @@ enum GPUBlendOperation {
|
||||
dictionary GPUDepthStencilState {
|
||||
required GPUTextureFormat format;
|
||||
|
||||
required boolean depthWriteEnabled;
|
||||
required GPUCompareFunction depthCompare;
|
||||
boolean depthWriteEnabled;
|
||||
GPUCompareFunction depthCompare;
|
||||
|
||||
GPUStencilFaceState stencilFront = {};
|
||||
GPUStencilFaceState stencilBack = {};
|
||||
@ -922,8 +890,6 @@ interface GPUCommandEncoder {
|
||||
optional GPUSize64 offset = 0,
|
||||
optional GPUSize64 size);
|
||||
|
||||
undefined writeTimestamp(GPUQuerySet querySet, GPUSize32 queryIndex);
|
||||
|
||||
undefined resolveQuerySet(
|
||||
GPUQuerySet querySet,
|
||||
GPUSize32 firstQuery,
|
||||
@ -942,10 +908,10 @@ dictionary GPUCommandEncoderDescriptor
|
||||
};
|
||||
|
||||
interface mixin GPUBindingCommandsMixin {
|
||||
undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
|
||||
undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
|
||||
optional sequence<GPUBufferDynamicOffset> dynamicOffsets = []);
|
||||
|
||||
undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup,
|
||||
undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup,
|
||||
Uint32Array dynamicOffsetsData,
|
||||
GPUSize64 dynamicOffsetsDataStart,
|
||||
GPUSize32 dynamicOffsetsDataLength);
|
||||
@ -1228,7 +1194,6 @@ dictionary GPUUncapturedErrorEventInit : EventInit {
|
||||
};
|
||||
|
||||
partial interface GPUDevice {
|
||||
[Exposed=(Window, Worker)]
|
||||
attribute EventHandler onuncapturederror;
|
||||
};
|
||||
|
||||
|
640
deno_webgpu/webidl.rs
Normal file
640
deno_webgpu/webidl.rs
Normal file
@ -0,0 +1,640 @@
|
||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
#[allow(clippy::disallowed_types)]
|
||||
use std::collections::HashSet;
|
||||
|
||||
use deno_core::cppgc::Ptr;
|
||||
use deno_core::v8;
|
||||
use deno_core::webidl::ContextFn;
|
||||
use deno_core::webidl::IntOptions;
|
||||
use deno_core::webidl::WebIdlConverter;
|
||||
use deno_core::webidl::WebIdlError;
|
||||
use deno_core::webidl::WebIdlErrorKind;
|
||||
use deno_core::WebIDL;
|
||||
use deno_error::JsErrorBox;
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUExtent3DDict {
|
||||
#[options(enforce_range = true)]
|
||||
width: u32,
|
||||
#[webidl(default = 1)]
|
||||
#[options(enforce_range = true)]
|
||||
height: u32,
|
||||
#[webidl(default = 1)]
|
||||
#[options(enforce_range = true)]
|
||||
depth_or_array_layers: u32,
|
||||
}
|
||||
|
||||
pub(crate) enum GPUExtent3D {
|
||||
Dict(GPUExtent3DDict),
|
||||
Sequence((u32, u32, u32)),
|
||||
}
|
||||
|
||||
impl<'a> WebIdlConverter<'a> for GPUExtent3D {
|
||||
type Options = ();
|
||||
|
||||
fn convert<'b>(
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
value: v8::Local<'a, v8::Value>,
|
||||
prefix: Cow<'static, str>,
|
||||
context: ContextFn<'b>,
|
||||
options: &Self::Options,
|
||||
) -> Result<Self, WebIdlError> {
|
||||
if value.is_null_or_undefined() {
|
||||
return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert(
|
||||
scope,
|
||||
value,
|
||||
prefix,
|
||||
context.borrowed(),
|
||||
options,
|
||||
)?));
|
||||
}
|
||||
if let Ok(obj) = value.try_cast::<v8::Object>() {
|
||||
let iter = v8::Symbol::get_iterator(scope);
|
||||
if let Some(iter) = obj.get(scope, iter.into()) {
|
||||
if !iter.is_undefined() {
|
||||
let conv = <Vec<u32>>::convert(
|
||||
scope,
|
||||
value,
|
||||
prefix.clone(),
|
||||
context.borrowed(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)?;
|
||||
if !(conv.len() > 1 && conv.len() <= 3) {
|
||||
return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUExtent3D must have between 1 and 3 elements, received {} elements", conv.len()))));
|
||||
}
|
||||
|
||||
let mut iter = conv.into_iter();
|
||||
return Ok(GPUExtent3D::Sequence((
|
||||
iter.next().unwrap(),
|
||||
iter.next().unwrap_or(1),
|
||||
iter.next().unwrap_or(1),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert(
|
||||
scope, value, prefix, context, options,
|
||||
)?));
|
||||
}
|
||||
|
||||
Err(WebIdlError::new(
|
||||
prefix,
|
||||
context,
|
||||
WebIdlErrorKind::ConvertToConverterType(
|
||||
"sequence<GPUIntegerCoordinate> or GPUExtent3DDict",
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GPUExtent3D> for wgpu_types::Extent3d {
|
||||
fn from(value: GPUExtent3D) -> Self {
|
||||
match value {
|
||||
GPUExtent3D::Dict(dict) => Self {
|
||||
width: dict.width,
|
||||
height: dict.height,
|
||||
depth_or_array_layers: dict.depth_or_array_layers,
|
||||
},
|
||||
GPUExtent3D::Sequence((width, height, depth)) => Self {
|
||||
width,
|
||||
height,
|
||||
depth_or_array_layers: depth,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUOrigin3DDict {
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
x: u32,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
y: u32,
|
||||
#[webidl(default = 0)]
|
||||
#[options(enforce_range = true)]
|
||||
z: u32,
|
||||
}
|
||||
|
||||
pub(crate) enum GPUOrigin3D {
|
||||
Dict(GPUOrigin3DDict),
|
||||
Sequence((u32, u32, u32)),
|
||||
}
|
||||
|
||||
impl Default for GPUOrigin3D {
|
||||
fn default() -> Self {
|
||||
GPUOrigin3D::Sequence((0, 0, 0))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WebIdlConverter<'a> for GPUOrigin3D {
|
||||
type Options = ();
|
||||
|
||||
fn convert<'b>(
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
value: v8::Local<'a, v8::Value>,
|
||||
prefix: Cow<'static, str>,
|
||||
context: ContextFn<'b>,
|
||||
options: &Self::Options,
|
||||
) -> Result<Self, WebIdlError> {
|
||||
if value.is_null_or_undefined() {
|
||||
return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert(
|
||||
scope,
|
||||
value,
|
||||
prefix,
|
||||
context.borrowed(),
|
||||
options,
|
||||
)?));
|
||||
}
|
||||
if let Ok(obj) = value.try_cast::<v8::Object>() {
|
||||
let iter = v8::Symbol::get_iterator(scope);
|
||||
if let Some(iter) = obj.get(scope, iter.into()) {
|
||||
if !iter.is_undefined() {
|
||||
let conv = <Vec<u32>>::convert(
|
||||
scope,
|
||||
value,
|
||||
prefix.clone(),
|
||||
context.borrowed(),
|
||||
&IntOptions {
|
||||
clamp: false,
|
||||
enforce_range: true,
|
||||
},
|
||||
)?;
|
||||
if conv.len() > 3 {
|
||||
return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUOrigin3D must have at most 3 elements, received {} elements", conv.len()))));
|
||||
}
|
||||
|
||||
let mut iter = conv.into_iter();
|
||||
return Ok(GPUOrigin3D::Sequence((
|
||||
iter.next().unwrap_or(0),
|
||||
iter.next().unwrap_or(0),
|
||||
iter.next().unwrap_or(0),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert(
|
||||
scope, value, prefix, context, options,
|
||||
)?));
|
||||
}
|
||||
|
||||
Err(WebIdlError::new(
|
||||
prefix,
|
||||
context,
|
||||
WebIdlErrorKind::ConvertToConverterType(
|
||||
"sequence<GPUIntegerCoordinate> or GPUOrigin3DDict",
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GPUOrigin3D> for wgpu_types::Origin3d {
|
||||
fn from(value: GPUOrigin3D) -> Self {
|
||||
match value {
|
||||
GPUOrigin3D::Dict(dict) => Self {
|
||||
x: dict.x,
|
||||
y: dict.y,
|
||||
z: dict.z,
|
||||
},
|
||||
GPUOrigin3D::Sequence((x, y, z)) => Self { x, y, z },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(dictionary)]
|
||||
pub(crate) struct GPUColorDict {
|
||||
r: f64,
|
||||
g: f64,
|
||||
b: f64,
|
||||
a: f64,
|
||||
}
|
||||
|
||||
pub(crate) enum GPUColor {
|
||||
Dict(GPUColorDict),
|
||||
Sequence((f64, f64, f64, f64)),
|
||||
}
|
||||
|
||||
impl<'a> WebIdlConverter<'a> for GPUColor {
|
||||
type Options = ();
|
||||
|
||||
fn convert<'b>(
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
value: v8::Local<'a, v8::Value>,
|
||||
prefix: Cow<'static, str>,
|
||||
context: ContextFn<'b>,
|
||||
options: &Self::Options,
|
||||
) -> Result<Self, WebIdlError> {
|
||||
if value.is_null_or_undefined() {
|
||||
return Ok(GPUColor::Dict(GPUColorDict::convert(
|
||||
scope,
|
||||
value,
|
||||
prefix,
|
||||
context.borrowed(),
|
||||
options,
|
||||
)?));
|
||||
}
|
||||
if let Ok(obj) = value.try_cast::<v8::Object>() {
|
||||
let iter = v8::Symbol::get_iterator(scope);
|
||||
if let Some(iter) = obj.get(scope, iter.into()) {
|
||||
if !iter.is_undefined() {
|
||||
let conv = <Vec<f64>>::convert(
|
||||
scope,
|
||||
value,
|
||||
prefix.clone(),
|
||||
context.borrowed(),
|
||||
options,
|
||||
)?;
|
||||
if conv.len() != 4 {
|
||||
return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUColor must have exactly 4 elements, received {} elements", conv.len()))));
|
||||
}
|
||||
|
||||
let mut iter = conv.into_iter();
|
||||
return Ok(GPUColor::Sequence((
|
||||
iter.next().unwrap(),
|
||||
iter.next().unwrap(),
|
||||
iter.next().unwrap(),
|
||||
iter.next().unwrap(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(GPUColor::Dict(GPUColorDict::convert(
|
||||
scope, value, prefix, context, options,
|
||||
)?));
|
||||
}
|
||||
|
||||
Err(WebIdlError::new(
|
||||
prefix,
|
||||
context,
|
||||
WebIdlErrorKind::ConvertToConverterType(
|
||||
"sequence<GPUIntegerCoordinate> or GPUOrigin3DDict",
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<GPUColor> for wgpu_types::Color {
|
||||
fn from(value: GPUColor) -> Self {
|
||||
match value {
|
||||
GPUColor::Dict(dict) => Self {
|
||||
r: dict.r,
|
||||
g: dict.g,
|
||||
b: dict.b,
|
||||
a: dict.a,
|
||||
},
|
||||
GPUColor::Sequence((r, g, b, a)) => Self { r, g, b, a },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL)]
|
||||
#[webidl(enum)]
|
||||
pub(crate) enum GPUAutoLayoutMode {
|
||||
Auto,
|
||||
}
|
||||
|
||||
pub(crate) enum GPUPipelineLayoutOrGPUAutoLayoutMode {
|
||||
PipelineLayout(Ptr<crate::pipeline_layout::GPUPipelineLayout>),
|
||||
AutoLayoutMode(GPUAutoLayoutMode),
|
||||
}
|
||||
|
||||
impl From<GPUPipelineLayoutOrGPUAutoLayoutMode> for Option<wgpu_core::id::PipelineLayoutId> {
|
||||
fn from(value: GPUPipelineLayoutOrGPUAutoLayoutMode) -> Self {
|
||||
match value {
|
||||
GPUPipelineLayoutOrGPUAutoLayoutMode::PipelineLayout(layout) => Some(layout.id),
|
||||
GPUPipelineLayoutOrGPUAutoLayoutMode::AutoLayoutMode(GPUAutoLayoutMode::Auto) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> WebIdlConverter<'a> for GPUPipelineLayoutOrGPUAutoLayoutMode {
|
||||
type Options = ();
|
||||
|
||||
fn convert<'b>(
|
||||
scope: &mut v8::HandleScope<'a>,
|
||||
value: v8::Local<'a, v8::Value>,
|
||||
prefix: Cow<'static, str>,
|
||||
context: ContextFn<'b>,
|
||||
options: &Self::Options,
|
||||
) -> Result<Self, WebIdlError> {
|
||||
if value.is_object() {
|
||||
Ok(Self::PipelineLayout(WebIdlConverter::convert(
|
||||
scope, value, prefix, context, options,
|
||||
)?))
|
||||
} else {
|
||||
Ok(Self::AutoLayoutMode(WebIdlConverter::convert(
|
||||
scope, value, prefix, context, options,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebIDL, Clone, Hash, Eq, PartialEq)]
|
||||
#[webidl(enum)]
|
||||
pub enum GPUFeatureName {
|
||||
#[webidl(rename = "depth-clip-control")]
|
||||
DepthClipControl,
|
||||
#[webidl(rename = "timestamp-query")]
|
||||
TimestampQuery,
|
||||
#[webidl(rename = "indirect-first-instance")]
|
||||
IndirectFirstInstance,
|
||||
#[webidl(rename = "shader-f16")]
|
||||
ShaderF16,
|
||||
#[webidl(rename = "depth32float-stencil8")]
|
||||
Depth32floatStencil8,
|
||||
#[webidl(rename = "texture-compression-bc")]
|
||||
TextureCompressionBc,
|
||||
#[webidl(rename = "texture-compression-bc-sliced-3d")]
|
||||
TextureCompressionBcSliced3d,
|
||||
#[webidl(rename = "texture-compression-etc2")]
|
||||
TextureCompressionEtc2,
|
||||
#[webidl(rename = "texture-compression-astc")]
|
||||
TextureCompressionAstc,
|
||||
#[webidl(rename = "rg11b10ufloat-renderable")]
|
||||
Rg11b10ufloatRenderable,
|
||||
#[webidl(rename = "bgra8unorm-storage")]
|
||||
Bgra8unormStorage,
|
||||
#[webidl(rename = "float32-filterable")]
|
||||
Float32Filterable,
|
||||
#[webidl(rename = "dual-source-blending")]
|
||||
DualSourceBlending,
|
||||
#[webidl(rename = "subgroups")]
|
||||
Subgroups,
|
||||
|
||||
// extended from spec
|
||||
#[webidl(rename = "texture-format-16-bit-norm")]
|
||||
TextureFormat16BitNorm,
|
||||
#[webidl(rename = "texture-compression-astc-hdr")]
|
||||
TextureCompressionAstcHdr,
|
||||
#[webidl(rename = "texture-adapter-specific-format-features")]
|
||||
TextureAdapterSpecificFormatFeatures,
|
||||
#[webidl(rename = "pipeline-statistics-query")]
|
||||
PipelineStatisticsQuery,
|
||||
#[webidl(rename = "timestamp-query-inside-passes")]
|
||||
TimestampQueryInsidePasses,
|
||||
#[webidl(rename = "mappable-primary-buffers")]
|
||||
MappablePrimaryBuffers,
|
||||
#[webidl(rename = "texture-binding-array")]
|
||||
TextureBindingArray,
|
||||
#[webidl(rename = "buffer-binding-array")]
|
||||
BufferBindingArray,
|
||||
#[webidl(rename = "storage-resource-binding-array")]
|
||||
StorageResourceBindingArray,
|
||||
#[webidl(rename = "sampled-texture-and-storage-buffer-array-non-uniform-indexing")]
|
||||
SampledTextureAndStorageBufferArrayNonUniformIndexing,
|
||||
#[webidl(rename = "uniform-buffer-and-storage-texture-array-non-uniform-indexing")]
|
||||
UniformBufferAndStorageTextureArrayNonUniformIndexing,
|
||||
#[webidl(rename = "partially-bound-binding-array")]
|
||||
PartiallyBoundBindingArray,
|
||||
#[webidl(rename = "multi-draw-indirect")]
|
||||
MultiDrawIndirect,
|
||||
#[webidl(rename = "multi-draw-indirect-count")]
|
||||
MultiDrawIndirectCount,
|
||||
#[webidl(rename = "push-constants")]
|
||||
PushConstants,
|
||||
#[webidl(rename = "address-mode-clamp-to-zero")]
|
||||
AddressModeClampToZero,
|
||||
#[webidl(rename = "address-mode-clamp-to-border")]
|
||||
AddressModeClampToBorder,
|
||||
#[webidl(rename = "polygon-mode-line")]
|
||||
PolygonModeLine,
|
||||
#[webidl(rename = "polygon-mode-point")]
|
||||
PolygonModePoint,
|
||||
#[webidl(rename = "conservative-rasterization")]
|
||||
ConservativeRasterization,
|
||||
#[webidl(rename = "vertex-writable-storage")]
|
||||
VertexWritableStorage,
|
||||
#[webidl(rename = "clear-texture")]
|
||||
ClearTexture,
|
||||
#[webidl(rename = "spirv-shader-passthrough")]
|
||||
SpirvShaderPassthrough,
|
||||
#[webidl(rename = "multiview")]
|
||||
Multiview,
|
||||
#[webidl(rename = "vertex-attribute-64-bit")]
|
||||
VertexAttribute64Bit,
|
||||
#[webidl(rename = "shader-f64")]
|
||||
ShaderF64,
|
||||
#[webidl(rename = "shader-i16")]
|
||||
ShaderI16,
|
||||
#[webidl(rename = "shader-primitive-index")]
|
||||
ShaderPrimitiveIndex,
|
||||
#[webidl(rename = "shader-early-depth-test")]
|
||||
ShaderEarlyDepthTest,
|
||||
}
|
||||
|
||||
pub fn feature_names_to_features(names: Vec<GPUFeatureName>) -> wgpu_types::Features {
|
||||
use wgpu_types::Features;
|
||||
let mut features = Features::empty();
|
||||
|
||||
for name in names {
|
||||
#[rustfmt::skip]
|
||||
let feature = match name {
|
||||
GPUFeatureName::DepthClipControl => Features::DEPTH_CLIP_CONTROL,
|
||||
GPUFeatureName::TimestampQuery => Features::TIMESTAMP_QUERY,
|
||||
GPUFeatureName::IndirectFirstInstance => Features::INDIRECT_FIRST_INSTANCE,
|
||||
GPUFeatureName::ShaderF16 => Features::SHADER_F16,
|
||||
GPUFeatureName::Depth32floatStencil8 => Features::DEPTH32FLOAT_STENCIL8,
|
||||
GPUFeatureName::TextureCompressionBc => Features::TEXTURE_COMPRESSION_BC,
|
||||
GPUFeatureName::TextureCompressionBcSliced3d => Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
|
||||
GPUFeatureName::TextureCompressionEtc2 => Features::TEXTURE_COMPRESSION_ETC2,
|
||||
GPUFeatureName::TextureCompressionAstc => Features::TEXTURE_COMPRESSION_ASTC,
|
||||
GPUFeatureName::Rg11b10ufloatRenderable => Features::RG11B10UFLOAT_RENDERABLE,
|
||||
GPUFeatureName::Bgra8unormStorage => Features::BGRA8UNORM_STORAGE,
|
||||
GPUFeatureName::Float32Filterable => Features::FLOAT32_FILTERABLE,
|
||||
GPUFeatureName::DualSourceBlending => Features::DUAL_SOURCE_BLENDING,
|
||||
GPUFeatureName::Subgroups => Features::SUBGROUP,
|
||||
GPUFeatureName::TextureFormat16BitNorm => Features::TEXTURE_FORMAT_16BIT_NORM,
|
||||
GPUFeatureName::TextureCompressionAstcHdr => Features::TEXTURE_COMPRESSION_ASTC_HDR,
|
||||
GPUFeatureName::TextureAdapterSpecificFormatFeatures => Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||
GPUFeatureName::PipelineStatisticsQuery => Features::PIPELINE_STATISTICS_QUERY,
|
||||
GPUFeatureName::TimestampQueryInsidePasses => Features::TIMESTAMP_QUERY_INSIDE_PASSES,
|
||||
GPUFeatureName::MappablePrimaryBuffers => Features::MAPPABLE_PRIMARY_BUFFERS,
|
||||
GPUFeatureName::TextureBindingArray => Features::TEXTURE_BINDING_ARRAY,
|
||||
GPUFeatureName::BufferBindingArray => Features::BUFFER_BINDING_ARRAY,
|
||||
GPUFeatureName::StorageResourceBindingArray => Features::STORAGE_RESOURCE_BINDING_ARRAY,
|
||||
GPUFeatureName::SampledTextureAndStorageBufferArrayNonUniformIndexing => Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||
GPUFeatureName::UniformBufferAndStorageTextureArrayNonUniformIndexing => Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
GPUFeatureName::PartiallyBoundBindingArray => Features::PARTIALLY_BOUND_BINDING_ARRAY,
|
||||
GPUFeatureName::MultiDrawIndirect => Features::MULTI_DRAW_INDIRECT,
|
||||
GPUFeatureName::MultiDrawIndirectCount => Features::MULTI_DRAW_INDIRECT_COUNT,
|
||||
GPUFeatureName::PushConstants => Features::PUSH_CONSTANTS,
|
||||
GPUFeatureName::AddressModeClampToZero => Features::ADDRESS_MODE_CLAMP_TO_ZERO,
|
||||
GPUFeatureName::AddressModeClampToBorder => Features::ADDRESS_MODE_CLAMP_TO_BORDER,
|
||||
GPUFeatureName::PolygonModeLine => Features::POLYGON_MODE_LINE,
|
||||
GPUFeatureName::PolygonModePoint => Features::POLYGON_MODE_POINT,
|
||||
GPUFeatureName::ConservativeRasterization => Features::CONSERVATIVE_RASTERIZATION,
|
||||
GPUFeatureName::VertexWritableStorage => Features::VERTEX_WRITABLE_STORAGE,
|
||||
GPUFeatureName::ClearTexture => Features::CLEAR_TEXTURE,
|
||||
GPUFeatureName::SpirvShaderPassthrough => Features::SPIRV_SHADER_PASSTHROUGH,
|
||||
GPUFeatureName::Multiview => Features::MULTIVIEW,
|
||||
GPUFeatureName::VertexAttribute64Bit => Features::VERTEX_ATTRIBUTE_64BIT,
|
||||
GPUFeatureName::ShaderF64 => Features::SHADER_F64,
|
||||
GPUFeatureName::ShaderI16 => Features::SHADER_F16,
|
||||
GPUFeatureName::ShaderPrimitiveIndex => Features::SHADER_PRIMITIVE_INDEX,
|
||||
GPUFeatureName::ShaderEarlyDepthTest => Features::SHADER_EARLY_DEPTH_TEST,
|
||||
};
|
||||
features.set(feature, true);
|
||||
}
|
||||
|
||||
features
|
||||
}
|
||||
|
||||
#[allow(clippy::disallowed_types)]
|
||||
pub fn features_to_feature_names(features: wgpu_types::Features) -> HashSet<GPUFeatureName> {
|
||||
use GPUFeatureName::*;
|
||||
let mut return_features = HashSet::new();
|
||||
|
||||
// api
|
||||
if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) {
|
||||
return_features.insert(DepthClipControl);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) {
|
||||
return_features.insert(TimestampQuery);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) {
|
||||
return_features.insert(IndirectFirstInstance);
|
||||
}
|
||||
// shader
|
||||
if features.contains(wgpu_types::Features::SHADER_F16) {
|
||||
return_features.insert(ShaderF16);
|
||||
}
|
||||
// texture formats
|
||||
if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) {
|
||||
return_features.insert(Depth32floatStencil8);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) {
|
||||
return_features.insert(TextureCompressionBc);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC_SLICED_3D) {
|
||||
return_features.insert(TextureCompressionBcSliced3d);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) {
|
||||
return_features.insert(TextureCompressionEtc2);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC) {
|
||||
return_features.insert(TextureCompressionAstc);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) {
|
||||
return_features.insert(Rg11b10ufloatRenderable);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) {
|
||||
return_features.insert(Bgra8unormStorage);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) {
|
||||
return_features.insert(Float32Filterable);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::DUAL_SOURCE_BLENDING) {
|
||||
return_features.insert(DualSourceBlending);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SUBGROUP) {
|
||||
return_features.insert(Subgroups);
|
||||
}
|
||||
|
||||
// extended from spec
|
||||
|
||||
// texture formats
|
||||
if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) {
|
||||
return_features.insert(TextureFormat16BitNorm);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
|
||||
return_features.insert(TextureCompressionAstcHdr);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) {
|
||||
return_features.insert(TextureAdapterSpecificFormatFeatures);
|
||||
}
|
||||
// api
|
||||
if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) {
|
||||
return_features.insert(PipelineStatisticsQuery);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) {
|
||||
return_features.insert(TimestampQueryInsidePasses);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) {
|
||||
return_features.insert(MappablePrimaryBuffers);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) {
|
||||
return_features.insert(TextureBindingArray);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) {
|
||||
return_features.insert(BufferBindingArray);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) {
|
||||
return_features.insert(StorageResourceBindingArray);
|
||||
}
|
||||
if features.contains(
|
||||
wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
|
||||
) {
|
||||
return_features.insert(SampledTextureAndStorageBufferArrayNonUniformIndexing);
|
||||
}
|
||||
if features.contains(
|
||||
wgpu_types::Features::UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
|
||||
) {
|
||||
return_features.insert(UniformBufferAndStorageTextureArrayNonUniformIndexing);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) {
|
||||
return_features.insert(PartiallyBoundBindingArray);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) {
|
||||
return_features.insert(MultiDrawIndirect);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) {
|
||||
return_features.insert(MultiDrawIndirectCount);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::PUSH_CONSTANTS) {
|
||||
return_features.insert(PushConstants);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) {
|
||||
return_features.insert(AddressModeClampToZero);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) {
|
||||
return_features.insert(AddressModeClampToBorder);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::POLYGON_MODE_LINE) {
|
||||
return_features.insert(PolygonModeLine);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) {
|
||||
return_features.insert(PolygonModePoint);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) {
|
||||
return_features.insert(ConservativeRasterization);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) {
|
||||
return_features.insert(VertexWritableStorage);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::CLEAR_TEXTURE) {
|
||||
return_features.insert(ClearTexture);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) {
|
||||
return_features.insert(SpirvShaderPassthrough);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::MULTIVIEW) {
|
||||
return_features.insert(Multiview);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) {
|
||||
return_features.insert(VertexAttribute64Bit);
|
||||
}
|
||||
// shader
|
||||
if features.contains(wgpu_types::Features::SHADER_F64) {
|
||||
return_features.insert(ShaderF64);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SHADER_I16) {
|
||||
return_features.insert(ShaderI16);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) {
|
||||
return_features.insert(ShaderPrimitiveIndex);
|
||||
}
|
||||
if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) {
|
||||
return_features.insert(ShaderEarlyDepthTest);
|
||||
}
|
||||
|
||||
return_features
|
||||
}
|
Loading…
Reference in New Issue
Block a user