From f0bf789f5899fde9ece30de68c24ea799934f8cb Mon Sep 17 00:00:00 2001
From: Leo Kettmeir <crowlkats@toaxl.com>
Date: Thu, 13 Feb 2025 13:17:00 +0100
Subject: [PATCH] refactor(deno): use object wrap (#7113)

---
 .deny.toml                       |    9 +-
 Cargo.lock                       |  171 +-
 Cargo.toml                       |   16 +-
 cts_runner/src/main.rs           |  113 +-
 deno_webgpu/00_init.js           |    2 +-
 deno_webgpu/01_webgpu.js         | 6865 +-----------------------------
 deno_webgpu/02_surface.js        |  196 +-
 deno_webgpu/Cargo.toml           |   15 +-
 deno_webgpu/adapter.rs           |  471 ++
 deno_webgpu/bind_group.rs        |  116 +
 deno_webgpu/bind_group_layout.rs |  176 +
 deno_webgpu/binding.rs           |  312 --
 deno_webgpu/buffer.rs            |  428 +-
 deno_webgpu/bundle.rs            |  392 --
 deno_webgpu/byow.rs              |  246 +-
 deno_webgpu/command_buffer.rs    |   52 +
 deno_webgpu/command_encoder.rs   |  958 ++---
 deno_webgpu/compute_pass.rs      |  402 +-
 deno_webgpu/compute_pipeline.rs  |   82 +
 deno_webgpu/device.rs            |  843 ++++
 deno_webgpu/error.rs             |  298 +-
 deno_webgpu/lib.rs               |  928 +---
 deno_webgpu/pipeline.rs          |  420 --
 deno_webgpu/pipeline_layout.rs   |   50 +
 deno_webgpu/query_set.rs         |   87 +
 deno_webgpu/queue.rs             |  254 +-
 deno_webgpu/render_bundle.rs     |  409 ++
 deno_webgpu/render_pass.rs       |  941 ++--
 deno_webgpu/render_pipeline.rs   |  550 +++
 deno_webgpu/sampler.rs           |  188 +-
 deno_webgpu/shader.rs            |   82 +-
 deno_webgpu/surface.rs           |  303 +-
 deno_webgpu/texture.rs           |  741 +++-
 deno_webgpu/webgpu.idl           |   59 +-
 deno_webgpu/webidl.rs            |  640 +++
 35 files changed, 6811 insertions(+), 11004 deletions(-)
 create mode 100644 deno_webgpu/adapter.rs
 create mode 100644 deno_webgpu/bind_group.rs
 create mode 100644 deno_webgpu/bind_group_layout.rs
 delete mode 100644 deno_webgpu/binding.rs
 delete mode 100644 deno_webgpu/bundle.rs
 create mode 100644 deno_webgpu/command_buffer.rs
 create mode 100644 deno_webgpu/compute_pipeline.rs
 create mode 100644 deno_webgpu/device.rs
 delete mode 100644 deno_webgpu/pipeline.rs
 create mode 100644 deno_webgpu/pipeline_layout.rs
 create mode 100644 deno_webgpu/query_set.rs
 create mode 100644 deno_webgpu/render_bundle.rs
 create mode 100644 deno_webgpu/render_pipeline.rs
 create mode 100644 deno_webgpu/webidl.rs

diff --git a/.deny.toml b/.deny.toml
index 96b182ac2..8c22a2d1a 100644
--- a/.deny.toml
+++ b/.deny.toml
@@ -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
diff --git a/Cargo.lock b/Cargo.lock
index ce965d267..384713520 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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]]
diff --git a/Cargo.toml b/Cargo.toml
index 6d98d9904..32461eddf 100644
--- a/Cargo.toml
+++ b/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" }
 
diff --git a/cts_runner/src/main.rs b/cts_runner/src/main.rs
index d34a3867a..b8da75af1 100644
--- a/cts_runner/src/main.rs
+++ b/cts_runner/src/main.rs
@@ -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,
diff --git a/deno_webgpu/00_init.js b/deno_webgpu/00_init.js
index 0f10847ce..81bb27286 100644
--- a/deno_webgpu/00_init.js
+++ b/deno_webgpu/00_init.js
@@ -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";
 
diff --git a/deno_webgpu/01_webgpu.js b/deno_webgpu/01_webgpu.js
index 022239171..903c3b63c 100644
--- a/deno_webgpu/01_webgpu.js
+++ b/deno_webgpu/01_webgpu.js
@@ -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,260 +7,57 @@
 /// <reference path="./lib.deno_webgpu.d.ts" />
 
 import { core, primordials } from "ext:core/mod.js";
-const {
-  isDataView,
-  isTypedArray,
-} = core;
 import {
-  op_webgpu_buffer_get_map_async,
-  op_webgpu_buffer_get_mapped_range,
-  op_webgpu_buffer_unmap,
-  op_webgpu_command_encoder_begin_compute_pass,
-  op_webgpu_command_encoder_begin_render_pass,
-  op_webgpu_command_encoder_clear_buffer,
-  op_webgpu_command_encoder_copy_buffer_to_buffer,
-  op_webgpu_command_encoder_copy_buffer_to_texture,
-  op_webgpu_command_encoder_copy_texture_to_buffer,
-  op_webgpu_command_encoder_copy_texture_to_texture,
-  op_webgpu_command_encoder_finish,
-  op_webgpu_command_encoder_insert_debug_marker,
-  op_webgpu_command_encoder_pop_debug_group,
-  op_webgpu_command_encoder_push_debug_group,
-  op_webgpu_command_encoder_resolve_query_set,
-  op_webgpu_command_encoder_write_timestamp,
-  op_webgpu_compute_pass_dispatch_workgroups,
-  op_webgpu_compute_pass_dispatch_workgroups_indirect,
-  op_webgpu_compute_pass_end,
-  op_webgpu_compute_pass_insert_debug_marker,
-  op_webgpu_compute_pass_pop_debug_group,
-  op_webgpu_compute_pass_push_debug_group,
-  op_webgpu_compute_pass_set_bind_group,
-  op_webgpu_compute_pass_set_pipeline,
-  op_webgpu_compute_pipeline_get_bind_group_layout,
-  op_webgpu_create_bind_group,
-  op_webgpu_create_bind_group_layout,
-  op_webgpu_create_buffer,
-  op_webgpu_create_command_encoder,
-  op_webgpu_create_compute_pipeline,
-  op_webgpu_create_pipeline_layout,
-  op_webgpu_create_query_set,
-  op_webgpu_create_render_bundle_encoder,
-  op_webgpu_create_render_pipeline,
-  op_webgpu_create_sampler,
-  op_webgpu_create_shader_module,
-  op_webgpu_create_texture,
-  op_webgpu_create_texture_view,
-  op_webgpu_queue_submit,
-  op_webgpu_render_bundle_encoder_draw,
-  op_webgpu_render_bundle_encoder_draw_indexed,
-  op_webgpu_render_bundle_encoder_draw_indirect,
-  op_webgpu_render_bundle_encoder_finish,
-  op_webgpu_render_bundle_encoder_insert_debug_marker,
-  op_webgpu_render_bundle_encoder_pop_debug_group,
-  op_webgpu_render_bundle_encoder_push_debug_group,
-  op_webgpu_render_bundle_encoder_set_bind_group,
-  op_webgpu_render_bundle_encoder_set_index_buffer,
-  op_webgpu_render_bundle_encoder_set_pipeline,
-  op_webgpu_render_bundle_encoder_set_vertex_buffer,
-  op_webgpu_render_pass_begin_occlusion_query,
-  op_webgpu_render_pass_draw,
-  op_webgpu_render_pass_draw_indexed,
-  op_webgpu_render_pass_draw_indexed_indirect,
-  op_webgpu_render_pass_draw_indirect,
-  op_webgpu_render_pass_end,
-  op_webgpu_render_pass_end_occlusion_query,
-  op_webgpu_render_pass_execute_bundles,
-  op_webgpu_render_pass_insert_debug_marker,
-  op_webgpu_render_pass_pop_debug_group,
-  op_webgpu_render_pass_push_debug_group,
-  op_webgpu_render_pass_set_bind_group,
-  op_webgpu_render_pass_set_blend_constant,
-  op_webgpu_render_pass_set_index_buffer,
-  op_webgpu_render_pass_set_pipeline,
-  op_webgpu_render_pass_set_scissor_rect,
-  op_webgpu_render_pass_set_stencil_reference,
-  op_webgpu_render_pass_set_vertex_buffer,
-  op_webgpu_render_pass_set_viewport,
-  op_webgpu_render_pipeline_get_bind_group_layout,
-  op_webgpu_request_adapter,
-  op_webgpu_request_adapter_info,
-  op_webgpu_request_device,
-  op_webgpu_write_buffer,
-  op_webgpu_write_texture,
+  GPU,
+  GPUAdapter,
+  GPUAdapterInfo,
+  GPUBindGroup,
+  GPUBindGroupLayout,
+  GPUBuffer,
+  GPUCommandBuffer,
+  GPUCommandEncoder,
+  GPUComputePassEncoder,
+  GPUComputePipeline,
+  GPUDevice,
+  GPUDeviceLostInfo,
+  GPUPipelineLayout,
+  GPUQuerySet,
+  GPUQueue,
+  GPURenderBundle,
+  GPURenderBundleEncoder,
+  GPURenderPassEncoder,
+  GPURenderPipeline,
+  GPUSampler,
+  GPUShaderModule,
+  GPUSupportedFeatures,
+  GPUSupportedLimits,
+  GPUTexture,
+  GPUTextureView,
+  op_create_gpu,
 } from "ext:core/ops";
 const {
-  ArrayBuffer,
-  ArrayBufferPrototypeGetByteLength,
-  ArrayIsArray,
-  ArrayPrototypeFindLast,
-  ArrayPrototypeMap,
-  ArrayPrototypePop,
-  ArrayPrototypePush,
-  DataViewPrototypeGetBuffer,
-  Error,
-  Number,
-  NumberPOSITIVE_INFINITY,
-  NumberMAX_SAFE_INTEGER,
-  NumberNEGATIVE_INFINITY,
-  NumberMIN_SAFE_INTEGER,
-  MathMax,
   ObjectDefineProperty,
-  ObjectHasOwn,
   ObjectPrototypeIsPrototypeOf,
-  Promise,
-  PromiseReject,
-  PromiseResolve,
-  SafeArrayIterator,
-  SafeSet,
-  SafeWeakRef,
-  SetPrototypeHas,
+  ObjectSetPrototypeOf,
   Symbol,
   SymbolFor,
-  SymbolIterator,
-  TypeError,
-  TypedArrayPrototypeGetBuffer,
-  TypedArrayPrototypeGetSymbolToStringTag,
-  Uint32Array,
-  Uint8Array,
 } = primordials;
 
 import * as webidl from "ext:deno_webidl/00_webidl.js";
 import {
   defineEventHandler,
   Event,
-  EventTarget,
+  EventTargetPrototype,
   setEventTargetData,
 } from "ext:deno_web/02_event.js";
 import { DOMException } from "ext:deno_web/01_dom_exception.js";
 import { createFilteredInspectProxy } from "ext:deno_console/01_console.js";
 
-const _rid = Symbol("[[rid]]");
-const _size = Symbol("[[size]]");
-const _usage = Symbol("[[usage]]");
-const _state = Symbol("[[state]]");
-const _mappingRange = Symbol("[[mapping_range]]");
-const _mappedRanges = Symbol("[[mapped_ranges]]");
-const _mapMode = Symbol("[[map_mode]]");
-const _adapter = Symbol("[[adapter]]");
-const _adapterInfo = Symbol("[[adapterInfo]]");
-const _invalid = Symbol("[[invalid]]");
-const _cleanup = Symbol("[[cleanup]]");
-const _vendor = Symbol("[[vendor]]");
-const _architecture = Symbol("[[architecture]]");
-const _description = Symbol("[[description]]");
-const _limits = Symbol("[[limits]]");
-const _reason = Symbol("[[reason]]");
+const customInspect = SymbolFor("Deno.privateCustomInspect");
 const _message = Symbol("[[message]]");
-const _label = Symbol("[[label]]");
-const _device = Symbol("[[device]]");
-const _queue = Symbol("[[queue]]");
-const _views = Symbol("[[views]]");
-const _texture = Symbol("[[texture]]");
-const _encoders = Symbol("[[encoders]]");
-const _encoder = Symbol("[[encoder]]");
-const _descriptor = Symbol("[[descriptor]]");
-const _width = Symbol("[[width]]");
-const _height = Symbol("[[height]]");
-const _depthOrArrayLayers = Symbol("[[depthOrArrayLayers]]");
-const _mipLevelCount = Symbol("[[mipLevelCount]]");
-const _sampleCount = Symbol("[[sampleCount]]");
-const _dimension = Symbol("[[dimension]]");
-const _format = Symbol("[[format]]");
-const _type = Symbol("[[type]]");
-const _count = Symbol("[[count]]");
-
-/**
- * @param {any} self
- * @param {string} prefix
- * @param {string} context
- * @returns {InnerGPUDevice & {rid: number}}
- */
-function assertDevice(self, prefix, context) {
-  const device = self[_device];
-  const deviceRid = device?.rid;
-  if (deviceRid === undefined) {
-    throw new DOMException(
-      `${prefix}: ${context} references an invalid or destroyed device`,
-      "OperationError",
-    );
-  }
-  return device;
-}
-
-/**
- * @param {any} self
- * @param {string} prefix
- * @param {string} context
- * @returns {number}
- */
-function assertResource(self, prefix, context) {
-  const rid = self[_rid];
-  if (rid === undefined) {
-    throw new DOMException(
-      `${prefix}: ${context} an invalid or destroyed resource`,
-      "OperationError",
-    );
-  }
-  return rid;
-}
-
-/**
- * @param {number[] | GPUExtent3DDict} data
- * @returns {GPUExtent3DDict}
- */
-function normalizeGPUExtent3D(data) {
-  if (ArrayIsArray(data)) {
-    return {
-      width: data[0],
-      height: data[1] ?? 1,
-      depthOrArrayLayers: data[2] ?? 1,
-    };
-  } else {
-    return {
-      width: data.width,
-      height: data.height ?? 1,
-      depthOrArrayLayers: data.depthOrArrayLayers ?? 1,
-    };
-  }
-}
-
-/**
- * @param {number[] | GPUOrigin3DDict} data
- * @returns {GPUOrigin3DDict}
- */
-function normalizeGPUOrigin3D(data) {
-  if (ArrayIsArray(data)) {
-    return {
-      x: data[0],
-      y: data[1],
-      z: data[2],
-    };
-  } else {
-    return data;
-  }
-}
-
-/**
- * @param {number[] | GPUColor} data
- * @returns {GPUColor}
- */
-function normalizeGPUColor(data) {
-  if (ArrayIsArray(data)) {
-    return {
-      r: data[0],
-      g: data[1],
-      b: data[2],
-      a: data[3],
-    };
-  } else {
-    return data;
-  }
-}
-
 const illegalConstructorKey = Symbol("illegalConstructorKey");
-class GPUError extends Error {
+class GPUError {
   constructor(key = null) {
-    super();
     if (key !== illegalConstructorKey) {
       webidl.illegalConstructor();
     }
@@ -271,11 +68,23 @@ class GPUError extends Error {
     webidl.assertBranded(this, GPUErrorPrototype);
     return this[_message];
   }
+
+  [customInspect](inspect, inspectOptions) {
+    return inspect(
+      createFilteredInspectProxy({
+        object: this,
+        evaluate: ObjectPrototypeIsPrototypeOf(GPUErrorPrototype, this),
+        keys: [
+          "message",
+        ],
+      }),
+      inspectOptions,
+    );
+  }
 }
 const GPUErrorPrototype = GPUError.prototype;
 
 class GPUValidationError extends GPUError {
-  name = "GPUValidationError";
   /** @param {string} message */
   constructor(message) {
     const prefix = "Failed to construct 'GPUValidationError'";
@@ -286,9 +95,9 @@ class GPUValidationError extends GPUError {
     this[_message] = message;
   }
 }
+core.registerErrorClass("GPUValidationError", GPUValidationError);
 
 class GPUOutOfMemoryError extends GPUError {
-  name = "GPUOutOfMemoryError";
   constructor(message) {
     const prefix = "Failed to construct 'GPUOutOfMemoryError'";
     webidl.requiredArguments(arguments.length, 1, prefix);
@@ -298,14 +107,38 @@ class GPUOutOfMemoryError extends GPUError {
     this[_message] = message;
   }
 }
+core.registerErrorClass("GPUOutOfMemoryError", GPUOutOfMemoryError);
 
 class GPUInternalError extends GPUError {
-  name = "GPUInternalError";
   constructor() {
     super(illegalConstructorKey);
     this[webidl.brand] = webidl.brand;
   }
 }
+core.registerErrorClass("GPUInternalError", GPUInternalError);
+
+class GPUPipelineError extends DOMException {
+  #reason;
+
+  constructor(message = "", options = { __proto__: null }) {
+    const prefix = "Failed to construct 'GPUPipelineError'";
+    message = webidl.converters.DOMString(message, prefix, "Argument 1");
+    options = webidl.converters.GPUPipelineErrorInit(
+      options,
+      prefix,
+      "Argument 2",
+    );
+    super(message, "GPUPipelineError");
+
+    this.#reason = options.reason;
+  }
+
+  get reason() {
+    webidl.assertBranded(this, GPUPipelineErrorPrototype);
+    return this.#reason;
+  }
+}
+const GPUPipelineErrorPrototype = GPUPipelineError.prototype;
 
 class GPUUncapturedErrorEvent extends Event {
   #error;
@@ -333,193 +166,16 @@ class GPUUncapturedErrorEvent extends Event {
 }
 const GPUUncapturedErrorEventPrototype = GPUUncapturedErrorEvent.prototype;
 
-class GPU {
-  [webidl.brand] = webidl.brand;
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {GPURequestAdapterOptions} options
-   */
-  // deno-lint-ignore require-await
-  async requestAdapter(options = { __proto__: null }) {
-    webidl.assertBranded(this, GPUPrototype);
-    options = webidl.converters.GPURequestAdapterOptions(
-      options,
-      "Failed to execute 'requestAdapter' on 'GPU'",
-      "Argument 1",
-    );
-
-    const { err, ...data } = op_webgpu_request_adapter(
-      options.powerPreference,
-      options.forceFallbackAdapter,
-    );
-
-    if (err) {
-      return null;
-    } else {
-      return createGPUAdapter(data);
-    }
-  }
-
-  getPreferredCanvasFormat() {
-    // Same as Gecko.
-    //
-    // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47
-    if (core.build.os == "android") {
-      return "rgba8unorm";
-    }
-    return "bgra8unorm";
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPU, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return `${this.constructor.name} ${inspect({}, inspectOptions)}`;
-  }
-}
-const GPUPrototype = GPU.prototype;
+  },
+});
 
-/**
- * @typedef InnerGPUAdapter
- * @property {number} rid
- * @property {GPUSupportedFeatures} features
- * @property {GPUSupportedLimits} limits
- * @property {boolean} isFallbackAdapter
- */
-
-/**
- * @param {InnerGPUAdapter} inner
- * @returns {GPUAdapter}
- */
-function createGPUAdapter(inner) {
-  /** @type {GPUAdapter} */
-  const adapter = webidl.createBranded(GPUAdapter);
-  adapter[_adapter] = {
-    ...inner,
-    features: createGPUSupportedFeatures(inner.features),
-    limits: createGPUSupportedLimits(inner.limits),
-  };
-  return adapter;
-}
-
-class GPUAdapter {
-  /** @type {InnerGPUAdapter} */
-  [_adapter];
-  [_adapterInfo];
-  /** @type {boolean} */
-  [_invalid];
-
-  /** @returns {GPUSupportedFeatures} */
-  get features() {
-    webidl.assertBranded(this, GPUAdapterPrototype);
-    return this[_adapter].features;
-  }
-  /** @returns {GPUSupportedLimits} */
-  get limits() {
-    webidl.assertBranded(this, GPUAdapterPrototype);
-    return this[_adapter].limits;
-  }
-  /** @returns {boolean} */
-  get isFallbackAdapter() {
-    webidl.assertBranded(this, GPUAdapterPrototype);
-    return this[_adapter].isFallback;
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {GPUDeviceDescriptor} descriptor
-   * @returns {Promise<GPUDevice>}
-   */
-  // deno-lint-ignore require-await
-  async requestDevice(descriptor = { __proto__: null }) {
-    webidl.assertBranded(this, GPUAdapterPrototype);
-    const prefix = "Failed to execute 'requestDevice' on 'GPUAdapter'";
-    descriptor = webidl.converters.GPUDeviceDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const requiredFeatures = descriptor.requiredFeatures ?? [];
-    for (let i = 0; i < requiredFeatures.length; ++i) {
-      const feature = requiredFeatures[i];
-      if (
-        !SetPrototypeHas(this[_adapter].features[webidl.setlikeInner], feature)
-      ) {
-        throw new TypeError(
-          `${prefix}: requiredFeatures must be a subset of the adapter features`,
-        );
-      }
-    }
-
-    if (this[_invalid]) {
-      throw new TypeError(
-        "The adapter cannot be reused, as it has been invalidated by a device creation",
-      );
-    }
-
-    const { rid, queueRid, features, limits } = op_webgpu_request_device(
-      this[_adapter].rid,
-      descriptor.label,
-      requiredFeatures,
-      descriptor.requiredLimits,
-    );
-
-    this[_invalid] = true;
-
-    const inner = new InnerGPUDevice({
-      rid,
-      adapter: this,
-      features: createGPUSupportedFeatures(features),
-      limits: createGPUSupportedLimits(limits),
-    });
-    const queue = createGPUQueue(descriptor.label, inner, queueRid);
-    inner.trackResource(queue);
-    const device = createGPUDevice(
-      descriptor.label,
-      inner,
-      queue,
-    );
-    inner.device = device;
-    return device;
-  }
-
-  /**
-   * @returns {GPUAdapterInfo}
-   */
-  get info() {
-    webidl.assertBranded(this, GPUAdapterPrototype);
-
-    if (this[_adapterInfo] !== undefined) {
-      return this[_adapterInfo];
-    }
-
-    if (this[_invalid]) {
-      throw new TypeError(
-        "The adapter cannot be reused, as it has been invalidated by a device creation",
-      );
-    }
-
-    const {
-      vendor,
-      architecture,
-      device,
-      description,
-    } = op_webgpu_request_adapter_info(this[_adapter].rid);
-
-    const adapterInfo = webidl.createBranded(GPUAdapterInfo);
-    adapterInfo[_vendor] = vendor;
-    adapterInfo[_architecture] = architecture;
-    adapterInfo[_device] = device;
-    adapterInfo[_description] = description;
-    this[_adapterInfo] = adapterInfo;
-    return adapterInfo;
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUAdapter, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -533,44 +189,13 @@ class GPUAdapter {
       }),
       inspectOptions,
     );
-  }
-}
+  },
+});
 const GPUAdapterPrototype = GPUAdapter.prototype;
 
-class GPUAdapterInfo {
-  /** @type {string} */
-  [_vendor];
-  /** @returns {string} */
-  get vendor() {
-    webidl.assertBranded(this, GPUAdapterInfoPrototype);
-    return this[_vendor];
-  }
-
-  /** @type {string} */
-  [_architecture];
-  /** @returns {string} */
-  get architecture() {
-    webidl.assertBranded(this, GPUAdapterInfoPrototype);
-    return this[_architecture];
-  }
-
-  /** @type {string} */
-  [_device];
-  /** @returns {string} */
-  get device() {
-    webidl.assertBranded(this, GPUAdapterInfoPrototype);
-    return this[_device];
-  }
-
-  /** @type {string} */
-  [_description];
-  /** @returns {string} */
-  get description() {
-    webidl.assertBranded(this, GPUAdapterInfoPrototype);
-    return this[_description];
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUAdapterInfo, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -584,256 +209,13 @@ class GPUAdapterInfo {
       }),
       inspectOptions,
     );
-  }
-}
+  },
+});
 const GPUAdapterInfoPrototype = GPUAdapterInfo.prototype;
 
-function createGPUSupportedLimits(limits) {
-  /** @type {GPUSupportedLimits} */
-  const adapterFeatures = webidl.createBranded(GPUSupportedLimits);
-  adapterFeatures[_limits] = limits;
-  return adapterFeatures;
-}
-
-function normalizeLimit(limit) {
-  if (typeof limit === "bigint") {
-    limit = Number(limit);
-    if (limit === NumberPOSITIVE_INFINITY) {
-      limit = NumberMAX_SAFE_INTEGER;
-    } else if (limit === NumberNEGATIVE_INFINITY) {
-      limit = NumberMIN_SAFE_INTEGER;
-    }
-  }
-  return limit;
-}
-
-/**
- * @typedef InnerAdapterLimits
- * @property {number} maxTextureDimension1D
- * @property {number} maxTextureDimension2D
- * @property {number} maxTextureDimension3D
- * @property {number} maxTextureArrayLayers
- * @property {number} maxBindGroups
- * @property {number} maxDynamicUniformBuffersPerPipelineLayout
- * @property {number} maxDynamicStorageBuffersPerPipelineLayout
- * @property {number} maxSampledTexturesPerShaderStage
- * @property {number} maxSamplersPerShaderStage
- * @property {number} maxStorageBuffersPerShaderStage
- * @property {number} maxStorageTexturesPerShaderStage
- * @property {number} maxUniformBuffersPerShaderStage
- * @property {number} maxUniformBufferBindingSize
- * @property {number} maxStorageBufferBindingSize
- * @property {number} minUniformBufferOffsetAlignment
- * @property {number} minStorageBufferOffsetAlignment
- * @property {number} maxVertexBuffers
- * @property {number} maxVertexAttributes
- * @property {number} maxVertexBufferArrayStride
- * @property {number} maxInterStageShaderComponents
- * @property {number} maxComputeWorkgroupStorageSize
- * @property {number} maxComputeInvocationsPerWorkgroup
- * @property {number} maxComputeWorkgroupSizeX
- * @property {number} maxComputeWorkgroupSizeY
- * @property {number} maxComputeWorkgroupSizeZ
- * @property {number} maxComputeWorkgroupsPerDimension
- */
-
-class GPUSupportedLimits {
-  /** @type {InnerAdapterLimits} */
-  [_limits];
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  get maxTextureDimension1D() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxTextureDimension1D);
-  }
-  get maxTextureDimension2D() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxTextureDimension2D);
-  }
-  get maxTextureDimension3D() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxTextureDimension3D);
-  }
-  get maxTextureArrayLayers() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxTextureArrayLayers);
-  }
-  get maxBindGroups() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxBindGroups);
-  }
-  get maxBindingsPerBindGroup() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxBindingsPerBindGroup);
-  }
-  get maxBufferSize() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxBufferSize);
-  }
-  get maxDynamicUniformBuffersPerPipelineLayout() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(
-      this[_limits].maxDynamicUniformBuffersPerPipelineLayout,
-    );
-  }
-  get maxDynamicStorageBuffersPerPipelineLayout() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(
-      this[_limits].maxDynamicStorageBuffersPerPipelineLayout,
-    );
-  }
-  get maxSampledTexturesPerShaderStage() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxSampledTexturesPerShaderStage);
-  }
-  get maxSamplersPerShaderStage() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxSamplersPerShaderStage);
-  }
-  get maxStorageBuffersPerShaderStage() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxStorageBuffersPerShaderStage);
-  }
-  get maxStorageTexturesPerShaderStage() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxStorageTexturesPerShaderStage);
-  }
-  get maxUniformBuffersPerShaderStage() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxUniformBuffersPerShaderStage);
-  }
-  get maxUniformBufferBindingSize() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxUniformBufferBindingSize);
-  }
-  get maxStorageBufferBindingSize() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxStorageBufferBindingSize);
-  }
-  get minUniformBufferOffsetAlignment() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].minUniformBufferOffsetAlignment);
-  }
-  get minStorageBufferOffsetAlignment() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].minStorageBufferOffsetAlignment);
-  }
-  get maxVertexBuffers() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxVertexBuffers);
-  }
-  get maxVertexAttributes() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxVertexAttributes);
-  }
-  get maxVertexBufferArrayStride() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxVertexBufferArrayStride);
-  }
-  get maxInterStageShaderComponents() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxInterStageShaderComponents);
-  }
-  get maxColorAttachments() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxColorAttachments);
-  }
-  get maxColorAttachmentBytesPerSample() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxColorAttachmentBytesPerSample);
-  }
-  get maxComputeWorkgroupStorageSize() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxComputeWorkgroupStorageSize);
-  }
-  get maxComputeInvocationsPerWorkgroup() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxComputeInvocationsPerWorkgroup);
-  }
-  get maxComputeWorkgroupSizeX() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxComputeWorkgroupSizeX);
-  }
-  get maxComputeWorkgroupSizeY() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxComputeWorkgroupSizeY);
-  }
-  get maxComputeWorkgroupSizeZ() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxComputeWorkgroupSizeZ);
-  }
-  get maxComputeWorkgroupsPerDimension() {
-    webidl.assertBranded(this, GPUSupportedLimitsPrototype);
-    return normalizeLimit(this[_limits].maxComputeWorkgroupsPerDimension);
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
-    return inspect(
-      createFilteredInspectProxy({
-        object: this,
-        evaluate: ObjectPrototypeIsPrototypeOf(
-          GPUSupportedLimitsPrototype,
-          this,
-        ),
-        keys: [
-          "maxTextureDimension1D",
-          "maxTextureDimension2D",
-          "maxTextureDimension3D",
-          "maxTextureArrayLayers",
-          "maxBindGroups",
-          "maxBindingsPerBindGroup",
-          "maxBufferSize",
-          "maxDynamicUniformBuffersPerPipelineLayout",
-          "maxDynamicStorageBuffersPerPipelineLayout",
-          "maxSampledTexturesPerShaderStage",
-          "maxSamplersPerShaderStage",
-          "maxStorageBuffersPerShaderStage",
-          "maxStorageTexturesPerShaderStage",
-          "maxUniformBuffersPerShaderStage",
-          "maxUniformBufferBindingSize",
-          "maxStorageBufferBindingSize",
-          "minUniformBufferOffsetAlignment",
-          "minStorageBufferOffsetAlignment",
-          "maxVertexBuffers",
-          "maxVertexAttributes",
-          "maxVertexBufferArrayStride",
-          "maxInterStageShaderComponents",
-          "maxColorAttachments",
-          "maxColorAttachmentBytesPerSample",
-          "maxComputeWorkgroupStorageSize",
-          "maxComputeInvocationsPerWorkgroup",
-          "maxComputeWorkgroupSizeX",
-          "maxComputeWorkgroupSizeY",
-          "maxComputeWorkgroupSizeZ",
-          "maxComputeWorkgroupsPerDimension",
-        ],
-      }),
-      inspectOptions,
-    );
-  }
-}
-const GPUSupportedLimitsPrototype = GPUSupportedLimits.prototype;
-
-function createGPUSupportedFeatures(features) {
-  /** @type {GPUSupportedFeatures} */
-  const supportedFeatures = webidl.createBranded(GPUSupportedFeatures);
-  supportedFeatures[webidl.setlikeInner] = new SafeSet(features);
-  webidl.setlike(
-    supportedFeatures,
-    GPUSupportedFeaturesPrototype,
-    true,
-  );
-  return supportedFeatures;
-}
-
-class GPUSupportedFeatures {
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUSupportedFeatures, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) {
       return `${this.constructor.name} ${
         // deno-lint-ignore prefer-primordials
@@ -841,43 +223,14 @@ class GPUSupportedFeatures {
     } else {
       return `${this.constructor.name} ${inspect({}, inspectOptions)}`;
     }
-  }
-}
+  },
+});
 const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype;
+webidl.setlikeObjectWrap(GPUSupportedFeaturesPrototype, true);
 
-/**
- * @param {string | undefined} reason
- * @param {string} message
- * @returns {GPUDeviceLostInfo}
- */
-function createGPUDeviceLostInfo(reason, message) {
-  /** @type {GPUDeviceLostInfo} */
-  const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo);
-  deviceLostInfo[_reason] = reason ?? "unknown";
-  deviceLostInfo[_message] = message;
-  return deviceLostInfo;
-}
-
-class GPUDeviceLostInfo {
-  /** @type {string} */
-  [_reason];
-  /** @type {string} */
-  [_message];
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  get reason() {
-    webidl.assertBranded(this, GPUDeviceLostInfoPrototype);
-    return this[_reason];
-  }
-  get message() {
-    webidl.assertBranded(this, GPUDeviceLostInfoPrototype);
-    return this[_message];
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUDeviceLostInfo, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -892,884 +245,13 @@ class GPUDeviceLostInfo {
       }),
       inspectOptions,
     );
-  }
-}
-
+  },
+});
 const GPUDeviceLostInfoPrototype = GPUDeviceLostInfo.prototype;
 
-/**
- * @param {string} name
- * @param {any} type
- */
-function GPUObjectBaseMixin(name, type) {
-  type.prototype[_label] = null;
-  ObjectDefineProperty(type.prototype, "label", {
-    __proto__: null,
-    /**
-     * @return {string | null}
-     */
-    get() {
-      webidl.assertBranded(this, type.prototype);
-      return this[_label];
-    },
-    /**
-     * @param {string | null} label
-     */
-    set(label) {
-      webidl.assertBranded(this, type.prototype);
-      label = webidl.converters["UVString?"](
-        label,
-        `Failed to set 'label' on '${name}'`,
-        "Argument 1",
-      );
-      this[_label] = label;
-    },
-  });
-}
-
-/**
- * @typedef ErrorScope
- * @property {string} filter
- * @property {GPUError[]} errors
- */
-
-/**
- * @typedef InnerGPUDeviceOptions
- * @property {GPUAdapter} adapter
- * @property {number | undefined} rid
- * @property {GPUSupportedFeatures} features
- * @property {GPUSupportedLimits} limits
- * @property {GPUDevice} device
- */
-
-class InnerGPUDevice {
-  /** @type {GPUAdapter} */
-  adapter;
-  /** @type {number | undefined} */
-  rid;
-  /** @type {GPUSupportedFeatures} */
-  features;
-  /** @type {GPUSupportedLimits} */
-  limits;
-  /** @type {SafeWeakRef<any>[]} */
-  resources;
-  /** @type {boolean} */
-  isLost;
-  /** @type {Promise<GPUDeviceLostInfo>} */
-  lost;
-  /** @type {(info: GPUDeviceLostInfo) => void} */
-  resolveLost;
-  /** @type {ErrorScope[]} */
-  errorScopeStack;
-  /** @type {GPUDevice} */
-  device;
-
-  /**
-   * @param {InnerGPUDeviceOptions} options
-   */
-  constructor(options) {
-    this.adapter = options.adapter;
-    this.rid = options.rid;
-    this.features = options.features;
-    this.limits = options.limits;
-    this.resources = [];
-    this.isLost = false;
-    this.resolveLost = () => {};
-    this.lost = new Promise((resolve) => {
-      this.resolveLost = resolve;
-    });
-    this.errorScopeStack = [];
-  }
-
-  /** @param {any} resource */
-  trackResource(resource) {
-    ArrayPrototypePush(this.resources, new SafeWeakRef(resource));
-  }
-
-  // Ref: https://gpuweb.github.io/gpuweb/#abstract-opdef-dispatch-error
-  /** @param {{ type: string, value: string | null } | undefined} error */
-  pushError(error) {
-    if (!error) {
-      return;
-    }
-
-    let constructedError;
-    switch (error.type) {
-      case "lost":
-        this.isLost = true;
-        this.resolveLost(
-          createGPUDeviceLostInfo(undefined, "device was lost"),
-        );
-        return;
-      case "validation":
-        constructedError = new GPUValidationError(
-          error.value ?? "validation error",
-        );
-        break;
-      case "out-of-memory":
-        constructedError = new GPUOutOfMemoryError("not enough memory left");
-        break;
-      case "internal":
-        constructedError = new GPUInternalError();
-        break;
-    }
-
-    if (this.isLost) {
-      return;
-    }
-
-    const scope = ArrayPrototypeFindLast(
-      this.errorScopeStack,
-      ({ filter }) => filter === error.type,
-    );
-    if (scope) {
-      ArrayPrototypePush(scope.errors, constructedError);
-    } else {
-      this.device.dispatchEvent(
-        new GPUUncapturedErrorEvent("uncapturederror", {
-          error: constructedError,
-        }),
-      );
-    }
-  }
-}
-
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} inner
- * @param {GPUQueue} queue
- * @returns {GPUDevice}
- */
-function createGPUDevice(label, inner, queue) {
-  /** @type {GPUDevice} */
-  const device = webidl.createBranded(GPUDevice);
-  device[_label] = label;
-  device[_device] = inner;
-  device[_queue] = queue;
-  setEventTargetData(device);
-  return device;
-}
-
-class GPUDevice extends EventTarget {
-  /** @type {InnerGPUDevice} */
-  [_device];
-
-  /** @type {GPUQueue} */
-  [_queue];
-
-  [_cleanup]() {
-    const device = this[_device];
-    const resources = device.resources;
-    while (resources.length > 0) {
-      const resource = ArrayPrototypePop(resources)?.deref();
-      if (resource) {
-        resource[_cleanup]();
-      }
-    }
-    const rid = device.rid;
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      device.rid = undefined;
-    }
-  }
-
-  get features() {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    return this[_device].features;
-  }
-  get limits() {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    return this[_device].limits;
-  }
-  get queue() {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    return this[_queue];
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-    super();
-  }
-
-  destroy() {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    this[_cleanup]();
-  }
-
-  /**
-   * @param {GPUBufferDescriptor} descriptor
-   * @returns {GPUBuffer}
-   */
-  createBuffer(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createBuffer' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUBufferDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const { rid, err } = op_webgpu_create_buffer(
-      device.rid,
-      descriptor.label,
-      descriptor.size,
-      descriptor.usage,
-      descriptor.mappedAtCreation,
-    );
-    device.pushError(err);
-    /** @type {CreateGPUBufferOptions} */
-    let options;
-    if (descriptor.mappedAtCreation) {
-      options = {
-        mapping: new ArrayBuffer(descriptor.size),
-        mappingRange: [0, descriptor.size],
-        mappedRanges: [],
-        state: "mapped at creation",
-      };
-    } else {
-      options = {
-        mapping: null,
-        mappedRanges: null,
-        mappingRange: null,
-        state: "unmapped",
-      };
-    }
-    const buffer = createGPUBuffer(
-      descriptor.label,
-      device,
-      rid,
-      descriptor.size,
-      descriptor.usage,
-      options,
-    );
-    device.trackResource(buffer);
-    return buffer;
-  }
-
-  /**
-   * @param {GPUTextureDescriptor} descriptor
-   * @returns {GPUTexture}
-   */
-  createTexture(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createTexture' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUTextureDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    // assign normalized size to descriptor due to createGPUTexture needs it
-    descriptor.size = normalizeGPUExtent3D(descriptor.size);
-    const { rid, err } = op_webgpu_create_texture({
-      deviceRid: device.rid,
-      ...descriptor,
-    });
-    device.pushError(err);
-
-    const texture = createGPUTexture(
-      descriptor,
-      device,
-      rid,
-    );
-    device.trackResource(texture);
-    return texture;
-  }
-
-  /**
-   * @param {GPUSamplerDescriptor} descriptor
-   * @returns {GPUSampler}
-   */
-  createSampler(descriptor = { __proto__: null }) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createSampler' on 'GPUDevice'";
-    descriptor = webidl.converters.GPUSamplerDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const { rid, err } = op_webgpu_create_sampler({
-      deviceRid: device.rid,
-      ...descriptor,
-    });
-    device.pushError(err);
-
-    const sampler = createGPUSampler(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(sampler);
-    return sampler;
-  }
-
-  /**
-   * @param {GPUBindGroupLayoutDescriptor} descriptor
-   * @returns {GPUBindGroupLayout}
-   */
-  createBindGroupLayout(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createBindGroupLayout' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUBindGroupLayoutDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    for (let i = 0; i < descriptor.entries.length; ++i) {
-      const entry = descriptor.entries[i];
-
-      let j = 0;
-      // deno-lint-ignore prefer-primordials
-      if (entry.buffer) j++;
-      if (entry.sampler) j++;
-      if (entry.texture) j++;
-      if (entry.storageTexture) j++;
-
-      if (j !== 1) {
-        throw new Error(); // TODO(@crowlKats): correct error
-      }
-    }
-
-    const { rid, err } = op_webgpu_create_bind_group_layout(
-      device.rid,
-      descriptor.label,
-      descriptor.entries,
-    );
-    device.pushError(err);
-
-    const bindGroupLayout = createGPUBindGroupLayout(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(bindGroupLayout);
-    return bindGroupLayout;
-  }
-
-  /**
-   * @param {GPUPipelineLayoutDescriptor} descriptor
-   * @returns {GPUPipelineLayout}
-   */
-  createPipelineLayout(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createPipelineLayout' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUPipelineLayoutDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const bindGroupLayouts = ArrayPrototypeMap(
-      descriptor.bindGroupLayouts,
-      (layout, i) => {
-        const context = `bind group layout ${i + 1}`;
-        const rid = assertResource(layout, prefix, context);
-        return rid;
-      },
-    );
-    const { rid, err } = op_webgpu_create_pipeline_layout(
-      device.rid,
-      descriptor.label,
-      bindGroupLayouts,
-    );
-    device.pushError(err);
-
-    const pipelineLayout = createGPUPipelineLayout(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(pipelineLayout);
-    return pipelineLayout;
-  }
-
-  /**
-   * @param {GPUBindGroupDescriptor} descriptor
-   * @returns {GPUBindGroup}
-   */
-  createBindGroup(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createBindGroup' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUBindGroupDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const layout = assertResource(descriptor.layout, prefix, "layout");
-    const entries = ArrayPrototypeMap(descriptor.entries, (entry, i) => {
-      const context = `entry ${i + 1}`;
-      const resource = entry.resource;
-      if (ObjectPrototypeIsPrototypeOf(GPUSamplerPrototype, resource)) {
-        const rid = assertResource(resource, prefix, context);
-        return {
-          binding: entry.binding,
-          kind: "GPUSampler",
-          resource: rid,
-        };
-      } else if (
-        ObjectPrototypeIsPrototypeOf(GPUTextureViewPrototype, resource)
-      ) {
-        const rid = assertResource(resource, prefix, context);
-        assertResource(resource[_texture], prefix, context);
-        return {
-          binding: entry.binding,
-          kind: "GPUTextureView",
-          resource: rid,
-        };
-      } else {
-        // deno-lint-ignore prefer-primordials
-        const rid = assertResource(resource.buffer, prefix, context);
-        return {
-          binding: entry.binding,
-          kind: "GPUBufferBinding",
-          resource: rid,
-          offset: entry.resource.offset,
-          size: entry.resource.size,
-        };
-      }
-    });
-
-    const { rid, err } = op_webgpu_create_bind_group(
-      device.rid,
-      descriptor.label,
-      layout,
-      entries,
-    );
-    device.pushError(err);
-
-    const bindGroup = createGPUBindGroup(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(bindGroup);
-    return bindGroup;
-  }
-
-  /**
-   * @param {GPUShaderModuleDescriptor} descriptor
-   */
-  createShaderModule(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createShaderModule' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUShaderModuleDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const { rid, err } = op_webgpu_create_shader_module(
-      device.rid,
-      descriptor.label,
-      descriptor.code,
-    );
-    device.pushError(err);
-
-    const shaderModule = createGPUShaderModule(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(shaderModule);
-    return shaderModule;
-  }
-
-  /**
-   * @param {GPUComputePipelineDescriptor} descriptor
-   * @returns {GPUComputePipeline}
-   */
-  createComputePipeline(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createComputePipeline' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUComputePipelineDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    let layout = descriptor.layout;
-    if (typeof descriptor.layout !== "string") {
-      const context = "layout";
-      layout = assertResource(descriptor.layout, prefix, context);
-    }
-    const module = assertResource(
-      descriptor.compute.module,
-      prefix,
-      "compute shader module",
-    );
-
-    const { rid, err } = op_webgpu_create_compute_pipeline(
-      device.rid,
-      descriptor.label,
-      layout,
-      {
-        module,
-        entryPoint: descriptor.compute.entryPoint,
-        constants: descriptor.compute.constants,
-      },
-    );
-    device.pushError(err);
-
-    const computePipeline = createGPUComputePipeline(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(computePipeline);
-    return computePipeline;
-  }
-
-  /**
-   * @param {GPURenderPipelineDescriptor} descriptor
-   * @returns {GPURenderPipeline}
-   */
-  createRenderPipeline(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createRenderPipeline' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPURenderPipelineDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    let layout = descriptor.layout;
-    if (typeof descriptor.layout !== "string") {
-      const context = "layout";
-      layout = assertResource(descriptor.layout, prefix, context);
-    }
-    const module = assertResource(
-      descriptor.vertex.module,
-      prefix,
-      "vertex shader module",
-    );
-    let fragment = undefined;
-    if (descriptor.fragment) {
-      const module = assertResource(
-        descriptor.fragment.module,
-        prefix,
-        "fragment shader module",
-      );
-      fragment = {
-        module,
-        entryPoint: descriptor.fragment.entryPoint,
-        constants: descriptor.fragment.constants,
-        targets: descriptor.fragment.targets,
-      };
-    }
-
-    const { rid, err } = op_webgpu_create_render_pipeline({
-      deviceRid: device.rid,
-      label: descriptor.label,
-      layout,
-      vertex: {
-        module,
-        entryPoint: descriptor.vertex.entryPoint,
-        constants: descriptor.vertex.constants,
-        buffers: descriptor.vertex.buffers,
-      },
-      primitive: descriptor.primitive,
-      depthStencil: descriptor.depthStencil,
-      multisample: descriptor.multisample,
-      fragment,
-    });
-    device.pushError(err);
-
-    const renderPipeline = createGPURenderPipeline(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(renderPipeline);
-    return renderPipeline;
-  }
-
-  createComputePipelineAsync(descriptor) {
-    // TODO(lucacasonato): this should be real async
-
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix =
-      "Failed to execute 'createComputePipelineAsync' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUComputePipelineDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    let layout = descriptor.layout;
-    if (typeof descriptor.layout !== "string") {
-      const context = "layout";
-      layout = assertResource(descriptor.layout, prefix, context);
-    }
-    const module = assertResource(
-      descriptor.compute.module,
-      prefix,
-      "compute shader module",
-    );
-
-    const { rid, err } = op_webgpu_create_compute_pipeline(
-      device.rid,
-      descriptor.label,
-      layout,
-      {
-        module,
-        entryPoint: descriptor.compute.entryPoint,
-        constants: descriptor.compute.constants,
-      },
-    );
-    device.pushError(err);
-    if (err) {
-      switch (err.type) {
-        case "validation":
-          return PromiseReject(
-            new GPUPipelineError(err.value ?? "validation error", {
-              reason: "validation",
-            }),
-          );
-        case "internal":
-          return PromiseReject(
-            new GPUPipelineError("internal error", {
-              reason: "validation",
-            }),
-          );
-      }
-    }
-
-    const computePipeline = createGPUComputePipeline(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(computePipeline);
-    return PromiseResolve(computePipeline);
-  }
-
-  createRenderPipelineAsync(descriptor) {
-    // TODO(lucacasonato): this should be real async
-
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix =
-      "Failed to execute 'createRenderPipelineAsync' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPURenderPipelineDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    let layout = descriptor.layout;
-    if (typeof descriptor.layout !== "string") {
-      const context = "layout";
-      layout = assertResource(descriptor.layout, prefix, context);
-    }
-    const module = assertResource(
-      descriptor.vertex.module,
-      prefix,
-      "vertex shader module",
-    );
-    let fragment = undefined;
-    if (descriptor.fragment) {
-      const module = assertResource(
-        descriptor.fragment.module,
-        prefix,
-        "fragment shader module",
-      );
-      fragment = {
-        module,
-        entryPoint: descriptor.fragment.entryPoint,
-        constants: descriptor.fragment.constants,
-        targets: descriptor.fragment.targets,
-      };
-    }
-
-    const { rid, err } = op_webgpu_create_render_pipeline({
-      deviceRid: device.rid,
-      label: descriptor.label,
-      layout,
-      vertex: {
-        module,
-        entryPoint: descriptor.vertex.entryPoint,
-        constants: descriptor.vertex.constants,
-        buffers: descriptor.vertex.buffers,
-      },
-      primitive: descriptor.primitive,
-      depthStencil: descriptor.depthStencil,
-      multisample: descriptor.multisample,
-      fragment,
-    });
-    device.pushError(err);
-    if (err) {
-      switch (err.type) {
-        case "validation":
-          return PromiseReject(
-            new GPUPipelineError(err.value ?? "validation error", {
-              reason: "validation",
-            }),
-          );
-        case "internal":
-          return PromiseReject(
-            new GPUPipelineError("internal error", {
-              reason: "validation",
-            }),
-          );
-      }
-    }
-
-    const renderPipeline = createGPURenderPipeline(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(renderPipeline);
-    return PromiseResolve(renderPipeline);
-  }
-
-  /**
-   * @param {GPUCommandEncoderDescriptor} descriptor
-   * @returns {GPUCommandEncoder}
-   */
-  createCommandEncoder(descriptor = { __proto__: null }) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createCommandEncoder' on 'GPUDevice'";
-    descriptor = webidl.converters.GPUCommandEncoderDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const { rid, err } = op_webgpu_create_command_encoder(
-      device.rid,
-      descriptor.label,
-    );
-    device.pushError(err);
-
-    const commandEncoder = createGPUCommandEncoder(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(commandEncoder);
-    return commandEncoder;
-  }
-
-  /**
-   * @param {GPURenderBundleEncoderDescriptor} descriptor
-   * @returns {GPURenderBundleEncoder}
-   */
-  createRenderBundleEncoder(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix =
-      "Failed to execute 'createRenderBundleEncoder' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, { prefix });
-    descriptor = webidl.converters.GPURenderBundleEncoderDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const { rid, err } = op_webgpu_create_render_bundle_encoder({
-      deviceRid: device.rid,
-      ...descriptor,
-    });
-    device.pushError(err);
-
-    const renderBundleEncoder = createGPURenderBundleEncoder(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(renderBundleEncoder);
-    return renderBundleEncoder;
-  }
-
-  /**
-   * @param {GPUQuerySetDescriptor} descriptor
-   * @returns {GPUQuerySet}
-   */
-  createQuerySet(descriptor) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'createQuerySet' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPUQuerySetDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const { rid, err } = op_webgpu_create_query_set({
-      deviceRid: device.rid,
-      ...descriptor,
-    });
-    device.pushError(err);
-
-    const querySet = createGPUQuerySet(
-      descriptor.label,
-      device,
-      rid,
-      descriptor,
-    );
-    device.trackResource(querySet);
-    return querySet;
-  }
-
-  get lost() {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const device = this[_device];
-    if (!device) {
-      return PromiseResolve(true);
-    }
-    if (device.rid === undefined) {
-      return PromiseResolve(true);
-    }
-    return device.lost;
-  }
-
-  /**
-   * @param {GPUErrorFilter} filter
-   */
-  pushErrorScope(filter) {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'pushErrorScope' on 'GPUDevice'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    filter = webidl.converters.GPUErrorFilter(filter, prefix, "Argument 1");
-    const device = assertDevice(this, prefix, "this");
-    ArrayPrototypePush(device.errorScopeStack, { filter, errors: [] });
-  }
-
-  /**
-   * @returns {Promise<GPUError | null>}
-   */
-  // deno-lint-ignore require-await
-  async popErrorScope() {
-    webidl.assertBranded(this, GPUDevicePrototype);
-    const prefix = "Failed to execute 'popErrorScope' on 'GPUDevice'";
-    const device = assertDevice(this, prefix, "this");
-    if (device.isLost) {
-      throw new DOMException("Device has been lost", "OperationError");
-    }
-    const scope = ArrayPrototypePop(device.errorScopeStack);
-    if (!scope) {
-      throw new DOMException(
-        "There are no error scopes on the error scope stack",
-        "OperationError",
-      );
-    }
-    return PromiseResolve(scope.errors[0] ?? null);
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUDevice, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -1786,206 +268,15 @@ class GPUDevice extends EventTarget {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUDevice", GPUDevice);
+  },
+});
 const GPUDevicePrototype = GPUDevice.prototype;
+ObjectSetPrototypeOf(GPUDevicePrototype, EventTargetPrototype);
 defineEventHandler(GPUDevice.prototype, "uncapturederror");
 
-class GPUPipelineError extends DOMException {
-  #reason;
-
-  constructor(message = "", options = { __proto__: null }) {
-    const prefix = "Failed to construct 'GPUPipelineError'";
-    message = webidl.converters.DOMString(message, prefix, "Argument 1");
-    options = webidl.converters.GPUPipelineErrorInit(
-      options,
-      prefix,
-      "Argument 2",
-    );
-    super(message, "GPUPipelineError");
-
-    this.#reason = options.reason;
-  }
-
-  get reason() {
-    webidl.assertBranded(this, GPUPipelineErrorPrototype);
-    return this.#reason;
-  }
-}
-const GPUPipelineErrorPrototype = GPUPipelineError.prototype;
-
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUQueue}
- */
-function createGPUQueue(label, device, rid) {
-  /** @type {GPUQueue} */
-  const queue = webidl.createBranded(GPUQueue);
-  queue[_label] = label;
-  queue[_device] = device;
-  queue[_rid] = rid;
-  return queue;
-}
-
-class GPUQueue {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {GPUCommandBuffer[]} commandBuffers
-   */
-  submit(commandBuffers) {
-    webidl.assertBranded(this, GPUQueuePrototype);
-    const prefix = "Failed to execute 'submit' on 'GPUQueue'";
-    webidl.requiredArguments(arguments.length, 1, {
-      prefix,
-    });
-    commandBuffers = webidl.converters["sequence<GPUCommandBuffer>"](
-      commandBuffers,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const commandBufferRids = ArrayPrototypeMap(
-      commandBuffers,
-      (buffer, i) => {
-        const context = `command buffer ${i + 1}`;
-        const rid = assertResource(buffer, prefix, context);
-        return rid;
-      },
-    );
-    const { err } = op_webgpu_queue_submit(this[_rid], commandBufferRids);
-    for (let i = 0; i < commandBuffers.length; ++i) {
-      commandBuffers[i][_rid] = undefined;
-    }
-    device.pushError(err);
-  }
-
-  onSubmittedWorkDone() {
-    webidl.assertBranded(this, GPUQueuePrototype);
-    return PromiseResolve();
-  }
-
-  /**
-   * @param {GPUBuffer} buffer
-   * @param {number} bufferOffset
-   * @param {BufferSource} data
-   * @param {number} [dataOffset]
-   * @param {number} [size]
-   */
-  writeBuffer(buffer, bufferOffset, data, dataOffset = 0, size) {
-    webidl.assertBranded(this, GPUQueuePrototype);
-    const prefix = "Failed to execute 'writeBuffer' on 'GPUQueue'";
-    webidl.requiredArguments(arguments.length, 3, prefix);
-    buffer = webidl.converters["GPUBuffer"](buffer, prefix, "Argument 1");
-    bufferOffset = webidl.converters["GPUSize64"](
-      bufferOffset,
-      prefix,
-      "Argument 2",
-    );
-    data = webidl.converters.BufferSource(data, prefix, "Argument 3");
-    dataOffset = webidl.converters["GPUSize64"](
-      dataOffset,
-      prefix,
-      "Argument 4",
-    );
-    size = size === undefined
-      ? undefined
-      : webidl.converters.GPUSize64(size, prefix, "Argument 5");
-    const device = assertDevice(this, prefix, "this");
-    const bufferRid = assertResource(buffer, prefix, "Argument 1");
-    /** @type {ArrayBufferLike} */
-    let abLike = data;
-    if (isTypedArray(data)) {
-      abLike = TypedArrayPrototypeGetBuffer(
-        /** @type {Uint8Array} */ (data),
-      );
-    } else if (isDataView(data)) {
-      abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data));
-    }
-
-    const { err } = op_webgpu_write_buffer(
-      this[_rid],
-      bufferRid,
-      bufferOffset,
-      dataOffset,
-      size,
-      new Uint8Array(abLike),
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUTexelCopyTextureInfo} destination
-   * @param {BufferSource} data
-   * @param {GPUTexelCopyBufferLayout} dataLayout
-   * @param {GPUExtent3D} size
-   */
-  writeTexture(destination, data, dataLayout, size) {
-    webidl.assertBranded(this, GPUQueuePrototype);
-    const prefix = "Failed to execute 'writeTexture' on 'GPUQueue'";
-    webidl.requiredArguments(arguments.length, 4, prefix);
-    destination = webidl.converters.GPUTexelCopyTextureInfo(
-      destination,
-      prefix,
-      "Argument 1",
-    );
-    data = webidl.converters.BufferSource(data, prefix, "Argument 2");
-    dataLayout = webidl.converters.GPUTexelCopyBufferLayout(
-      dataLayout,
-      prefix,
-      "Argument 3",
-    );
-    size = webidl.converters.GPUExtent3D(size, prefix, "Argument 4");
-    const device = assertDevice(this, prefix, "this");
-    const textureRid = assertResource(destination.texture, prefix, "texture");
-
-    /** @type {ArrayBufferLike} */
-    let abLike = data;
-    if (isTypedArray(data)) {
-      abLike = TypedArrayPrototypeGetBuffer(
-        /** @type {Uint8Array} */ (data),
-      );
-    } else if (isDataView(data)) {
-      abLike = DataViewPrototypeGetBuffer(/** @type {DataView} */ (data));
-    }
-
-    const { err } = op_webgpu_write_texture(
-      this[_rid],
-      {
-        texture: textureRid,
-        mipLevel: destination.mipLevel,
-        origin: destination.origin
-          ? normalizeGPUOrigin3D(destination.origin)
-          : undefined,
-        aspect: destination.aspect,
-      },
-      dataLayout,
-      normalizeGPUExtent3D(size),
-      new Uint8Array(abLike),
-    );
-    device.pushError(err);
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUQueue, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -1996,307 +287,13 @@ class GPUQueue {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUQueue", GPUQueue);
+  },
+});
 const GPUQueuePrototype = GPUQueue.prototype;
 
-/**
- * @typedef CreateGPUBufferOptions
- * @property {ArrayBuffer | null} mapping
- * @property {number[] | null} mappingRange
- * @property {[ArrayBuffer, number, number][] | null} mappedRanges
- * @property {"mapped" | "mapped at creation" | "mapped pending" | "unmapped" | "destroy" } state
- */
-
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @param {number} size
- * @param {number} usage
- * @param {CreateGPUBufferOptions} options
- * @returns {GPUBuffer}
- */
-function createGPUBuffer(label, device, rid, size, usage, options) {
-  /** @type {GPUBuffer} */
-  const buffer = webidl.createBranded(GPUBuffer);
-  buffer[_label] = label;
-  buffer[_device] = device;
-  buffer[_rid] = rid;
-  buffer[_size] = size;
-  buffer[_usage] = usage;
-  buffer[_mappingRange] = options.mappingRange;
-  buffer[_mappedRanges] = options.mappedRanges;
-  buffer[_state] = options.state;
-  return buffer;
-}
-
-class GPUBuffer {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number} */
-  [_rid];
-  /** @type {number} */
-  [_size];
-  /** @type {number} */
-  [_usage];
-  /** @type {"mapped" | "mapped at creation" | "pending" | "unmapped" | "destroy"} */
-  [_state];
-  /** @type {[number, number] | null} */
-  [_mappingRange];
-  /** @type {[ArrayBuffer, number, number][] | null} */
-  [_mappedRanges];
-  /** @type {number} */
-  [_mapMode];
-
-  [_cleanup]() {
-    const mappedRanges = this[_mappedRanges];
-    if (mappedRanges) {
-      while (mappedRanges.length > 0) {
-        const mappedRange = ArrayPrototypePop(mappedRanges);
-        if (mappedRange !== undefined) {
-          core.close(mappedRange[1]);
-        }
-      }
-    }
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-    this[_state] = "destroy";
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  get size() {
-    webidl.assertBranded(this, GPUBufferPrototype);
-    return this[_size];
-  }
-
-  get usage() {
-    webidl.assertBranded(this, GPUBufferPrototype);
-    return this[_usage];
-  }
-
-  get mapState() {
-    webidl.assertBranded(this, GPUBufferPrototype);
-    const state = this[_state];
-    if (state === "mapped at creation") {
-      return "mapped";
-    } else {
-      return state;
-    }
-  }
-
-  /**
-   * @param {number} mode
-   * @param {number} offset
-   * @param {number} [size]
-   */
-  async mapAsync(mode, offset = 0, size) {
-    webidl.assertBranded(this, GPUBufferPrototype);
-    const prefix = "Failed to execute 'mapAsync' on 'GPUBuffer'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    mode = webidl.converters.GPUMapModeFlags(mode, prefix, "Argument 1");
-    offset = webidl.converters.GPUSize64(offset, prefix, "Argument 2");
-    size = size === undefined
-      ? undefined
-      : webidl.converters.GPUSize64(size, prefix, "Argument 3");
-    const device = assertDevice(this, prefix, "this");
-    const bufferRid = assertResource(this, prefix, "this");
-    /** @type {number} */
-    let rangeSize;
-    if (size === undefined) {
-      rangeSize = MathMax(0, this[_size] - offset);
-    } else {
-      rangeSize = this[_size];
-    }
-    if ((offset % 8) !== 0) {
-      throw new DOMException(
-        `${prefix}: offset must be a multiple of 8, received ${offset}`,
-        "OperationError",
-      );
-    }
-    if ((rangeSize % 4) !== 0) {
-      throw new DOMException(
-        `${prefix}: rangeSize must be a multiple of 4, received ${rangeSize}`,
-        "OperationError",
-      );
-    }
-    if ((offset + rangeSize) > this[_size]) {
-      throw new DOMException(
-        `${prefix}: offset + rangeSize must be less than or equal to buffer size`,
-        "OperationError",
-      );
-    }
-    if (this[_state] !== "unmapped") {
-      throw new DOMException(
-        `${prefix}: GPUBuffer is not currently unmapped`,
-        "OperationError",
-      );
-    }
-    const readMode = (mode & 0x0001) === 0x0001;
-    const writeMode = (mode & 0x0002) === 0x0002;
-    if ((readMode && writeMode) || (!readMode && !writeMode)) {
-      throw new DOMException(
-        `${prefix}: exactly one of READ or WRITE map mode must be set`,
-        "OperationError",
-      );
-    }
-    if (readMode && !((this[_usage] && 0x0001) === 0x0001)) {
-      throw new DOMException(
-        `${prefix}: READ map mode not valid because buffer does not have MAP_READ usage`,
-        "OperationError",
-      );
-    }
-    if (writeMode && !((this[_usage] && 0x0002) === 0x0002)) {
-      throw new DOMException(
-        `${prefix}: WRITE map mode not valid because buffer does not have MAP_WRITE usage`,
-        "OperationError",
-      );
-    }
-
-    this[_mapMode] = mode;
-    this[_state] = "pending";
-    const { err } = await op_webgpu_buffer_get_map_async(
-      bufferRid,
-      device.rid,
-      mode,
-      offset,
-      rangeSize,
-    );
-    if (err) {
-      device.pushError(err);
-      throw new DOMException("validation error occurred", "OperationError");
-    }
-    this[_state] = "mapped";
-    this[_mappingRange] = [offset, offset + rangeSize];
-    /** @type {[ArrayBuffer, number, number][] | null} */
-    this[_mappedRanges] = [];
-  }
-
-  /**
-   * @param {number} offset
-   * @param {number} size
-   */
-  getMappedRange(offset = 0, size) {
-    webidl.assertBranded(this, GPUBufferPrototype);
-    const prefix = "Failed to execute 'getMappedRange' on 'GPUBuffer'";
-    offset = webidl.converters.GPUSize64(offset, prefix, "Argument 1");
-    if (size !== undefined) {
-      size = webidl.converters.GPUSize64(size, prefix, "Argument 2");
-    }
-    assertDevice(this, prefix, "this");
-    const bufferRid = assertResource(this, prefix, "this");
-    /** @type {number} */
-    let rangeSize;
-    if (size === undefined) {
-      rangeSize = MathMax(0, this[_size] - offset);
-    } else {
-      rangeSize = size;
-    }
-
-    const mappedRanges = this[_mappedRanges];
-    if (!mappedRanges) {
-      throw new DOMException(`${prefix}: invalid state`, "OperationError");
-    }
-    for (let i = 0; i < mappedRanges.length; ++i) {
-      const { 0: buffer, 1: _rid, 2: start } = mappedRanges[i];
-      // TODO(lucacasonato): is this logic correct?
-      const end = start + ArrayBufferPrototypeGetByteLength(buffer);
-      if (
-        (start >= offset && start < (offset + rangeSize)) ||
-        (end >= offset && end < (offset + rangeSize))
-      ) {
-        throw new DOMException(
-          `${prefix}: requested buffer overlaps with another mapped range`,
-          "OperationError",
-        );
-      }
-    }
-
-    const buffer = new ArrayBuffer(rangeSize);
-    const { rid } = op_webgpu_buffer_get_mapped_range(
-      bufferRid,
-      offset,
-      size,
-      new Uint8Array(buffer),
-    );
-
-    ArrayPrototypePush(mappedRanges, [buffer, rid, offset]);
-
-    return buffer;
-  }
-
-  unmap() {
-    webidl.assertBranded(this, GPUBufferPrototype);
-    const prefix = "Failed to execute 'unmap' on 'GPUBuffer'";
-    const device = assertDevice(this, prefix, "this");
-    const bufferRid = assertResource(this, prefix, "this");
-    if (this[_state] === "unmapped" || this[_state] === "destroyed") {
-      throw new DOMException(
-        `${prefix}: buffer is not ready to be unmapped`,
-        "OperationError",
-      );
-    }
-    if (this[_state] === "pending") {
-      // TODO(lucacasonato): this is not spec compliant.
-      throw new DOMException(
-        `${prefix}: can not unmap while mapping, this is a Deno limitation`,
-        "OperationError",
-      );
-    } else if (
-      this[_state] === "mapped" || this[_state] === "mapped at creation"
-    ) {
-      /** @type {boolean} */
-      let write = false;
-      if (this[_state] === "mapped at creation") {
-        write = true;
-      } else if (this[_state] === "mapped") {
-        const mapMode = this[_mapMode];
-        if (mapMode === undefined) {
-          throw new DOMException(
-            `${prefix}: invalid state`,
-            "OperationError",
-          );
-        }
-        if ((mapMode & 0x0002) === 0x0002) {
-          write = true;
-        }
-      }
-
-      const mappedRanges = this[_mappedRanges];
-      if (!mappedRanges) {
-        throw new DOMException(`${prefix}: invalid state`, "OperationError");
-      }
-      for (let i = 0; i < mappedRanges.length; ++i) {
-        const { 0: buffer, 1: mappedRid } = mappedRanges[i];
-        const { err } = op_webgpu_buffer_unmap(
-          bufferRid,
-          mappedRid,
-          ...new SafeArrayIterator(write ? [new Uint8Array(buffer)] : []),
-        );
-        device.pushError(err);
-        if (err) return;
-      }
-      this[_mappingRange] = null;
-      this[_mappedRanges] = null;
-    }
-
-    this[_state] = "unmapped";
-  }
-
-  destroy() {
-    webidl.assertBranded(this, GPUBufferPrototype);
-    this[_cleanup]();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUBuffer, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2310,9 +307,8 @@ class GPUBuffer {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUBuffer", GPUBuffer);
+  },
+});
 const GPUBufferPrototype = GPUBuffer.prototype;
 
 class GPUBufferUsage {
@@ -2365,150 +361,9 @@ class GPUMapMode {
   }
 }
 
-/**
- * @param {GPUTextureDescriptor} descriptor
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUTexture}
- */
-function createGPUTexture(descriptor, device, rid) {
-  /** @type {GPUTexture} */
-  const texture = webidl.createBranded(GPUTexture);
-  texture[_label] = descriptor.label;
-  texture[_device] = device;
-  texture[_rid] = rid;
-  texture[_views] = [];
-  texture[_width] = descriptor.size.width;
-  texture[_height] = descriptor.size.height;
-  texture[_depthOrArrayLayers] = descriptor.size.depthOrArrayLayers;
-  texture[_mipLevelCount] = descriptor.mipLevelCount;
-  texture[_sampleCount] = descriptor.sampleCount;
-  texture[_dimension] = descriptor.dimension;
-  texture[_format] = descriptor.format;
-  texture[_usage] = descriptor.usage;
-  return texture;
-}
-
-class GPUTexture {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-  /** @type {SafeWeakRef<GPUTextureView>[]} */
-  [_views];
-
-  /** @type {number} */
-  [_width];
-  /** @type {number} */
-  [_height];
-  /** @type {number} */
-  [_depthOrArrayLayers];
-  /** @type {number} */
-  [_mipLevelCount];
-  /** @type {number} */
-  [_sampleCount];
-  /** @type {GPUTextureDimension} */
-  [_dimension];
-  /** @type {GPUTextureFormat} */
-  [_format];
-  /** @type {number} */
-  [_usage];
-
-  [_cleanup]() {
-    const views = this[_views];
-    while (views.length > 0) {
-      const view = ArrayPrototypePop(views)?.deref();
-      if (view) {
-        view[_cleanup]();
-      }
-    }
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {GPUTextureViewDescriptor} descriptor
-   */
-  createView(descriptor = { __proto__: null }) {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    const prefix = "Failed to execute 'createView' on 'GPUTexture'";
-    webidl.requiredArguments(arguments.length, 0, prefix);
-    descriptor = webidl.converters.GPUTextureViewDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const textureRid = assertResource(this, prefix, "this");
-    const { rid, err } = op_webgpu_create_texture_view({
-      textureRid,
-      ...descriptor,
-    });
-    device.pushError(err);
-
-    const textureView = createGPUTextureView(
-      descriptor.label,
-      this,
-      rid,
-    );
-    ArrayPrototypePush(this[_views], new SafeWeakRef(textureView));
-    return textureView;
-  }
-
-  destroy() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    this[_cleanup]();
-  }
-
-  get width() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_width];
-  }
-
-  get height() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_height];
-  }
-
-  get depthOrArrayLayers() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_depthOrArrayLayers];
-  }
-
-  get mipLevelCount() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_mipLevelCount];
-  }
-
-  get sampleCount() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_sampleCount];
-  }
-
-  get dimension() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_dimension];
-  }
-
-  get format() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_format];
-  }
-
-  get usage() {
-    webidl.assertBranded(this, GPUTexturePrototype);
-    return this[_usage];
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUTexture, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2527,9 +382,8 @@ class GPUTexture {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUTexture", GPUTexture);
+  },
+});
 const GPUTexturePrototype = GPUTexture.prototype;
 
 class GPUTextureUsage {
@@ -2554,40 +408,9 @@ class GPUTextureUsage {
   }
 }
 
-/**
- * @param {string | null} label
- * @param {GPUTexture} texture
- * @param {number} rid
- * @returns {GPUTextureView}
- */
-function createGPUTextureView(label, texture, rid) {
-  /** @type {GPUTextureView} */
-  const textureView = webidl.createBranded(GPUTextureView);
-  textureView[_label] = label;
-  textureView[_texture] = texture;
-  textureView[_rid] = rid;
-  return textureView;
-}
-class GPUTextureView {
-  /** @type {GPUTexture} */
-  [_texture];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUTextureView, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2598,87 +421,24 @@ class GPUTextureView {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUTextureView", GPUTextureView);
+  },
+});
 const GPUTextureViewPrototype = GPUTextureView.prototype;
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUSampler}
- */
-function createGPUSampler(label, device, rid) {
-  /** @type {GPUSampler} */
-  const sampler = webidl.createBranded(GPUSampler);
-  sampler[_label] = label;
-  sampler[_device] = device;
-  sampler[_rid] = rid;
-  return sampler;
-}
-class GPUSampler {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
 
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ObjectDefineProperty(GPUSampler, customInspect, {
+  __proto__: null,
+  value(inspect) {
     return `${this.constructor.name} ${
       inspect({
         label: this.label,
       })
     }`;
-  }
-}
-GPUObjectBaseMixin("GPUSampler", GPUSampler);
-const GPUSamplerPrototype = GPUSampler.prototype;
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUBindGroupLayout}
- */
-function createGPUBindGroupLayout(label, device, rid) {
-  /** @type {GPUBindGroupLayout} */
-  const bindGroupLayout = webidl.createBranded(GPUBindGroupLayout);
-  bindGroupLayout[_label] = label;
-  bindGroupLayout[_device] = device;
-  bindGroupLayout[_rid] = rid;
-  return bindGroupLayout;
-}
-class GPUBindGroupLayout {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
+  },
+});
 
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUBindGroupLayout, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2692,44 +452,13 @@ class GPUBindGroupLayout {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUBindGroupLayout", GPUBindGroupLayout);
+  },
+});
 const GPUBindGroupLayoutPrototype = GPUBindGroupLayout.prototype;
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUPipelineLayout}
- */
-function createGPUPipelineLayout(label, device, rid) {
-  /** @type {GPUPipelineLayout} */
-  const pipelineLayout = webidl.createBranded(GPUPipelineLayout);
-  pipelineLayout[_label] = label;
-  pipelineLayout[_device] = device;
-  pipelineLayout[_rid] = rid;
-  return pipelineLayout;
-}
-class GPUPipelineLayout {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
 
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUPipelineLayout, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2743,45 +472,13 @@ class GPUPipelineLayout {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUPipelineLayout", GPUPipelineLayout);
+  },
+});
 const GPUPipelineLayoutPrototype = GPUPipelineLayout.prototype;
 
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUBindGroup}
- */
-function createGPUBindGroup(label, device, rid) {
-  /** @type {GPUBindGroup} */
-  const bindGroup = webidl.createBranded(GPUBindGroup);
-  bindGroup[_label] = label;
-  bindGroup[_device] = device;
-  bindGroup[_rid] = rid;
-  return bindGroup;
-}
-class GPUBindGroup {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUBindGroup, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2792,44 +489,13 @@ class GPUBindGroup {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUBindGroup", GPUBindGroup);
+  },
+});
 const GPUBindGroupPrototype = GPUBindGroup.prototype;
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUShaderModule}
- */
-function createGPUShaderModule(label, device, rid) {
-  /** @type {GPUShaderModule} */
-  const bindGroup = webidl.createBranded(GPUShaderModule);
-  bindGroup[_label] = label;
-  bindGroup[_device] = device;
-  bindGroup[_rid] = rid;
-  return bindGroup;
-}
-class GPUShaderModule {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
 
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUShaderModule, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2840,10 +506,10 @@ class GPUShaderModule {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUShaderModule", GPUShaderModule);
+  },
+});
 const GPUShaderModulePrototype = GPUShaderModule.prototype;
+
 class GPUShaderStage {
   constructor() {
     webidl.illegalConstructor();
@@ -2862,68 +528,9 @@ class GPUShaderStage {
   }
 }
 
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUComputePipeline}
- */
-function createGPUComputePipeline(label, device, rid) {
-  /** @type {GPUComputePipeline} */
-  const pipeline = webidl.createBranded(GPUComputePipeline);
-  pipeline[_label] = label;
-  pipeline[_device] = device;
-  pipeline[_rid] = rid;
-  return pipeline;
-}
-class GPUComputePipeline {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {number} index
-   * @returns {GPUBindGroupLayout}
-   */
-  getBindGroupLayout(index) {
-    webidl.assertBranded(this, GPUComputePipelinePrototype);
-    const prefix =
-      "Failed to execute 'getBindGroupLayout' on 'GPUComputePipeline'";
-    webidl.requiredArguments(arguments.length, 1, { prefix });
-    index = webidl.converters["unsigned long"](index, prefix, "Argument 1");
-    const device = assertDevice(this, prefix, "this");
-    const computePipelineRid = assertResource(this, prefix, "this");
-    const { rid, label, err } =
-      op_webgpu_compute_pipeline_get_bind_group_layout(
-        computePipelineRid,
-        index,
-      );
-    device.pushError(err);
-
-    const bindGroupLayout = createGPUBindGroupLayout(
-      label,
-      device,
-      rid,
-    );
-    device.trackResource(bindGroupLayout);
-    return bindGroupLayout;
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUComputePipeline, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -2937,71 +544,13 @@ class GPUComputePipeline {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUComputePipeline", GPUComputePipeline);
+  },
+});
 const GPUComputePipelinePrototype = GPUComputePipeline.prototype;
 
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPURenderPipeline}
- */
-function createGPURenderPipeline(label, device, rid) {
-  /** @type {GPURenderPipeline} */
-  const pipeline = webidl.createBranded(GPURenderPipeline);
-  pipeline[_label] = label;
-  pipeline[_device] = device;
-  pipeline[_rid] = rid;
-  return pipeline;
-}
-class GPURenderPipeline {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {number} index
-   */
-  getBindGroupLayout(index) {
-    webidl.assertBranded(this, GPURenderPipelinePrototype);
-    const prefix =
-      "Failed to execute 'getBindGroupLayout' on 'GPURenderPipeline'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    index = webidl.converters["unsigned long"](index, prefix, "Argument 1");
-    const device = assertDevice(this, prefix, "this");
-    const renderPipelineRid = assertResource(this, prefix, "this");
-    const { rid, label, err } = op_webgpu_render_pipeline_get_bind_group_layout(
-      renderPipelineRid,
-      index,
-    );
-    device.pushError(err);
-
-    const bindGroupLayout = createGPUBindGroupLayout(
-      label,
-      device,
-      rid,
-    );
-    device.trackResource(bindGroupLayout);
-    return bindGroupLayout;
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPURenderPipeline, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -3015,9 +564,8 @@ class GPURenderPipeline {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPURenderPipeline", GPURenderPipeline);
+  },
+});
 const GPURenderPipelinePrototype = GPURenderPipeline.prototype;
 
 class GPUColorWrite {
@@ -3042,603 +590,9 @@ class GPUColorWrite {
   }
 }
 
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUCommandEncoder}
- */
-function createGPUCommandEncoder(label, device, rid) {
-  /** @type {GPUCommandEncoder} */
-  const encoder = webidl.createBranded(GPUCommandEncoder);
-  encoder[_label] = label;
-  encoder[_device] = device;
-  encoder[_rid] = rid;
-  encoder[_encoders] = [];
-  return encoder;
-}
-class GPUCommandEncoder {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-  /** @type {SafeWeakRef<GPURenderPassEncoder | GPUComputePassEncoder>[]} */
-  [_encoders];
-
-  [_cleanup]() {
-    const encoders = this[_encoders];
-    while (encoders.length > 0) {
-      const encoder = ArrayPrototypePop(encoders)?.deref();
-      if (encoder) {
-        encoder[_cleanup]();
-      }
-    }
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {GPURenderPassDescriptor} descriptor
-   * @return {GPURenderPassEncoder}
-   */
-  beginRenderPass(descriptor) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix = "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    descriptor = webidl.converters.GPURenderPassDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-
-    if (this[_rid] === undefined) {
-      throw new DOMException(
-        "Failed to execute 'beginRenderPass' on 'GPUCommandEncoder': already consumed",
-        "OperationError",
-      );
-    }
-
-    let depthStencilAttachment;
-    if (descriptor.depthStencilAttachment) {
-      if (
-        descriptor.depthStencilAttachment.depthLoadOp === "clear" &&
-        !(ObjectHasOwn(descriptor.depthStencilAttachment, "depthClearValue"))
-      ) {
-        throw webidl.makeException(
-          TypeError,
-          '`depthClearValue` must be specified when `depthLoadOp` is "clear"',
-          prefix,
-          "Argument 1",
-        );
-      }
-
-      const view = assertResource(
-        descriptor.depthStencilAttachment.view,
-        prefix,
-        "texture view for depth stencil attachment",
-      );
-
-      depthStencilAttachment = {
-        ...descriptor.depthStencilAttachment,
-        view,
-      };
-    }
-    const colorAttachments = ArrayPrototypeMap(
-      descriptor.colorAttachments,
-      (colorAttachment, i) => {
-        const context = `color attachment ${i + 1}`;
-        const view = assertResource(
-          colorAttachment.view,
-          prefix,
-          `texture view for ${context}`,
-        );
-        assertResource(
-          colorAttachment.view[_texture],
-          prefix,
-          `texture backing texture view for ${context}`,
-        );
-        let resolveTarget;
-        if (colorAttachment.resolveTarget) {
-          resolveTarget = assertResource(
-            colorAttachment.resolveTarget,
-            prefix,
-            `resolve target texture view for ${context}`,
-          );
-          assertResource(
-            colorAttachment.resolveTarget[_texture],
-            prefix,
-            `texture backing resolve target texture view for ${context}`,
-          );
-        }
-        return {
-          view: view,
-          resolveTarget,
-          storeOp: colorAttachment.storeOp,
-          loadOp: colorAttachment.loadOp,
-          clearValue: normalizeGPUColor(colorAttachment.clearValue),
-        };
-      },
-    );
-
-    let occlusionQuerySet;
-
-    if (descriptor.occlusionQuerySet) {
-      occlusionQuerySet = assertResource(
-        descriptor.occlusionQuerySet,
-        prefix,
-        "occlusionQuerySet",
-      );
-    }
-
-    let timestampWrites = null;
-    if (descriptor.timestampWrites) {
-      const querySet = assertResource(
-        descriptor.timestampWrites.querySet,
-        prefix,
-        "querySet",
-      );
-
-      timestampWrites = {
-        querySet,
-        beginningOfPassWriteIndex:
-        descriptor.timestampWrites.beginningOfPassWriteIndex,
-        endOfPassWriteIndex: descriptor.timestampWrites.endOfPassWriteIndex,
-      };
-    }
-
-    const { rid } = op_webgpu_command_encoder_begin_render_pass(
-      commandEncoderRid,
-      descriptor.label,
-      colorAttachments,
-      depthStencilAttachment,
-      occlusionQuerySet,
-      timestampWrites,
-    );
-
-    const renderPassEncoder = createGPURenderPassEncoder(
-      descriptor.label,
-      this,
-      rid,
-    );
-    ArrayPrototypePush(this[_encoders], new SafeWeakRef(renderPassEncoder));
-    return renderPassEncoder;
-  }
-
-  /**
-   * @param {GPUComputePassDescriptor} descriptor
-   */
-  beginComputePass(descriptor = { __proto__: null }) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix =
-      "Failed to execute 'beginComputePass' on 'GPUCommandEncoder'";
-    descriptor = webidl.converters.GPUComputePassDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-
-    assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-
-    let timestampWrites = null;
-    if (descriptor.timestampWrites) {
-      const querySet = assertResource(
-        descriptor.timestampWrites.querySet,
-        prefix,
-        "querySet",
-      );
-
-      timestampWrites = {
-        querySet,
-        beginningOfPassWriteIndex:
-        descriptor.timestampWrites.beginningOfPassWriteIndex,
-        endOfPassWriteIndex: descriptor.timestampWrites.endOfPassWriteIndex,
-      };
-    }
-
-    const { rid } = op_webgpu_command_encoder_begin_compute_pass(
-      commandEncoderRid,
-      descriptor.label,
-      timestampWrites,
-    );
-
-    const computePassEncoder = createGPUComputePassEncoder(
-      descriptor.label,
-      this,
-      rid,
-    );
-    ArrayPrototypePush(this[_encoders], new SafeWeakRef(computePassEncoder));
-    return computePassEncoder;
-  }
-
-  /**
-   * @param {GPUBuffer} source
-   * @param {number} sourceOffset
-   * @param {GPUBuffer} destination
-   * @param {number} destinationOffset
-   * @param {number} size
-   */
-  copyBufferToBuffer(
-    source,
-    sourceOffset,
-    destination,
-    destinationOffset,
-    size,
-  ) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix =
-      "Failed to execute 'copyBufferToBuffer' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 5, prefix);
-    source = webidl.converters.GPUBuffer(source, prefix, "Argument 1");
-    sourceOffset = webidl.converters.GPUSize64(
-      sourceOffset,
-      prefix,
-      "Argument 2",
-    );
-    destination = webidl.converters.GPUBuffer(
-      destination,
-      prefix,
-      "Argument 3",
-    );
-    destinationOffset = webidl.converters.GPUSize64(
-      destinationOffset,
-      prefix,
-      "Argument 4",
-    );
-    size = webidl.converters.GPUSize64(size, prefix, "Argument 5");
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const sourceRid = assertResource(source, prefix, "Argument 1");
-    const destinationRid = assertResource(destination, prefix, "Argument 3");
-
-    const { err } = op_webgpu_command_encoder_copy_buffer_to_buffer(
-      commandEncoderRid,
-      sourceRid,
-      sourceOffset,
-      destinationRid,
-      destinationOffset,
-      size,
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUTexelCopyBufferInfo} source
-   * @param {GPUTexelCopyTextureInfo} destination
-   * @param {GPUExtent3D} copySize
-   */
-  copyBufferToTexture(source, destination, copySize) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix =
-      "Failed to execute 'copyBufferToTexture' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 3, prefix);
-    source = webidl.converters.GPUTexelCopyBufferInfo(source, prefix, "Argument 1");
-    destination = webidl.converters.GPUTexelCopyTextureInfo(
-      destination,
-      prefix,
-      "Argument 2",
-    );
-    copySize = webidl.converters.GPUExtent3D(copySize, prefix, "Argument 3");
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const sourceBufferRid = assertResource(
-      // deno-lint-ignore prefer-primordials
-      source.buffer,
-      prefix,
-      "source in Argument 1",
-    );
-    const destinationTextureRid = assertResource(
-      destination.texture,
-      prefix,
-      "texture in Argument 2",
-    );
-
-    const { err } = op_webgpu_command_encoder_copy_buffer_to_texture(
-      commandEncoderRid,
-      {
-        ...source,
-        buffer: sourceBufferRid,
-      },
-      {
-        texture: destinationTextureRid,
-        mipLevel: destination.mipLevel,
-        origin: destination.origin
-          ? normalizeGPUOrigin3D(destination.origin)
-          : undefined,
-        aspect: destination.aspect,
-      },
-      normalizeGPUExtent3D(copySize),
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUTexelCopyTextureInfo} source
-   * @param {GPUTexelCopyBufferInfo} destination
-   * @param {GPUExtent3D} copySize
-   */
-  copyTextureToBuffer(source, destination, copySize) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix =
-      "Failed to execute 'copyTextureToBuffer' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 3, prefix);
-    source = webidl.converters.GPUTexelCopyTextureInfo(
-      source,
-      prefix,
-      "Argument 1",
-    );
-    destination = webidl.converters.GPUTexelCopyBufferInfo(
-      destination,
-      prefix,
-      "Argument 2",
-    );
-    copySize = webidl.converters.GPUExtent3D(copySize, prefix, "Argument 3");
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const sourceTextureRid = assertResource(
-      source.texture,
-      prefix,
-      "texture in Argument 1",
-    );
-    const destinationBufferRid = assertResource(
-      // deno-lint-ignore prefer-primordials
-      destination.buffer,
-      prefix,
-      "buffer in Argument 2",
-    );
-    const { err } = op_webgpu_command_encoder_copy_texture_to_buffer(
-      commandEncoderRid,
-      {
-        texture: sourceTextureRid,
-        mipLevel: source.mipLevel,
-        origin: source.origin ? normalizeGPUOrigin3D(source.origin) : undefined,
-        aspect: source.aspect,
-      },
-      {
-        ...destination,
-        buffer: destinationBufferRid,
-      },
-      normalizeGPUExtent3D(copySize),
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUTexelCopyTextureInfo} source
-   * @param {GPUTexelCopyTextureInfo} destination
-   * @param {GPUExtent3D} copySize
-   */
-  copyTextureToTexture(source, destination, copySize) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix =
-      "Failed to execute 'copyTextureToTexture' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 3, prefix);
-    source = webidl.converters.GPUTexelCopyTextureInfo(
-      source,
-      prefix,
-      "Argument 1",
-    );
-    destination = webidl.converters.GPUTexelCopyTextureInfo(
-      destination,
-      prefix,
-      "Argument 2",
-    );
-    copySize = webidl.converters.GPUExtent3D(copySize, prefix, "Argument 3");
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const sourceTextureRid = assertResource(
-      source.texture,
-      prefix,
-      "texture in Argument 1",
-    );
-    const destinationTextureRid = assertResource(
-      destination.texture,
-      prefix,
-      "texture in Argument 2",
-    );
-    const { err } = op_webgpu_command_encoder_copy_texture_to_texture(
-      commandEncoderRid,
-      {
-        texture: sourceTextureRid,
-        mipLevel: source.mipLevel,
-        origin: source.origin ? normalizeGPUOrigin3D(source.origin) : undefined,
-        aspect: source.aspect,
-      },
-      {
-        texture: destinationTextureRid,
-        mipLevel: destination.mipLevel,
-        origin: destination.origin
-          ? normalizeGPUOrigin3D(destination.origin)
-          : undefined,
-        aspect: source.aspect,
-      },
-      normalizeGPUExtent3D(copySize),
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUBuffer} buffer
-   * @param {GPUSize64} offset
-   * @param {GPUSize64} size
-   */
-  clearBuffer(buffer, offset = 0, size = undefined) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix = "Failed to execute 'clearBuffer' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 3, prefix);
-    buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 1");
-    offset = webidl.converters.GPUSize64(offset, prefix, "Argument 2");
-    size = webidl.converters.GPUSize64(size, prefix, "Argument 3");
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const bufferRid = assertResource(buffer, prefix, "Argument 1");
-    const { err } = op_webgpu_command_encoder_clear_buffer(
-      commandEncoderRid,
-      bufferRid,
-      offset,
-      size,
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {string} groupLabel
-   */
-  pushDebugGroup(groupLabel) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix = "Failed to execute 'pushDebugGroup' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const { err } = op_webgpu_command_encoder_push_debug_group(
-      commandEncoderRid,
-      groupLabel,
-    );
-    device.pushError(err);
-  }
-
-  popDebugGroup() {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix = "Failed to execute 'popDebugGroup' on 'GPUCommandEncoder'";
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const { err } = op_webgpu_command_encoder_pop_debug_group(
-      commandEncoderRid,
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {string} markerLabel
-   */
-  insertDebugMarker(markerLabel) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix =
-      "Failed to execute 'insertDebugMarker' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    markerLabel = webidl.converters.USVString(
-      markerLabel,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const { err } = op_webgpu_command_encoder_insert_debug_marker(
-      commandEncoderRid,
-      markerLabel,
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUQuerySet} querySet
-   * @param {number} queryIndex
-   */
-  writeTimestamp(querySet, queryIndex) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix = "Failed to execute 'writeTimestamp' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    querySet = webidl.converters.GPUQuerySet(querySet, prefix, "Argument 1");
-    queryIndex = webidl.converters.GPUSize32(queryIndex, prefix, "Argument 2");
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const querySetRid = assertResource(querySet, prefix, "Argument 1");
-    const { err } = op_webgpu_command_encoder_write_timestamp(
-      commandEncoderRid,
-      querySetRid,
-      queryIndex,
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUQuerySet} querySet
-   * @param {number} firstQuery
-   * @param {number} queryCount
-   * @param {GPUBuffer} destination
-   * @param {number} destinationOffset
-   */
-  resolveQuerySet(
-    querySet,
-    firstQuery,
-    queryCount,
-    destination,
-    destinationOffset,
-  ) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix = "Failed to execute 'resolveQuerySet' on 'GPUCommandEncoder'";
-    webidl.requiredArguments(arguments.length, 5, { prefix });
-    querySet = webidl.converters.GPUQuerySet(querySet, prefix, "Argument 1");
-    firstQuery = webidl.converters.GPUSize32(firstQuery, prefix, "Argument 2");
-    queryCount = webidl.converters.GPUSize32(queryCount, prefix, "Argument 3");
-    destination = webidl.converters.GPUBuffer(
-      destination,
-      prefix,
-      "Argument 4",
-    );
-    destinationOffset = webidl.converters.GPUSize64(
-      destinationOffset,
-      prefix,
-      "Argument 5",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const querySetRid = assertResource(querySet, prefix, "Argument 1");
-    const destinationRid = assertResource(destination, prefix, "Argument 3");
-    const { err } = op_webgpu_command_encoder_resolve_query_set(
-      commandEncoderRid,
-      querySetRid,
-      firstQuery,
-      queryCount,
-      destinationRid,
-      destinationOffset,
-    );
-    device.pushError(err);
-  }
-
-  /**
-   * @param {GPUCommandBufferDescriptor} descriptor
-   * @returns {GPUCommandBuffer}
-   */
-  finish(descriptor = { __proto__: null }) {
-    webidl.assertBranded(this, GPUCommandEncoderPrototype);
-    const prefix = "Failed to execute 'finish' on 'GPUCommandEncoder'";
-    descriptor = webidl.converters.GPUCommandBufferDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const commandEncoderRid = assertResource(this, prefix, "this");
-    const { rid, err } = op_webgpu_command_encoder_finish(
-      commandEncoderRid,
-      descriptor.label,
-    );
-    device.pushError(err);
-    /** @type {number | undefined} */
-    this[_rid] = undefined;
-
-    const commandBuffer = createGPUCommandBuffer(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(commandBuffer);
-    return commandBuffer;
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUCommandEncoder, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -3652,566 +606,13 @@ class GPUCommandEncoder {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUCommandEncoder", GPUCommandEncoder);
+  },
+});
 const GPUCommandEncoderPrototype = GPUCommandEncoder.prototype;
 
-/**
- * @param {string | null} label
- * @param {GPUCommandEncoder} encoder
- * @param {number} rid
- * @returns {GPURenderPassEncoder}
- */
-function createGPURenderPassEncoder(label, encoder, rid) {
-  /** @type {GPURenderPassEncoder} */
-  const passEncoder = webidl.createBranded(GPURenderPassEncoder);
-  passEncoder[_label] = label;
-  passEncoder[_encoder] = encoder;
-  passEncoder[_rid] = rid;
-  return passEncoder;
-}
-
-class GPURenderPassEncoder {
-  /** @type {GPUCommandEncoder} */
-  [_encoder];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {number} x
-   * @param {number} y
-   * @param {number} width
-   * @param {number} height
-   * @param {number} minDepth
-   * @param {number} maxDepth
-   */
-  setViewport(x, y, width, height, minDepth, maxDepth) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix = "Failed to execute 'setViewport' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 6, { prefix });
-    x = webidl.converters.float(x, prefix, "Argument 1");
-    y = webidl.converters.float(y, prefix, "Argument 2");
-    width = webidl.converters.float(width, prefix, "Argument 3");
-    height = webidl.converters.float(height, prefix, "Argument 4");
-    minDepth = webidl.converters.float(minDepth, prefix, "Argument 5");
-    maxDepth = webidl.converters.float(maxDepth, prefix, "Argument 6");
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_set_viewport({
-      renderPassRid,
-      x,
-      y,
-      width,
-      height,
-      minDepth,
-      maxDepth,
-    });
-  }
-
-  /**
-   * @param {number} x
-   * @param {number} y
-   * @param {number} width
-   * @param {number} height
-   */
-  setScissorRect(x, y, width, height) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setScissorRect' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 4, prefix);
-    x = webidl.converters.GPUIntegerCoordinate(x, prefix, "Argument 1");
-    y = webidl.converters.GPUIntegerCoordinate(y, prefix, "Argument 2");
-    width = webidl.converters.GPUIntegerCoordinate(width, prefix, "Argument 3");
-    height = webidl.converters.GPUIntegerCoordinate(
-      height,
-      prefix,
-      "Argument 4",
-    );
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_set_scissor_rect(
-      renderPassRid,
-      x,
-      y,
-      width,
-      height,
-    );
-  }
-
-  /**
-   * @param {GPUColor} color
-   */
-  setBlendConstant(color) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setBlendConstant' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    color = webidl.converters.GPUColor(color, prefix, "Argument 1");
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_set_blend_constant(
-      renderPassRid,
-      normalizeGPUColor(color),
-    );
-  }
-
-  /**
-   * @param {number} reference
-   */
-  setStencilReference(reference) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setStencilReference' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    reference = webidl.converters.GPUStencilValue(
-      reference,
-      prefix,
-      "Argument 1",
-    );
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_set_stencil_reference(
-      renderPassRid,
-      reference,
-    );
-  }
-
-  /**
-   * @param {number} queryIndex
-   */
-  beginOcclusionQuery(queryIndex) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'beginOcclusionQuery' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    queryIndex = webidl.converters.GPUSize32(queryIndex, prefix, "Argument 1");
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_begin_occlusion_query(
-      renderPassRid,
-      queryIndex,
-    );
-  }
-
-  endOcclusionQuery() {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'endOcclusionQuery' on 'GPUComputePassEncoder'";
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_end_occlusion_query(renderPassRid);
-  }
-
-  /**
-   * @param {GPURenderBundle[]} bundles
-   */
-  executeBundles(bundles) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'executeBundles' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    bundles = webidl.converters["sequence<GPURenderBundle>"](
-      bundles,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    const bundleRids = ArrayPrototypeMap(bundles, (bundle, i) => {
-      const context = `bundle ${i + 1}`;
-      const rid = assertResource(bundle, prefix, context);
-      return rid;
-    });
-    op_webgpu_render_pass_execute_bundles(renderPassRid, bundleRids);
-  }
-
-  end() {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix = "Failed to execute 'end' on 'GPURenderPassEncoder'";
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    const commandEncoderRid = assertResource(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    const renderPassRid = assertResource(this, prefix, "this");
-    const { err } = op_webgpu_render_pass_end(
-      commandEncoderRid,
-      renderPassRid,
-    );
-    device.pushError(err);
-    this[_rid] = undefined;
-  }
-
-  // TODO(lucacasonato): has an overload
-  setBindGroup(
-    index,
-    bindGroup,
-    dynamicOffsetsData,
-    dynamicOffsetsDataStart,
-    dynamicOffsetsDataLength,
-  ) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix = "Failed to execute 'setBindGroup' on 'GPURenderPassEncoder'";
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    const bindGroupRid = assertResource(bindGroup, prefix, "Argument 2");
-    if (
-      TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !==
-      "Uint32Array"
-    ) {
-      dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []);
-      dynamicOffsetsDataStart = 0;
-      dynamicOffsetsDataLength = dynamicOffsetsData.length;
-    }
-    op_webgpu_render_pass_set_bind_group(
-      renderPassRid,
-      index,
-      bindGroupRid,
-      dynamicOffsetsData,
-      dynamicOffsetsDataStart,
-      dynamicOffsetsDataLength,
-    );
-  }
-
-  /**
-   * @param {string} groupLabel
-   */
-  pushDebugGroup(groupLabel) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'pushDebugGroup' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_push_debug_group(renderPassRid, groupLabel);
-  }
-
-  popDebugGroup() {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'popDebugGroup' on 'GPURenderPassEncoder'";
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_pop_debug_group(renderPassRid);
-  }
-
-  /**
-   * @param {string} markerLabel
-   */
-  insertDebugMarker(markerLabel) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'insertDebugMarker' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    markerLabel = webidl.converters.USVString(
-      markerLabel,
-      prefix,
-      "Argument 1",
-    );
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_insert_debug_marker(renderPassRid, markerLabel);
-  }
-
-  /**
-   * @param {GPURenderPipeline} pipeline
-   */
-  setPipeline(pipeline) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix = "Failed to execute 'setPipeline' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    pipeline = webidl.converters.GPURenderPipeline(
-      pipeline,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    const pipelineRid = assertResource(pipeline, prefix, "Argument 1");
-    op_webgpu_render_pass_set_pipeline(renderPassRid, pipelineRid);
-  }
-
-  /**
-   * @param {GPUBuffer} buffer
-   * @param {GPUIndexFormat} indexFormat
-   * @param {number} offset
-   * @param {number} size
-   */
-  setIndexBuffer(buffer, indexFormat, offset = 0, size) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setIndexBuffer' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 1");
-    indexFormat = webidl.converters.GPUIndexFormat(
-      indexFormat,
-      prefix,
-      "Argument 2",
-    );
-    offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
-    if (size !== undefined) {
-      size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
-    }
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    const bufferRid = assertResource(buffer, prefix, "Argument 1");
-    op_webgpu_render_pass_set_index_buffer(
-      renderPassRid,
-      bufferRid,
-      indexFormat,
-      offset,
-      size,
-    );
-  }
-
-  /**
-   * @param {number} slot
-   * @param {GPUBuffer} buffer
-   * @param {number} offset
-   * @param {number} size
-   */
-  setVertexBuffer(slot, buffer, offset = 0, size) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setVertexBuffer' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    slot = webidl.converters.GPUSize32(slot, prefix, "Argument 1");
-    buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 2");
-    offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
-    if (size !== undefined) {
-      size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
-    }
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    const bufferRid = assertResource(buffer, prefix, "Argument 2");
-    op_webgpu_render_pass_set_vertex_buffer(
-      renderPassRid,
-      slot,
-      bufferRid,
-      offset,
-      size,
-    );
-  }
-
-  /**
-   * @param {number} vertexCount
-   * @param {number} instanceCount
-   * @param {number} firstVertex
-   * @param {number} firstInstance
-   */
-  draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix = "Failed to execute 'draw' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    vertexCount = webidl.converters.GPUSize32(
-      vertexCount,
-      prefix,
-      "Argument 1",
-    );
-    instanceCount = webidl.converters.GPUSize32(
-      instanceCount,
-      prefix,
-      "Argument 2",
-    );
-    firstVertex = webidl.converters.GPUSize32(
-      firstVertex,
-      prefix,
-      "Argument 3",
-    );
-    firstInstance = webidl.converters.GPUSize32(
-      firstInstance,
-      prefix,
-      "Argument 4",
-    );
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_draw(
-      renderPassRid,
-      vertexCount,
-      instanceCount,
-      firstVertex,
-      firstInstance,
-    );
-  }
-
-  /**
-   * @param {number} indexCount
-   * @param {number} instanceCount
-   * @param {number} firstIndex
-   * @param {number} baseVertex
-   * @param {number} firstInstance
-   */
-  drawIndexed(
-    indexCount,
-    instanceCount = 1,
-    firstIndex = 0,
-    baseVertex = 0,
-    firstInstance = 0,
-  ) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix = "Failed to execute 'drawIndexed' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    indexCount = webidl.converters.GPUSize32(indexCount, prefix, "Argument 1");
-    instanceCount = webidl.converters.GPUSize32(
-      instanceCount,
-      prefix,
-      "Argument 2",
-    );
-    firstIndex = webidl.converters.GPUSize32(firstIndex, prefix, "Argument 3");
-    baseVertex = webidl.converters.GPUSignedOffset32(
-      baseVertex,
-      prefix,
-      "Argument 4",
-    );
-    firstInstance = webidl.converters.GPUSize32(
-      firstInstance,
-      prefix,
-      "Argument 5",
-    );
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    op_webgpu_render_pass_draw_indexed(
-      renderPassRid,
-      indexCount,
-      instanceCount,
-      firstIndex,
-      baseVertex,
-      firstInstance,
-    );
-  }
-
-  /**
-   * @param {GPUBuffer} indirectBuffer
-   * @param {number} indirectOffset
-   */
-  drawIndirect(indirectBuffer, indirectOffset) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix = "Failed to execute 'drawIndirect' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    indirectBuffer = webidl.converters.GPUBuffer(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    indirectOffset = webidl.converters.GPUSize64(
-      indirectOffset,
-      prefix,
-      "Argument 2",
-    );
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    const indirectBufferRid = assertResource(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    op_webgpu_render_pass_draw_indirect(
-      renderPassRid,
-      indirectBufferRid,
-      indirectOffset,
-    );
-  }
-
-  /**
-   * @param {GPUBuffer} indirectBuffer
-   * @param {number} indirectOffset
-   */
-  drawIndexedIndirect(indirectBuffer, indirectOffset) {
-    webidl.assertBranded(this, GPURenderPassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'drawIndexedIndirect' on 'GPURenderPassEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    indirectBuffer = webidl.converters.GPUBuffer(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    indirectOffset = webidl.converters.GPUSize64(
-      indirectOffset,
-      prefix,
-      "Argument 2",
-    );
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const renderPassRid = assertResource(this, prefix, "this");
-    const indirectBufferRid = assertResource(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    op_webgpu_render_pass_draw_indexed_indirect(
-      renderPassRid,
-      indirectBufferRid,
-      indirectOffset,
-    );
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPURenderPassEncoder, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -4225,254 +626,13 @@ class GPURenderPassEncoder {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPURenderPassEncoder", GPURenderPassEncoder);
+  },
+});
 const GPURenderPassEncoderPrototype = GPURenderPassEncoder.prototype;
 
-/**
- * @param {string | null} label
- * @param {GPUCommandEncoder} encoder
- * @param {number} rid
- * @returns {GPUComputePassEncoder}
- */
-function createGPUComputePassEncoder(label, encoder, rid) {
-  /** @type {GPUComputePassEncoder} */
-  const computePassEncoder = webidl.createBranded(GPUComputePassEncoder);
-  computePassEncoder[_label] = label;
-  computePassEncoder[_encoder] = encoder;
-  computePassEncoder[_rid] = rid;
-  return computePassEncoder;
-}
-
-class GPUComputePassEncoder {
-  /** @type {GPUCommandEncoder} */
-  [_encoder];
-
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {GPUComputePipeline} pipeline
-   */
-  setPipeline(pipeline) {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix = "Failed to execute 'setPipeline' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    pipeline = webidl.converters.GPUComputePipeline(
-      pipeline,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const computePassRid = assertResource(this, prefix, "this");
-    const pipelineRid = assertResource(pipeline, prefix, "Argument 1");
-    op_webgpu_compute_pass_set_pipeline(computePassRid, pipelineRid);
-  }
-
-  /**
-   * @param {number} workgroupCountX
-   * @param {number} workgroupCountY
-   * @param {number} workgroupCountZ
-   */
-  dispatchWorkgroups(
-    workgroupCountX,
-    workgroupCountY = 1,
-    workgroupCountZ = 1,
-  ) {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'dispatchWorkgroups' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    workgroupCountX = webidl.converters.GPUSize32(
-      workgroupCountX,
-      prefix,
-      "Argument 1",
-    );
-    workgroupCountY = webidl.converters.GPUSize32(
-      workgroupCountY,
-      prefix,
-      "Argument 2",
-    );
-    workgroupCountZ = webidl.converters.GPUSize32(
-      workgroupCountZ,
-      prefix,
-      "Argument 3",
-    );
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const computePassRid = assertResource(this, prefix, "this");
-    op_webgpu_compute_pass_dispatch_workgroups(
-      computePassRid,
-      workgroupCountX,
-      workgroupCountY,
-      workgroupCountZ,
-    );
-  }
-
-  /**
-   * @param {GPUBuffer} indirectBuffer
-   * @param {number} indirectOffset
-   */
-  dispatchWorkgroupsIndirect(indirectBuffer, indirectOffset) {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'dispatchWorkgroupsIndirect' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    indirectBuffer = webidl.converters.GPUBuffer(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    indirectOffset = webidl.converters.GPUSize64(
-      indirectOffset,
-      prefix,
-      "Argument 2",
-    );
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const computePassRid = assertResource(this, prefix, "this");
-    const indirectBufferRid = assertResource(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    op_webgpu_compute_pass_dispatch_workgroups_indirect(
-      computePassRid,
-      indirectBufferRid,
-      indirectOffset,
-    );
-  }
-
-  end() {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix = "Failed to execute 'end' on 'GPUComputePassEncoder'";
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    const commandEncoderRid = assertResource(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    const computePassRid = assertResource(this, prefix, "this");
-    const { err } = op_webgpu_compute_pass_end(
-      commandEncoderRid,
-      computePassRid,
-    );
-    device.pushError(err);
-    this[_rid] = undefined;
-  }
-
-  // TODO(lucacasonato): has an overload
-  setBindGroup(
-    index,
-    bindGroup,
-    dynamicOffsetsData,
-    dynamicOffsetsDataStart,
-    dynamicOffsetsDataLength,
-  ) {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'";
-    const device = assertDevice(
-      this[_encoder],
-      prefix,
-      "encoder referenced by this",
-    );
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const computePassRid = assertResource(this, prefix, "this");
-    const bindGroupRid = assertResource(bindGroup, prefix, "Argument 2");
-    if (
-      TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !==
-      "Uint32Array"
-    ) {
-      dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []);
-      dynamicOffsetsDataStart = 0;
-      dynamicOffsetsDataLength = dynamicOffsetsData.length;
-    }
-    op_webgpu_compute_pass_set_bind_group(
-      computePassRid,
-      index,
-      bindGroupRid,
-      dynamicOffsetsData,
-      dynamicOffsetsDataStart,
-      dynamicOffsetsDataLength,
-    );
-  }
-
-  /**
-   * @param {string} groupLabel
-   */
-  pushDebugGroup(groupLabel) {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'pushDebugGroup' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const computePassRid = assertResource(this, prefix, "this");
-    op_webgpu_compute_pass_push_debug_group(computePassRid, groupLabel);
-  }
-
-  popDebugGroup() {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'popDebugGroup' on 'GPUComputePassEncoder'";
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const computePassRid = assertResource(this, prefix, "this");
-    op_webgpu_compute_pass_pop_debug_group(computePassRid);
-  }
-
-  /**
-   * @param {string} markerLabel
-   */
-  insertDebugMarker(markerLabel) {
-    webidl.assertBranded(this, GPUComputePassEncoderPrototype);
-    const prefix =
-      "Failed to execute 'insertDebugMarker' on 'GPUComputePassEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    markerLabel = webidl.converters.USVString(
-      markerLabel,
-      prefix,
-      "Argument 1",
-    );
-    assertDevice(this[_encoder], prefix, "encoder referenced by this");
-    assertResource(this[_encoder], prefix, "encoder referenced by this");
-    const computePassRid = assertResource(this, prefix, "this");
-    op_webgpu_compute_pass_insert_debug_marker(
-      computePassRid,
-      markerLabel,
-    );
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUComputePassEncoder, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -4486,46 +646,13 @@ class GPUComputePassEncoder {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUComputePassEncoder", GPUComputePassEncoder);
+  },
+});
 const GPUComputePassEncoderPrototype = GPUComputePassEncoder.prototype;
 
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUCommandBuffer}
- */
-function createGPUCommandBuffer(label, device, rid) {
-  /** @type {GPUCommandBuffer} */
-  const commandBuffer = webidl.createBranded(GPUCommandBuffer);
-  commandBuffer[_label] = label;
-  commandBuffer[_device] = device;
-  commandBuffer[_rid] = rid;
-  return commandBuffer;
-}
-
-class GPUCommandBuffer {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUCommandBuffer, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -4536,359 +663,13 @@ class GPUCommandBuffer {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUCommandBuffer", GPUCommandBuffer);
+  },
+});
 const GPUCommandBufferPrototype = GPUCommandBuffer.prototype;
 
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPURenderBundleEncoder}
- */
-function createGPURenderBundleEncoder(label, device, rid) {
-  /** @type {GPURenderBundleEncoder} */
-  const bundleEncoder = webidl.createBranded(GPURenderBundleEncoder);
-  bundleEncoder[_label] = label;
-  bundleEncoder[_device] = device;
-  bundleEncoder[_rid] = rid;
-  return bundleEncoder;
-}
-
-class GPURenderBundleEncoder {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  /**
-   * @param {GPURenderBundleDescriptor} descriptor
-   */
-  finish(descriptor = { __proto__: null }) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix = "Failed to execute 'finish' on 'GPURenderBundleEncoder'";
-    descriptor = webidl.converters.GPURenderBundleDescriptor(
-      descriptor,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    const { rid, err } = op_webgpu_render_bundle_encoder_finish(
-      renderBundleEncoderRid,
-      descriptor.label,
-    );
-    device.pushError(err);
-    this[_rid] = undefined;
-
-    const renderBundle = createGPURenderBundle(
-      descriptor.label,
-      device,
-      rid,
-    );
-    device.trackResource(renderBundle);
-    return renderBundle;
-  }
-
-  // TODO(lucacasonato): has an overload
-  setBindGroup(
-    index,
-    bindGroup,
-    dynamicOffsetsData,
-    dynamicOffsetsDataStart,
-    dynamicOffsetsDataLength,
-  ) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setBindGroup' on 'GPURenderBundleEncoder'";
-    const device = assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    const bindGroupRid = assertResource(bindGroup, prefix, "Argument 2");
-    if (
-      TypedArrayPrototypeGetSymbolToStringTag(dynamicOffsetsData) !==
-      "Uint32Array"
-    ) {
-      dynamicOffsetsData = new Uint32Array(dynamicOffsetsData ?? []);
-      dynamicOffsetsDataStart = 0;
-      dynamicOffsetsDataLength = dynamicOffsetsData.length;
-    }
-    op_webgpu_render_bundle_encoder_set_bind_group(
-      renderBundleEncoderRid,
-      index,
-      bindGroupRid,
-      dynamicOffsetsData,
-      dynamicOffsetsDataStart,
-      dynamicOffsetsDataLength,
-    );
-  }
-
-  /**
-   * @param {string} groupLabel
-   */
-  pushDebugGroup(groupLabel) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'pushDebugGroup' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    groupLabel = webidl.converters.USVString(groupLabel, prefix, "Argument 1");
-    assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    op_webgpu_render_bundle_encoder_push_debug_group(
-      renderBundleEncoderRid,
-      groupLabel,
-    );
-  }
-
-  popDebugGroup() {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'popDebugGroup' on 'GPURenderBundleEncoder'";
-    assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    op_webgpu_render_bundle_encoder_pop_debug_group(
-      renderBundleEncoderRid,
-    );
-  }
-
-  /**
-   * @param {string} markerLabel
-   */
-  insertDebugMarker(markerLabel) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'insertDebugMarker' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    markerLabel = webidl.converters.USVString(
-      markerLabel,
-      prefix,
-      "Argument 1",
-    );
-    assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    op_webgpu_render_bundle_encoder_insert_debug_marker(
-      renderBundleEncoderRid,
-      markerLabel,
-    );
-  }
-
-  /**
-   * @param {GPURenderPipeline} pipeline
-   */
-  setPipeline(pipeline) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setPipeline' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    pipeline = webidl.converters.GPURenderPipeline(
-      pipeline,
-      prefix,
-      "Argument 1",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    const pipelineRid = assertResource(pipeline, prefix, "Argument 1");
-    op_webgpu_render_bundle_encoder_set_pipeline(
-      renderBundleEncoderRid,
-      pipelineRid,
-    );
-  }
-
-  /**
-   * @param {GPUBuffer} buffer
-   * @param {GPUIndexFormat} indexFormat
-   * @param {number} offset
-   * @param {number} size
-   */
-  setIndexBuffer(buffer, indexFormat, offset = 0, size = 0) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setIndexBuffer' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 1");
-    indexFormat = webidl.converters.GPUIndexFormat(
-      indexFormat,
-      prefix,
-      "Argument 2",
-    );
-    offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
-    size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
-    const device = assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    const bufferRid = assertResource(buffer, prefix, "Argument 1");
-    op_webgpu_render_bundle_encoder_set_index_buffer(
-      renderBundleEncoderRid,
-      bufferRid,
-      indexFormat,
-      offset,
-      size,
-    );
-  }
-
-  /**
-   * @param {number} slot
-   * @param {GPUBuffer} buffer
-   * @param {number} offset
-   * @param {number} size
-   */
-  setVertexBuffer(slot, buffer, offset = 0, size) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'setVertexBuffer' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    slot = webidl.converters.GPUSize32(slot, prefix, "Argument 1");
-    buffer = webidl.converters.GPUBuffer(buffer, prefix, "Argument 2");
-    offset = webidl.converters.GPUSize64(offset, prefix, "Argument 3");
-    if (size !== undefined) {
-      size = webidl.converters.GPUSize64(size, prefix, "Argument 4");
-    }
-    const device = assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    const bufferRid = assertResource(buffer, prefix, "Argument 2");
-    op_webgpu_render_bundle_encoder_set_vertex_buffer(
-      renderBundleEncoderRid,
-      slot,
-      bufferRid,
-      offset,
-      size,
-    );
-  }
-
-  /**
-   * @param {number} vertexCount
-   * @param {number} instanceCount
-   * @param {number} firstVertex
-   * @param {number} firstInstance
-   */
-  draw(vertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix = "Failed to execute 'draw' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    vertexCount = webidl.converters.GPUSize32(
-      vertexCount,
-      prefix,
-      "Argument 1",
-    );
-    instanceCount = webidl.converters.GPUSize32(
-      instanceCount,
-      prefix,
-      "Argument 2",
-    );
-    firstVertex = webidl.converters.GPUSize32(
-      firstVertex,
-      prefix,
-      "Argument 3",
-    );
-    firstInstance = webidl.converters.GPUSize32(
-      firstInstance,
-      prefix,
-      "Argument 4",
-    );
-    assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    op_webgpu_render_bundle_encoder_draw(
-      renderBundleEncoderRid,
-      vertexCount,
-      instanceCount,
-      firstVertex,
-      firstInstance,
-    );
-  }
-
-  /**
-   * @param {number} indexCount
-   * @param {number} instanceCount
-   * @param {number} firstIndex
-   * @param {number} baseVertex
-   * @param {number} firstInstance
-   */
-  drawIndexed(
-    indexCount,
-    instanceCount = 1,
-    firstIndex = 0,
-    baseVertex = 0,
-    firstInstance = 0,
-  ) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'drawIndexed' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 1, prefix);
-    indexCount = webidl.converters.GPUSize32(indexCount, prefix, "Argument 1");
-    instanceCount = webidl.converters.GPUSize32(
-      instanceCount,
-      prefix,
-      "Argument 2",
-    );
-    firstIndex = webidl.converters.GPUSize32(firstIndex, prefix, "Argument 3");
-    baseVertex = webidl.converters.GPUSignedOffset32(
-      baseVertex,
-      prefix,
-      "Argument 4",
-    );
-    firstInstance = webidl.converters.GPUSize32(
-      firstInstance,
-      prefix,
-      "Argument 5",
-    );
-    assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    op_webgpu_render_bundle_encoder_draw_indexed(
-      renderBundleEncoderRid,
-      indexCount,
-      instanceCount,
-      firstIndex,
-      baseVertex,
-      firstInstance,
-    );
-  }
-
-  /**
-   * @param {GPUBuffer} indirectBuffer
-   * @param {number} indirectOffset
-   */
-  drawIndirect(indirectBuffer, indirectOffset) {
-    webidl.assertBranded(this, GPURenderBundleEncoderPrototype);
-    const prefix =
-      "Failed to execute 'drawIndirect' on 'GPURenderBundleEncoder'";
-    webidl.requiredArguments(arguments.length, 2, prefix);
-    indirectBuffer = webidl.converters.GPUBuffer(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    indirectOffset = webidl.converters.GPUSize64(
-      indirectOffset,
-      prefix,
-      "Argument 2",
-    );
-    const device = assertDevice(this, prefix, "this");
-    const renderBundleEncoderRid = assertResource(this, prefix, "this");
-    const indirectBufferRid = assertResource(
-      indirectBuffer,
-      prefix,
-      "Argument 1",
-    );
-    op_webgpu_render_bundle_encoder_draw_indirect(
-      renderBundleEncoderRid,
-      indirectBufferRid,
-      indirectOffset,
-    );
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPURenderBundleEncoder, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -4902,46 +683,13 @@ class GPURenderBundleEncoder {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPURenderBundleEncoder", GPURenderBundleEncoder);
+  },
+});
 const GPURenderBundleEncoderPrototype = GPURenderBundleEncoder.prototype;
 
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPURenderBundle}
- */
-function createGPURenderBundle(label, device, rid) {
-  /** @type {GPURenderBundle} */
-  const bundle = webidl.createBranded(GPURenderBundle);
-  bundle[_label] = label;
-  bundle[_device] = device;
-  bundle[_rid] = rid;
-  return bundle;
-}
-
-class GPURenderBundle {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPURenderBundle, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -4952,67 +700,13 @@ class GPURenderBundle {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPURenderBundle", GPURenderBundle);
+  },
+});
 const GPURenderBundlePrototype = GPURenderBundle.prototype;
-/**
- * @param {string | null} label
- * @param {InnerGPUDevice} device
- * @param {number} rid
- * @returns {GPUQuerySet}
- */
-function createGPUQuerySet(label, device, rid, descriptor) {
-  /** @type {GPUQuerySet} */
-  const queue = webidl.createBranded(GPUQuerySet);
-  queue[_label] = label;
-  queue[_device] = device;
-  queue[_rid] = rid;
-  queue[_descriptor] = descriptor;
-  return queue;
-}
 
-class GPUQuerySet {
-  /** @type {InnerGPUDevice} */
-  [_device];
-  /** @type {number | undefined} */
-  [_rid];
-  /** @type {GPUQuerySetDescriptor} */
-  [_descriptor];
-  /** @type {GPUQueryType} */
-  [_type];
-  /** @type {number} */
-  [_count];
-
-  [_cleanup]() {
-    const rid = this[_rid];
-    if (rid !== undefined) {
-      core.close(rid);
-      /** @type {number | undefined} */
-      this[_rid] = undefined;
-    }
-  }
-
-  constructor() {
-    webidl.illegalConstructor();
-  }
-
-  destroy() {
-    webidl.assertBranded(this, GPUQuerySetPrototype);
-    this[_cleanup]();
-  }
-
-  get type() {
-    webidl.assertBranded(this, GPUQuerySetPrototype);
-    return this[_type]();
-  }
-
-  get count() {
-    webidl.assertBranded(this, GPUQuerySetPrototype);
-    return this[_count]();
-  }
-
-  [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) {
+ObjectDefineProperty(GPUQuerySet, customInspect, {
+  __proto__: null,
+  value(inspect, inspectOptions) {
     return inspect(
       createFilteredInspectProxy({
         object: this,
@@ -5025,131 +719,12 @@ class GPUQuerySet {
       }),
       inspectOptions,
     );
-  }
-}
-GPUObjectBaseMixin("GPUQuerySet", GPUQuerySet);
+  },
+});
 const GPUQuerySetPrototype = GPUQuerySet.prototype;
 
 // Converters
 
-// This needs to be initialized after all of the base classes are implemented,
-// otherwise their converters might not be available yet.
-// DICTIONARY: GPUObjectDescriptorBase
-const dictMembersGPUObjectDescriptorBase = [
-  { key: "label", converter: webidl.converters["USVString"], defaultValue: "" },
-];
-webidl.converters["GPUObjectDescriptorBase"] = webidl
-  .createDictionaryConverter(
-    "GPUObjectDescriptorBase",
-    dictMembersGPUObjectDescriptorBase,
-  );
-
-// INTERFACE: GPUSupportedLimits
-webidl.converters.GPUSupportedLimits = webidl.createInterfaceConverter(
-  "GPUSupportedLimits",
-  GPUSupportedLimits.prototype,
-);
-
-// INTERFACE: GPUSupportedFeatures
-webidl.converters.GPUSupportedFeatures = webidl.createInterfaceConverter(
-  "GPUSupportedFeatures",
-  GPUSupportedFeatures.prototype,
-);
-
-// INTERFACE: GPU
-webidl.converters.GPU = webidl.createInterfaceConverter("GPU", GPU.prototype);
-
-// ENUM: GPUPowerPreference
-webidl.converters["GPUPowerPreference"] = webidl.createEnumConverter(
-  "GPUPowerPreference",
-  [
-    "low-power",
-    "high-performance",
-  ],
-);
-
-// DICTIONARY: GPURequestAdapterOptions
-const dictMembersGPURequestAdapterOptions = [
-  {
-    key: "powerPreference",
-    converter: webidl.converters["GPUPowerPreference"],
-  },
-  {
-    key: "forceFallbackAdapter",
-    converter: webidl.converters.boolean,
-    defaultValue: false,
-  },
-];
-webidl.converters["GPURequestAdapterOptions"] = webidl
-  .createDictionaryConverter(
-    "GPURequestAdapterOptions",
-    dictMembersGPURequestAdapterOptions,
-  );
-
-// INTERFACE: GPUAdapter
-webidl.converters.GPUAdapter = webidl.createInterfaceConverter(
-  "GPUAdapter",
-  GPUAdapter.prototype,
-);
-
-// ENUM: GPUFeatureName
-webidl.converters["GPUFeatureName"] = webidl.createEnumConverter(
-  "GPUFeatureName",
-  [
-    // api
-    "depth-clip-control",
-    "timestamp-query",
-    "indirect-first-instance",
-    // shader
-    "shader-f16",
-    // texture formats
-    "depth32float-stencil8",
-    "texture-compression-bc",
-    "texture-compression-bc-sliced-3d",
-    "texture-compression-etc2",
-    "texture-compression-astc",
-    "rg11b10ufloat-renderable",
-    "bgra8unorm-storage",
-    "float32-filterable",
-
-    // extended from spec
-
-    // texture formats
-    "texture-format-16-bit-norm",
-    "texture-compression-astc-hdr",
-    "texture-adapter-specific-format-features",
-    // api
-    //"pipeline-statistics-query",
-    "timestamp-query-inside-passes",
-    "mappable-primary-buffers",
-    "texture-binding-array",
-    "buffer-binding-array",
-    "storage-resource-binding-array",
-    "sampled-texture-and-storage-buffer-array-non-uniform-indexing",
-    "uniform-buffer-and-storage-texture-array-non-uniform-indexing",
-    "partially-bound-binding-array",
-    "multi-draw-indirect",
-    "multi-draw-indirect-count",
-    "push-constants",
-    "address-mode-clamp-to-zero",
-    "address-mode-clamp-to-border",
-    "polygon-mode-line",
-    "polygon-mode-point",
-    "conservative-rasterization",
-    "vertex-writable-storage",
-    "clear-texture",
-    "spirv-shader-passthrough",
-    "multiview",
-    "vertex-attribute-64-bit",
-    // shader
-    "shader-f64",
-    "shader-i16",
-    "shader-primitive-index",
-    "shader-early-depth-test",
-  ],
-);
-
-// DICTIONARY: GPUPipelineErrorInit
 webidl.converters["GPUPipelineErrorInit"] = webidl.createDictionaryConverter(
   "GPUPipelineErrorInit",
   [
@@ -5161,7 +736,6 @@ webidl.converters["GPUPipelineErrorInit"] = webidl.createDictionaryConverter(
   ],
 );
 
-// ENUM: GPUPipelineErrorReason
 webidl.converters["GPUPipelineErrorReason"] = webidl.createEnumConverter(
   "GPUPipelineErrorReason",
   [
@@ -5170,1972 +744,8 @@ webidl.converters["GPUPipelineErrorReason"] = webidl.createEnumConverter(
   ],
 );
 
-// TYPEDEF: GPUSize32
-webidl.converters["GPUSize32"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+webidl.converters["GPUError"] = webidl.converters.any /* put union here! */;
 
-// TYPEDEF: GPUSize64
-webidl.converters["GPUSize64"] = (V, opts) =>
-  webidl.converters["unsigned long long"](V, { ...opts, enforceRange: true });
-
-// DICTIONARY: GPUDeviceDescriptor
-const dictMembersGPUDeviceDescriptor = [
-  {
-    key: "requiredFeatures",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUFeatureName"],
-    ),
-    get defaultValue() {
-      return [];
-    },
-  },
-  {
-    key: "requiredLimits",
-    converter: webidl.createRecordConverter(
-      webidl.converters["DOMString"],
-      webidl.converters["GPUSize64"],
-    ),
-  },
-];
-webidl.converters["GPUDeviceDescriptor"] = webidl.createDictionaryConverter(
-  "GPUDeviceDescriptor",
-  dictMembersGPUObjectDescriptorBase,
-  dictMembersGPUDeviceDescriptor,
-);
-
-// INTERFACE: GPUDevice
-webidl.converters.GPUDevice = webidl.createInterfaceConverter(
-  "GPUDevice",
-  GPUDevice.prototype,
-);
-
-// INTERFACE: GPUBuffer
-webidl.converters.GPUBuffer = webidl.createInterfaceConverter(
-  "GPUBuffer",
-  GPUBuffer.prototype,
-);
-
-// TYPEDEF: GPUBufferUsageFlags
-webidl.converters["GPUBufferUsageFlags"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// DICTIONARY: GPUBufferDescriptor
-const dictMembersGPUBufferDescriptor = [
-  { key: "size", converter: webidl.converters["GPUSize64"], required: true },
-  {
-    key: "usage",
-    converter: webidl.converters["GPUBufferUsageFlags"],
-    required: true,
-  },
-  {
-    key: "mappedAtCreation",
-    converter: webidl.converters["boolean"],
-    defaultValue: false,
-  },
-];
-webidl.converters["GPUBufferDescriptor"] = webidl.createDictionaryConverter(
-  "GPUBufferDescriptor",
-  dictMembersGPUObjectDescriptorBase,
-  dictMembersGPUBufferDescriptor,
-);
-
-// INTERFACE: GPUBufferUsage
-webidl.converters.GPUBufferUsage = webidl.createInterfaceConverter(
-  "GPUBufferUsage",
-  GPUBufferUsage.prototype,
-);
-
-// TYPEDEF: GPUMapModeFlags
-webidl.converters["GPUMapModeFlags"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// INTERFACE: GPUMapMode
-webidl.converters.GPUMapMode = webidl.createInterfaceConverter(
-  "GPUMapMode",
-  GPUMapMode.prototype,
-);
-
-// INTERFACE: GPUTexture
-webidl.converters.GPUTexture = webidl.createInterfaceConverter(
-  "GPUTexture",
-  GPUTexture.prototype,
-);
-
-// TYPEDEF: GPUIntegerCoordinate
-webidl.converters["GPUIntegerCoordinate"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-webidl.converters["sequence<GPUIntegerCoordinate>"] = webidl
-  .createSequenceConverter(webidl.converters["GPUIntegerCoordinate"]);
-
-// DICTIONARY: GPUExtent3DDict
-const dictMembersGPUExtent3DDict = [
-  {
-    key: "width",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    required: true,
-  },
-  {
-    key: "height",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 1,
-  },
-  {
-    key: "depthOrArrayLayers",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 1,
-  },
-];
-webidl.converters["GPUExtent3DDict"] = webidl.createDictionaryConverter(
-  "GPUExtent3DDict",
-  dictMembersGPUExtent3DDict,
-);
-
-// TYPEDEF: GPUExtent3D
-webidl.converters["GPUExtent3D"] = (V, opts) => {
-  // Union for (sequence<GPUIntegerCoordinate> or GPUExtent3DDict)
-  if (V === null || V === undefined) {
-    return webidl.converters["GPUExtent3DDict"](V, opts);
-  }
-  if (typeof V === "object") {
-    const method = V[SymbolIterator];
-    if (method !== undefined) {
-      // validate length of GPUExtent3D
-      const min = 1;
-      const max = 3;
-      if (V.length < min || V.length > max) {
-        throw webidl.makeException(
-          TypeError,
-          `A sequence of number used as a GPUExtent3D must have between ${min} and ${max} elements, received ${V.length} elements`,
-          opts,
-        );
-      }
-      return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
-    }
-    return webidl.converters["GPUExtent3DDict"](V, opts);
-  }
-  throw webidl.makeException(
-    TypeError,
-    "can not be converted to sequence<GPUIntegerCoordinate> or GPUExtent3DDict",
-    opts,
-  );
-};
-
-// ENUM: GPUTextureDimension
-webidl.converters["GPUTextureDimension"] = webidl.createEnumConverter(
-  "GPUTextureDimension",
-  [
-    "1d",
-    "2d",
-    "3d",
-  ],
-);
-
-// ENUM: GPUTextureFormat
-webidl.converters["GPUTextureFormat"] = webidl.createEnumConverter(
-  "GPUTextureFormat",
-  [
-    "r8unorm",
-    "r8snorm",
-    "r8uint",
-    "r8sint",
-    "r16uint",
-    "r16sint",
-    "r16float",
-    "rg8unorm",
-    "rg8snorm",
-    "rg8uint",
-    "rg8sint",
-    "r32uint",
-    "r32sint",
-    "r32float",
-    "rg16uint",
-    "rg16sint",
-    "rg16float",
-    "rgba8unorm",
-    "rgba8unorm-srgb",
-    "rgba8snorm",
-    "rgba8uint",
-    "rgba8sint",
-    "bgra8unorm",
-    "bgra8unorm-srgb",
-    "rgb9e5ufloat",
-    "rgb10a2uint",
-    "rgb10a2unorm",
-    "rg11b10ufloat",
-    "rg32uint",
-    "rg32sint",
-    "rg32float",
-    "rgba16uint",
-    "rgba16sint",
-    "rgba16float",
-    "rgba32uint",
-    "rgba32sint",
-    "rgba32float",
-    "stencil8",
-    "depth16unorm",
-    "depth24plus",
-    "depth24plus-stencil8",
-    "depth32float",
-    "depth32float-stencil8",
-    "bc1-rgba-unorm",
-    "bc1-rgba-unorm-srgb",
-    "bc2-rgba-unorm",
-    "bc2-rgba-unorm-srgb",
-    "bc3-rgba-unorm",
-    "bc3-rgba-unorm-srgb",
-    "bc4-r-unorm",
-    "bc4-r-snorm",
-    "bc5-rg-unorm",
-    "bc5-rg-snorm",
-    "bc6h-rgb-ufloat",
-    "bc6h-rgb-float",
-    "bc7-rgba-unorm",
-    "bc7-rgba-unorm-srgb",
-    "etc2-rgb8unorm",
-    "etc2-rgb8unorm-srgb",
-    "etc2-rgb8a1unorm",
-    "etc2-rgb8a1unorm-srgb",
-    "etc2-rgba8unorm",
-    "etc2-rgba8unorm-srgb",
-    "eac-r11unorm",
-    "eac-r11snorm",
-    "eac-rg11unorm",
-    "eac-rg11snorm",
-    "astc-4x4-unorm",
-    "astc-4x4-unorm-srgb",
-    "astc-5x4-unorm",
-    "astc-5x4-unorm-srgb",
-    "astc-5x5-unorm",
-    "astc-5x5-unorm-srgb",
-    "astc-6x5-unorm",
-    "astc-6x5-unorm-srgb",
-    "astc-6x6-unorm",
-    "astc-6x6-unorm-srgb",
-    "astc-8x5-unorm",
-    "astc-8x5-unorm-srgb",
-    "astc-8x6-unorm",
-    "astc-8x6-unorm-srgb",
-    "astc-8x8-unorm",
-    "astc-8x8-unorm-srgb",
-    "astc-10x5-unorm",
-    "astc-10x5-unorm-srgb",
-    "astc-10x6-unorm",
-    "astc-10x6-unorm-srgb",
-    "astc-10x8-unorm",
-    "astc-10x8-unorm-srgb",
-    "astc-10x10-unorm",
-    "astc-10x10-unorm-srgb",
-    "astc-12x10-unorm",
-    "astc-12x10-unorm-srgb",
-    "astc-12x12-unorm",
-    "astc-12x12-unorm-srgb",
-  ],
-);
-
-// TYPEDEF: GPUTextureUsageFlags
-webidl.converters["GPUTextureUsageFlags"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// DICTIONARY: GPUTextureDescriptor
-const dictMembersGPUTextureDescriptor = [
-  {
-    key: "size",
-    converter: webidl.converters["GPUExtent3D"],
-    required: true,
-  },
-  {
-    key: "mipLevelCount",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 1,
-  },
-  {
-    key: "sampleCount",
-    converter: webidl.converters["GPUSize32"],
-    defaultValue: 1,
-  },
-  {
-    key: "dimension",
-    converter: webidl.converters["GPUTextureDimension"],
-    defaultValue: "2d",
-  },
-  {
-    key: "format",
-    converter: webidl.converters["GPUTextureFormat"],
-    required: true,
-  },
-  {
-    key: "usage",
-    converter: webidl.converters["GPUTextureUsageFlags"],
-    required: true,
-  },
-  {
-    key: "viewFormats",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUTextureFormat"],
-    ),
-    get defaultValue() {
-      return [];
-    },
-  },
-];
-webidl.converters["GPUTextureDescriptor"] = webidl.createDictionaryConverter(
-  "GPUTextureDescriptor",
-  dictMembersGPUObjectDescriptorBase,
-  dictMembersGPUTextureDescriptor,
-);
-
-// INTERFACE: GPUTextureUsage
-webidl.converters.GPUTextureUsage = webidl.createInterfaceConverter(
-  "GPUTextureUsage",
-  GPUTextureUsage.prototype,
-);
-
-// INTERFACE: GPUTextureView
-webidl.converters.GPUTextureView = webidl.createInterfaceConverter(
-  "GPUTextureView",
-  GPUTextureView.prototype,
-);
-
-// ENUM: GPUTextureViewDimension
-webidl.converters["GPUTextureViewDimension"] = webidl.createEnumConverter(
-  "GPUTextureViewDimension",
-  [
-    "1d",
-    "2d",
-    "2d-array",
-    "cube",
-    "cube-array",
-    "3d",
-  ],
-);
-
-// ENUM: GPUTextureAspect
-webidl.converters["GPUTextureAspect"] = webidl.createEnumConverter(
-  "GPUTextureAspect",
-  [
-    "all",
-    "stencil-only",
-    "depth-only",
-  ],
-);
-
-// DICTIONARY: GPUTextureViewDescriptor
-const dictMembersGPUTextureViewDescriptor = [
-  { key: "format", converter: webidl.converters["GPUTextureFormat"] },
-  {
-    key: "dimension",
-    converter: webidl.converters["GPUTextureViewDimension"],
-  },
-  {
-    key: "aspect",
-    converter: webidl.converters["GPUTextureAspect"],
-    defaultValue: "all",
-  },
-  {
-    key: "baseMipLevel",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-  {
-    key: "mipLevelCount",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-  },
-  {
-    key: "baseArrayLayer",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-  {
-    key: "arrayLayerCount",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-  },
-];
-webidl.converters["GPUTextureViewDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUTextureViewDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUTextureViewDescriptor,
-  );
-
-// INTERFACE: GPUSampler
-webidl.converters.GPUSampler = webidl.createInterfaceConverter(
-  "GPUSampler",
-  GPUSampler.prototype,
-);
-
-// ENUM: GPUAddressMode
-webidl.converters["GPUAddressMode"] = webidl.createEnumConverter(
-  "GPUAddressMode",
-  [
-    "clamp-to-edge",
-    "repeat",
-    "mirror-repeat",
-  ],
-);
-
-// ENUM: GPUFilterMode
-webidl.converters["GPUFilterMode"] = webidl.createEnumConverter(
-  "GPUFilterMode",
-  [
-    "nearest",
-    "linear",
-  ],
-);
-
-// ENUM: GPUMipmapFilterMode
-webidl.converters["GPUMipmapFilterMode"] = webidl.createEnumConverter(
-  "GPUMipmapFilterMode",
-  [
-    "nearest",
-    "linear",
-  ],
-);
-
-// ENUM: GPUCompareFunction
-webidl.converters["GPUCompareFunction"] = webidl.createEnumConverter(
-  "GPUCompareFunction",
-  [
-    "never",
-    "less",
-    "equal",
-    "less-equal",
-    "greater",
-    "not-equal",
-    "greater-equal",
-    "always",
-  ],
-);
-
-// DICTIONARY: GPUSamplerDescriptor
-const dictMembersGPUSamplerDescriptor = [
-  {
-    key: "addressModeU",
-    converter: webidl.converters["GPUAddressMode"],
-    defaultValue: "clamp-to-edge",
-  },
-  {
-    key: "addressModeV",
-    converter: webidl.converters["GPUAddressMode"],
-    defaultValue: "clamp-to-edge",
-  },
-  {
-    key: "addressModeW",
-    converter: webidl.converters["GPUAddressMode"],
-    defaultValue: "clamp-to-edge",
-  },
-  {
-    key: "magFilter",
-    converter: webidl.converters["GPUFilterMode"],
-    defaultValue: "nearest",
-  },
-  {
-    key: "minFilter",
-    converter: webidl.converters["GPUFilterMode"],
-    defaultValue: "nearest",
-  },
-  {
-    key: "mipmapFilter",
-    converter: webidl.converters["GPUMipmapFilterMode"],
-    defaultValue: "nearest",
-  },
-  {
-    key: "lodMinClamp",
-    converter: webidl.converters["float"],
-    defaultValue: 0,
-  },
-  {
-    key: "lodMaxClamp",
-    converter: webidl.converters["float"],
-    defaultValue: 0xffffffff,
-  },
-  { key: "compare", converter: webidl.converters["GPUCompareFunction"] },
-  {
-    key: "maxAnisotropy",
-    converter: (V, opts) =>
-      webidl.converters["unsigned short"](V, { ...opts, clamp: true }),
-    defaultValue: 1,
-  },
-];
-webidl.converters["GPUSamplerDescriptor"] = webidl.createDictionaryConverter(
-  "GPUSamplerDescriptor",
-  dictMembersGPUObjectDescriptorBase,
-  dictMembersGPUSamplerDescriptor,
-);
-
-// INTERFACE: GPUBindGroupLayout
-webidl.converters.GPUBindGroupLayout = webidl.createInterfaceConverter(
-  "GPUBindGroupLayout",
-  GPUBindGroupLayout.prototype,
-);
-
-// TYPEDEF: GPUIndex32
-webidl.converters["GPUIndex32"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// TYPEDEF: GPUShaderStageFlags
-webidl.converters["GPUShaderStageFlags"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// ENUM: GPUBufferBindingType
-webidl.converters["GPUBufferBindingType"] = webidl.createEnumConverter(
-  "GPUBufferBindingType",
-  [
-    "uniform",
-    "storage",
-    "read-only-storage",
-  ],
-);
-
-// DICTIONARY: GPUBufferBindingLayout
-const dictMembersGPUBufferBindingLayout = [
-  {
-    key: "type",
-    converter: webidl.converters["GPUBufferBindingType"],
-    defaultValue: "uniform",
-  },
-  {
-    key: "hasDynamicOffset",
-    converter: webidl.converters["boolean"],
-    defaultValue: false,
-  },
-  {
-    key: "minBindingSize",
-    converter: webidl.converters["GPUSize64"],
-    defaultValue: 0,
-  },
-];
-webidl.converters["GPUBufferBindingLayout"] = webidl
-  .createDictionaryConverter(
-    "GPUBufferBindingLayout",
-    dictMembersGPUBufferBindingLayout,
-  );
-
-// ENUM: GPUSamplerBindingType
-webidl.converters["GPUSamplerBindingType"] = webidl.createEnumConverter(
-  "GPUSamplerBindingType",
-  [
-    "filtering",
-    "non-filtering",
-    "comparison",
-  ],
-);
-
-// DICTIONARY: GPUSamplerBindingLayout
-const dictMembersGPUSamplerBindingLayout = [
-  {
-    key: "type",
-    converter: webidl.converters["GPUSamplerBindingType"],
-    defaultValue: "filtering",
-  },
-];
-webidl.converters["GPUSamplerBindingLayout"] = webidl
-  .createDictionaryConverter(
-    "GPUSamplerBindingLayout",
-    dictMembersGPUSamplerBindingLayout,
-  );
-
-// ENUM: GPUTextureSampleType
-webidl.converters["GPUTextureSampleType"] = webidl.createEnumConverter(
-  "GPUTextureSampleType",
-  [
-    "float",
-    "unfilterable-float",
-    "depth",
-    "sint",
-    "uint",
-  ],
-);
-
-// DICTIONARY: GPUTextureBindingLayout
-const dictMembersGPUTextureBindingLayout = [
-  {
-    key: "sampleType",
-    converter: webidl.converters["GPUTextureSampleType"],
-    defaultValue: "float",
-  },
-  {
-    key: "viewDimension",
-    converter: webidl.converters["GPUTextureViewDimension"],
-    defaultValue: "2d",
-  },
-  {
-    key: "multisampled",
-    converter: webidl.converters["boolean"],
-    defaultValue: false,
-  },
-];
-webidl.converters["GPUTextureBindingLayout"] = webidl
-  .createDictionaryConverter(
-    "GPUTextureBindingLayout",
-    dictMembersGPUTextureBindingLayout,
-  );
-
-// ENUM: GPUStorageTextureAccess
-webidl.converters["GPUStorageTextureAccess"] = webidl.createEnumConverter(
-  "GPUStorageTextureAccess",
-  [
-    "write-only",
-    "read-only",
-    "read-write",
-  ],
-);
-
-// DICTIONARY: GPUStorageTextureBindingLayout
-const dictMembersGPUStorageTextureBindingLayout = [
-  {
-    key: "access",
-    converter: webidl.converters["GPUStorageTextureAccess"],
-    defaultValue: "write-only",
-  },
-  {
-    key: "format",
-    converter: webidl.converters["GPUTextureFormat"],
-    required: true,
-  },
-  {
-    key: "viewDimension",
-    converter: webidl.converters["GPUTextureViewDimension"],
-    defaultValue: "2d",
-  },
-];
-webidl.converters["GPUStorageTextureBindingLayout"] = webidl
-  .createDictionaryConverter(
-    "GPUStorageTextureBindingLayout",
-    dictMembersGPUStorageTextureBindingLayout,
-  );
-
-// DICTIONARY: GPUBindGroupLayoutEntry
-const dictMembersGPUBindGroupLayoutEntry = [
-  {
-    key: "binding",
-    converter: webidl.converters["GPUIndex32"],
-    required: true,
-  },
-  {
-    key: "visibility",
-    converter: webidl.converters["GPUShaderStageFlags"],
-    required: true,
-  },
-  { key: "buffer", converter: webidl.converters["GPUBufferBindingLayout"] },
-  { key: "sampler", converter: webidl.converters["GPUSamplerBindingLayout"] },
-  { key: "texture", converter: webidl.converters["GPUTextureBindingLayout"] },
-  {
-    key: "storageTexture",
-    converter: webidl.converters["GPUStorageTextureBindingLayout"],
-  },
-];
-webidl.converters["GPUBindGroupLayoutEntry"] = webidl
-  .createDictionaryConverter(
-    "GPUBindGroupLayoutEntry",
-    dictMembersGPUBindGroupLayoutEntry,
-  );
-
-// DICTIONARY: GPUBindGroupLayoutDescriptor
-const dictMembersGPUBindGroupLayoutDescriptor = [
-  {
-    key: "entries",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUBindGroupLayoutEntry"],
-    ),
-    required: true,
-  },
-];
-webidl.converters["GPUBindGroupLayoutDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUBindGroupLayoutDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUBindGroupLayoutDescriptor,
-  );
-
-// INTERFACE: GPUShaderStage
-webidl.converters.GPUShaderStage = webidl.createInterfaceConverter(
-  "GPUShaderStage",
-  GPUShaderStage.prototype,
-);
-
-// INTERFACE: GPUBindGroup
-webidl.converters.GPUBindGroup = webidl.createInterfaceConverter(
-  "GPUBindGroup",
-  GPUBindGroup.prototype,
-);
-
-// DICTIONARY: GPUBufferBinding
-const dictMembersGPUBufferBinding = [
-  {
-    key: "buffer",
-    converter: webidl.converters["GPUBuffer"],
-    required: true,
-  },
-  {
-    key: "offset",
-    converter: webidl.converters["GPUSize64"],
-    defaultValue: 0,
-  },
-  { key: "size", converter: webidl.converters["GPUSize64"] },
-];
-webidl.converters["GPUBufferBinding"] = webidl.createDictionaryConverter(
-  "GPUBufferBinding",
-  dictMembersGPUBufferBinding,
-);
-
-// TYPEDEF: GPUBindingResource
-webidl.converters["GPUBindingResource"] =
-  webidl.converters.any /** put union here! **/;
-
-// DICTIONARY: GPUBindGroupEntry
-const dictMembersGPUBindGroupEntry = [
-  {
-    key: "binding",
-    converter: webidl.converters["GPUIndex32"],
-    required: true,
-  },
-  {
-    key: "resource",
-    converter: webidl.converters["GPUBindingResource"],
-    required: true,
-  },
-];
-webidl.converters["GPUBindGroupEntry"] = webidl.createDictionaryConverter(
-  "GPUBindGroupEntry",
-  dictMembersGPUBindGroupEntry,
-);
-
-// DICTIONARY: GPUBindGroupDescriptor
-const dictMembersGPUBindGroupDescriptor = [
-  {
-    key: "layout",
-    converter: webidl.converters["GPUBindGroupLayout"],
-    required: true,
-  },
-  {
-    key: "entries",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUBindGroupEntry"],
-    ),
-    required: true,
-  },
-];
-webidl.converters["GPUBindGroupDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUBindGroupDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUBindGroupDescriptor,
-  );
-
-// INTERFACE: GPUPipelineLayout
-webidl.converters.GPUPipelineLayout = webidl.createInterfaceConverter(
-  "GPUPipelineLayout",
-  GPUPipelineLayout.prototype,
-);
-
-// DICTIONARY: GPUPipelineLayoutDescriptor
-const dictMembersGPUPipelineLayoutDescriptor = [
-  {
-    key: "bindGroupLayouts",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUBindGroupLayout"],
-    ),
-    required: true,
-  },
-];
-webidl.converters["GPUPipelineLayoutDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUPipelineLayoutDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUPipelineLayoutDescriptor,
-  );
-
-// INTERFACE: GPUShaderModule
-webidl.converters.GPUShaderModule = webidl.createInterfaceConverter(
-  "GPUShaderModule",
-  GPUShaderModule.prototype,
-);
-
-// DICTIONARY: GPUShaderModuleDescriptor
-const dictMembersGPUShaderModuleDescriptor = [
-  {
-    key: "code",
-    converter: webidl.converters["DOMString"],
-    required: true,
-  },
-];
-webidl.converters["GPUShaderModuleDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUShaderModuleDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUShaderModuleDescriptor,
-  );
-
-// // ENUM: GPUCompilationMessageType
-// webidl.converters["GPUCompilationMessageType"] = webidl.createEnumConverter(
-//   "GPUCompilationMessageType",
-//   [
-//     "error",
-//     "warning",
-//     "info",
-//   ],
-// );
-
-// // INTERFACE: GPUCompilationMessage
-// webidl.converters.GPUCompilationMessage = webidl.createInterfaceConverter(
-//   "GPUCompilationMessage",
-//   GPUCompilationMessage.prototype,
-// );
-
-// // INTERFACE: GPUCompilationInfo
-// webidl.converters.GPUCompilationInfo = webidl.createInterfaceConverter(
-//   "GPUCompilationInfo",
-//   GPUCompilationInfo.prototype,
-// );
-
-webidl.converters["GPUAutoLayoutMode"] = webidl.createEnumConverter(
-  "GPUAutoLayoutMode",
-  [
-    "auto",
-  ],
-);
-
-webidl.converters["GPUPipelineLayout or GPUAutoLayoutMode"] = (V, opts) => {
-  if (typeof V === "object") {
-    return webidl.converters["GPUPipelineLayout"](V, opts);
-  }
-  return webidl.converters["GPUAutoLayoutMode"](V, opts);
-};
-
-// DICTIONARY: GPUPipelineDescriptorBase
-const dictMembersGPUPipelineDescriptorBase = [
-  {
-    key: "layout",
-    converter: webidl.converters["GPUPipelineLayout or GPUAutoLayoutMode"],
-    required: true,
-  },
-];
-webidl.converters["GPUPipelineDescriptorBase"] = webidl
-  .createDictionaryConverter(
-    "GPUPipelineDescriptorBase",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUPipelineDescriptorBase,
-  );
-
-// TYPEDEF: GPUPipelineConstantValue
-webidl.converters.GPUPipelineConstantValue = webidl.converters.double;
-
-webidl.converters["record<USVString, GPUPipelineConstantValue>"] = webidl
-  .createRecordConverter(
-    webidl.converters.USVString,
-    webidl.converters.GPUPipelineConstantValue,
-  );
-
-// DICTIONARY: GPUProgrammableStage
-const dictMembersGPUProgrammableStage = [
-  {
-    key: "module",
-    converter: webidl.converters["GPUShaderModule"],
-    required: true,
-  },
-  {
-    key: "entryPoint",
-    converter: webidl.converters["USVString"],
-  },
-  {
-    key: "constants",
-    converter: webidl.converters["record<USVString, GPUPipelineConstantValue>"],
-  },
-];
-webidl.converters["GPUProgrammableStage"] = webidl.createDictionaryConverter(
-  "GPUProgrammableStage",
-  dictMembersGPUProgrammableStage,
-);
-
-// INTERFACE: GPUComputePipeline
-webidl.converters.GPUComputePipeline = webidl.createInterfaceConverter(
-  "GPUComputePipeline",
-  GPUComputePipeline.prototype,
-);
-
-// DICTIONARY: GPUComputePipelineDescriptor
-const dictMembersGPUComputePipelineDescriptor = [
-  {
-    key: "compute",
-    converter: webidl.converters["GPUProgrammableStage"],
-    required: true,
-  },
-];
-webidl.converters["GPUComputePipelineDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUComputePipelineDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUPipelineDescriptorBase,
-    dictMembersGPUComputePipelineDescriptor,
-  );
-
-// INTERFACE: GPURenderPipeline
-webidl.converters.GPURenderPipeline = webidl.createInterfaceConverter(
-  "GPURenderPipeline",
-  GPURenderPipeline.prototype,
-);
-
-// ENUM: GPUVertexStepMode
-webidl.converters["GPUVertexStepMode"] = webidl.createEnumConverter(
-  "GPUVertexStepMode",
-  [
-    "vertex",
-    "instance",
-  ],
-);
-
-// ENUM: GPUVertexFormat
-webidl.converters["GPUVertexFormat"] = webidl.createEnumConverter(
-  "GPUVertexFormat",
-  [
-    "uint8x2",
-    "uint8x4",
-    "sint8x2",
-    "sint8x4",
-    "unorm8x2",
-    "unorm8x4",
-    "snorm8x2",
-    "snorm8x4",
-    "uint16x2",
-    "uint16x4",
-    "sint16x2",
-    "sint16x4",
-    "unorm16x2",
-    "unorm16x4",
-    "snorm16x2",
-    "snorm16x4",
-    "float16x2",
-    "float16x4",
-    "float32",
-    "float32x2",
-    "float32x3",
-    "float32x4",
-    "uint32",
-    "uint32x2",
-    "uint32x3",
-    "uint32x4",
-    "sint32",
-    "sint32x2",
-    "sint32x3",
-    "sint32x4",
-    "unorm10-10-10-2",
-  ],
-);
-
-// DICTIONARY: GPUVertexAttribute
-const dictMembersGPUVertexAttribute = [
-  {
-    key: "format",
-    converter: webidl.converters["GPUVertexFormat"],
-    required: true,
-  },
-  {
-    key: "offset",
-    converter: webidl.converters["GPUSize64"],
-    required: true,
-  },
-  {
-    key: "shaderLocation",
-    converter: webidl.converters["GPUIndex32"],
-    required: true,
-  },
-];
-webidl.converters["GPUVertexAttribute"] = webidl.createDictionaryConverter(
-  "GPUVertexAttribute",
-  dictMembersGPUVertexAttribute,
-);
-
-// DICTIONARY: GPUVertexBufferLayout
-const dictMembersGPUVertexBufferLayout = [
-  {
-    key: "arrayStride",
-    converter: webidl.converters["GPUSize64"],
-    required: true,
-  },
-  {
-    key: "stepMode",
-    converter: webidl.converters["GPUVertexStepMode"],
-    defaultValue: "vertex",
-  },
-  {
-    key: "attributes",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUVertexAttribute"],
-    ),
-    required: true,
-  },
-];
-webidl.converters["GPUVertexBufferLayout"] = webidl.createDictionaryConverter(
-  "GPUVertexBufferLayout",
-  dictMembersGPUVertexBufferLayout,
-);
-
-// DICTIONARY: GPUVertexState
-const dictMembersGPUVertexState = [
-  {
-    key: "buffers",
-    converter: webidl.createSequenceConverter(
-      webidl.createNullableConverter(
-        webidl.converters["GPUVertexBufferLayout"],
-      ),
-    ),
-    get defaultValue() {
-      return [];
-    },
-  },
-];
-webidl.converters["GPUVertexState"] = webidl.createDictionaryConverter(
-  "GPUVertexState",
-  dictMembersGPUProgrammableStage,
-  dictMembersGPUVertexState,
-);
-
-// ENUM: GPUPrimitiveTopology
-webidl.converters["GPUPrimitiveTopology"] = webidl.createEnumConverter(
-  "GPUPrimitiveTopology",
-  [
-    "point-list",
-    "line-list",
-    "line-strip",
-    "triangle-list",
-    "triangle-strip",
-  ],
-);
-
-// ENUM: GPUIndexFormat
-webidl.converters["GPUIndexFormat"] = webidl.createEnumConverter(
-  "GPUIndexFormat",
-  [
-    "uint16",
-    "uint32",
-  ],
-);
-
-// ENUM: GPUFrontFace
-webidl.converters["GPUFrontFace"] = webidl.createEnumConverter(
-  "GPUFrontFace",
-  [
-    "ccw",
-    "cw",
-  ],
-);
-
-// ENUM: GPUCullMode
-webidl.converters["GPUCullMode"] = webidl.createEnumConverter("GPUCullMode", [
-  "none",
-  "front",
-  "back",
-]);
-
-// DICTIONARY: GPUPrimitiveState
-const dictMembersGPUPrimitiveState = [
-  {
-    key: "topology",
-    converter: webidl.converters["GPUPrimitiveTopology"],
-    defaultValue: "triangle-list",
-  },
-  { key: "stripIndexFormat", converter: webidl.converters["GPUIndexFormat"] },
-  {
-    key: "frontFace",
-    converter: webidl.converters["GPUFrontFace"],
-    defaultValue: "ccw",
-  },
-  {
-    key: "cullMode",
-    converter: webidl.converters["GPUCullMode"],
-    defaultValue: "none",
-  },
-  {
-    key: "unclippedDepth",
-    converter: webidl.converters["boolean"],
-    defaultValue: false,
-  },
-];
-webidl.converters["GPUPrimitiveState"] = webidl.createDictionaryConverter(
-  "GPUPrimitiveState",
-  dictMembersGPUPrimitiveState,
-);
-
-// ENUM: GPUStencilOperation
-webidl.converters["GPUStencilOperation"] = webidl.createEnumConverter(
-  "GPUStencilOperation",
-  [
-    "keep",
-    "zero",
-    "replace",
-    "invert",
-    "increment-clamp",
-    "decrement-clamp",
-    "increment-wrap",
-    "decrement-wrap",
-  ],
-);
-
-// DICTIONARY: GPUStencilFaceState
-const dictMembersGPUStencilFaceState = [
-  {
-    key: "compare",
-    converter: webidl.converters["GPUCompareFunction"],
-    defaultValue: "always",
-  },
-  {
-    key: "failOp",
-    converter: webidl.converters["GPUStencilOperation"],
-    defaultValue: "keep",
-  },
-  {
-    key: "depthFailOp",
-    converter: webidl.converters["GPUStencilOperation"],
-    defaultValue: "keep",
-  },
-  {
-    key: "passOp",
-    converter: webidl.converters["GPUStencilOperation"],
-    defaultValue: "keep",
-  },
-];
-webidl.converters["GPUStencilFaceState"] = webidl.createDictionaryConverter(
-  "GPUStencilFaceState",
-  dictMembersGPUStencilFaceState,
-);
-
-// TYPEDEF: GPUStencilValue
-webidl.converters["GPUStencilValue"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// TYPEDEF: GPUDepthBias
-webidl.converters["GPUDepthBias"] = (V, opts) =>
-  webidl.converters["long"](V, { ...opts, enforceRange: true });
-
-// DICTIONARY: GPUDepthStencilState
-const dictMembersGPUDepthStencilState = [
-  {
-    key: "format",
-    converter: webidl.converters["GPUTextureFormat"],
-    required: true,
-  },
-  {
-    key: "depthWriteEnabled",
-    converter: webidl.converters["boolean"],
-    required: true,
-  },
-  {
-    key: "depthCompare",
-    converter: webidl.converters["GPUCompareFunction"],
-    required: true,
-  },
-  {
-    key: "stencilFront",
-    converter: webidl.converters["GPUStencilFaceState"],
-    get defaultValue() {
-      return {};
-    },
-  },
-  {
-    key: "stencilBack",
-    converter: webidl.converters["GPUStencilFaceState"],
-    get defaultValue() {
-      return {};
-    },
-  },
-  {
-    key: "stencilReadMask",
-    converter: webidl.converters["GPUStencilValue"],
-    defaultValue: 0xFFFFFFFF,
-  },
-  {
-    key: "stencilWriteMask",
-    converter: webidl.converters["GPUStencilValue"],
-    defaultValue: 0xFFFFFFFF,
-  },
-  {
-    key: "depthBias",
-    converter: webidl.converters["GPUDepthBias"],
-    defaultValue: 0,
-  },
-  {
-    key: "depthBiasSlopeScale",
-    converter: webidl.converters["float"],
-    defaultValue: 0,
-  },
-  {
-    key: "depthBiasClamp",
-    converter: webidl.converters["float"],
-    defaultValue: 0,
-  },
-];
-webidl.converters["GPUDepthStencilState"] = webidl.createDictionaryConverter(
-  "GPUDepthStencilState",
-  dictMembersGPUDepthStencilState,
-);
-
-// TYPEDEF: GPUSampleMask
-webidl.converters["GPUSampleMask"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// DICTIONARY: GPUMultisampleState
-const dictMembersGPUMultisampleState = [
-  {
-    key: "count",
-    converter: webidl.converters["GPUSize32"],
-    defaultValue: 1,
-  },
-  {
-    key: "mask",
-    converter: webidl.converters["GPUSampleMask"],
-    defaultValue: 0xFFFFFFFF,
-  },
-  {
-    key: "alphaToCoverageEnabled",
-    converter: webidl.converters["boolean"],
-    defaultValue: false,
-  },
-];
-webidl.converters["GPUMultisampleState"] = webidl.createDictionaryConverter(
-  "GPUMultisampleState",
-  dictMembersGPUMultisampleState,
-);
-
-// ENUM: GPUBlendFactor
-webidl.converters["GPUBlendFactor"] = webidl.createEnumConverter(
-  "GPUBlendFactor",
-  [
-    "zero",
-    "one",
-    "src",
-    "one-minus-src",
-    "src-alpha",
-    "one-minus-src-alpha",
-    "dst",
-    "one-minus-dst",
-    "dst-alpha",
-    "one-minus-dst-alpha",
-    "src-alpha-saturated",
-    "constant",
-    "one-minus-constant",
-    "src1",
-    "one-minus-src1",
-    "src1-alpha",
-    "one-minus-src1-alpha",
-  ],
-);
-
-// ENUM: GPUBlendOperation
-webidl.converters["GPUBlendOperation"] = webidl.createEnumConverter(
-  "GPUBlendOperation",
-  [
-    "add",
-    "subtract",
-    "reverse-subtract",
-    "min",
-    "max",
-  ],
-);
-
-// DICTIONARY: GPUBlendComponent
-const dictMembersGPUBlendComponent = [
-  {
-    key: "srcFactor",
-    converter: webidl.converters["GPUBlendFactor"],
-    defaultValue: "one",
-  },
-  {
-    key: "dstFactor",
-    converter: webidl.converters["GPUBlendFactor"],
-    defaultValue: "zero",
-  },
-  {
-    key: "operation",
-    converter: webidl.converters["GPUBlendOperation"],
-    defaultValue: "add",
-  },
-];
-webidl.converters["GPUBlendComponent"] = webidl.createDictionaryConverter(
-  "GPUBlendComponent",
-  dictMembersGPUBlendComponent,
-);
-
-// DICTIONARY: GPUBlendState
-const dictMembersGPUBlendState = [
-  {
-    key: "color",
-    converter: webidl.converters["GPUBlendComponent"],
-    required: true,
-  },
-  {
-    key: "alpha",
-    converter: webidl.converters["GPUBlendComponent"],
-    required: true,
-  },
-];
-webidl.converters["GPUBlendState"] = webidl.createDictionaryConverter(
-  "GPUBlendState",
-  dictMembersGPUBlendState,
-);
-
-// TYPEDEF: GPUColorWriteFlags
-webidl.converters["GPUColorWriteFlags"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
-
-// DICTIONARY: GPUColorTargetState
-const dictMembersGPUColorTargetState = [
-  {
-    key: "format",
-    converter: webidl.converters["GPUTextureFormat"],
-    required: true,
-  },
-  { key: "blend", converter: webidl.converters["GPUBlendState"] },
-  {
-    key: "writeMask",
-    converter: webidl.converters["GPUColorWriteFlags"],
-    defaultValue: 0xF,
-  },
-];
-webidl.converters["GPUColorTargetState"] = webidl.createDictionaryConverter(
-  "GPUColorTargetState",
-  dictMembersGPUColorTargetState,
-);
-
-// DICTIONARY: GPUFragmentState
-const dictMembersGPUFragmentState = [
-  {
-    key: "targets",
-    converter: webidl.createSequenceConverter(
-      webidl.createNullableConverter(
-        webidl.converters["GPUColorTargetState"],
-      ),
-    ),
-    required: true,
-  },
-];
-webidl.converters["GPUFragmentState"] = webidl.createDictionaryConverter(
-  "GPUFragmentState",
-  dictMembersGPUProgrammableStage,
-  dictMembersGPUFragmentState,
-);
-
-// DICTIONARY: GPURenderPipelineDescriptor
-const dictMembersGPURenderPipelineDescriptor = [
-  {
-    key: "vertex",
-    converter: webidl.converters["GPUVertexState"],
-    required: true,
-  },
-  {
-    key: "primitive",
-    converter: webidl.converters["GPUPrimitiveState"],
-    get defaultValue() {
-      return {};
-    },
-  },
-  {
-    key: "depthStencil",
-    converter: webidl.converters["GPUDepthStencilState"],
-  },
-  {
-    key: "multisample",
-    converter: webidl.converters["GPUMultisampleState"],
-    get defaultValue() {
-      return {};
-    },
-  },
-  { key: "fragment", converter: webidl.converters["GPUFragmentState"] },
-];
-webidl.converters["GPURenderPipelineDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPURenderPipelineDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUPipelineDescriptorBase,
-    dictMembersGPURenderPipelineDescriptor,
-  );
-
-// INTERFACE: GPUColorWrite
-webidl.converters.GPUColorWrite = webidl.createInterfaceConverter(
-  "GPUColorWrite",
-  GPUColorWrite.prototype,
-);
-
-// INTERFACE: GPUCommandBuffer
-webidl.converters.GPUCommandBuffer = webidl.createInterfaceConverter(
-  "GPUCommandBuffer",
-  GPUCommandBuffer.prototype,
-);
-webidl.converters["sequence<GPUCommandBuffer>"] = webidl
-  .createSequenceConverter(webidl.converters["GPUCommandBuffer"]);
-
-// DICTIONARY: GPUCommandBufferDescriptor
-const dictMembersGPUCommandBufferDescriptor = [];
-webidl.converters["GPUCommandBufferDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUCommandBufferDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUCommandBufferDescriptor,
-  );
-
-// INTERFACE: GPUCommandEncoder
-webidl.converters.GPUCommandEncoder = webidl.createInterfaceConverter(
-  "GPUCommandEncoder",
-  GPUCommandEncoder.prototype,
-);
-
-// DICTIONARY: GPUCommandEncoderDescriptor
-const dictMembersGPUCommandEncoderDescriptor = [];
-webidl.converters["GPUCommandEncoderDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUCommandEncoderDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUCommandEncoderDescriptor,
-  );
-
-// DICTIONARY: GPUTexelCopyBufferLayout
-const dictMembersGPUTexelCopyBufferLayout = [
-  {
-    key: "offset",
-    converter: webidl.converters["GPUSize64"],
-    defaultValue: 0,
-  },
-  { key: "bytesPerRow", converter: webidl.converters["GPUSize32"] },
-  { key: "rowsPerImage", converter: webidl.converters["GPUSize32"] },
-];
-webidl.converters["GPUTexelCopyBufferLayout"] = webidl.createDictionaryConverter(
-  "GPUTexelCopyBufferLayout",
-  dictMembersGPUTexelCopyBufferLayout,
-);
-
-// DICTIONARY: GPUTexelCopyBufferInfo
-const dictMembersGPUTexelCopyBufferInfo = [
-  {
-    key: "buffer",
-    converter: webidl.converters["GPUBuffer"],
-    required: true,
-  },
-];
-webidl.converters["GPUTexelCopyBufferInfo"] = webidl.createDictionaryConverter(
-  "GPUTexelCopyBufferInfo",
-  dictMembersGPUTexelCopyBufferLayout,
-  dictMembersGPUTexelCopyBufferInfo,
-);
-
-// DICTIONARY: GPUOrigin3DDict
-const dictMembersGPUOrigin3DDict = [
-  {
-    key: "x",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-  {
-    key: "y",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-  {
-    key: "z",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-];
-webidl.converters["GPUOrigin3DDict"] = webidl.createDictionaryConverter(
-  "GPUOrigin3DDict",
-  dictMembersGPUOrigin3DDict,
-);
-
-// TYPEDEF: GPUOrigin3D
-webidl.converters["GPUOrigin3D"] = (V, opts) => {
-  // Union for (sequence<GPUIntegerCoordinate> or GPUOrigin3DDict)
-  if (V === null || V === undefined) {
-    return webidl.converters["GPUOrigin3DDict"](V, opts);
-  }
-  if (typeof V === "object") {
-    const method = V[SymbolIterator];
-    if (method !== undefined) {
-      // validate length of GPUOrigin3D
-      const length = 3;
-      if (V.length > length) {
-        throw webidl.makeException(
-          TypeError,
-          `A sequence of number used as a GPUOrigin3D must have at most ${length} elements, received ${V.length} elements`,
-          opts,
-        );
-      }
-      return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
-    }
-    return webidl.converters["GPUOrigin3DDict"](V, opts);
-  }
-  throw webidl.makeException(
-    TypeError,
-    "can not be converted to sequence<GPUIntegerCoordinate> or GPUOrigin3DDict",
-    opts,
-  );
-};
-
-// DICTIONARY: GPUTexelCopyTextureInfo
-const dictMembersGPUTexelCopyTextureInfo = [
-  {
-    key: "texture",
-    converter: webidl.converters["GPUTexture"],
-    required: true,
-  },
-  {
-    key: "mipLevel",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-  {
-    key: "origin",
-    converter: webidl.converters["GPUOrigin3D"],
-    get defaultValue() {
-      return {};
-    },
-  },
-  {
-    key: "aspect",
-    converter: webidl.converters["GPUTextureAspect"],
-    defaultValue: "all",
-  },
-];
-webidl.converters["GPUTexelCopyTextureInfo"] = webidl.createDictionaryConverter(
-  "GPUTexelCopyTextureInfo",
-  dictMembersGPUTexelCopyTextureInfo,
-);
-
-// DICTIONARY: GPUOrigin2DDict
-const dictMembersGPUOrigin2DDict = [
-  {
-    key: "x",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-  {
-    key: "y",
-    converter: webidl.converters["GPUIntegerCoordinate"],
-    defaultValue: 0,
-  },
-];
-webidl.converters["GPUOrigin2DDict"] = webidl.createDictionaryConverter(
-  "GPUOrigin2DDict",
-  dictMembersGPUOrigin2DDict,
-);
-
-// TYPEDEF: GPUOrigin2D
-webidl.converters["GPUOrigin2D"] = (V, opts) => {
-  // Union for (sequence<GPUIntegerCoordinate> or GPUOrigin2DDict)
-  if (V === null || V === undefined) {
-    return webidl.converters["GPUOrigin2DDict"](V, opts);
-  }
-  if (typeof V === "object") {
-    const method = V[SymbolIterator];
-    if (method !== undefined) {
-      // validate length of GPUOrigin2D
-      const length = 2;
-      if (V.length > length) {
-        throw webidl.makeException(
-          TypeError,
-          `A sequence of number used as a GPUOrigin2D must have at most ${length} elements`,
-          opts,
-        );
-      }
-      return webidl.converters["sequence<GPUIntegerCoordinate>"](V, opts);
-    }
-    return webidl.converters["GPUOrigin2DDict"](V, opts);
-  }
-  throw webidl.makeException(
-    TypeError,
-    "can not be converted to sequence<GPUIntegerCoordinate> or GPUOrigin2DDict.",
-    opts,
-  );
-};
-
-// INTERFACE: GPUComputePassEncoder
-webidl.converters.GPUComputePassEncoder = webidl.createInterfaceConverter(
-  "GPUComputePassEncoder",
-  GPUComputePassEncoder.prototype,
-);
-
-// INTERFACE: GPUQuerySet
-webidl.converters.GPUQuerySet = webidl.createInterfaceConverter(
-  "GPUQuerySet",
-  GPUQuerySet.prototype,
-);
-
-// DICTIONARY: GPUComputePassTimestampWrites
-webidl.converters["GPUComputePassTimestampWrites"] = webidl
-  .createDictionaryConverter(
-    "GPUComputePassTimestampWrites",
-    [
-      {
-        key: "querySet",
-        converter: webidl.converters["GPUQuerySet"],
-        required: true,
-      },
-      {
-        key: "beginningOfPassWriteIndex",
-        converter: webidl.converters["GPUSize32"],
-      },
-      {
-        key: "endOfPassWriteIndex",
-        converter: webidl.converters["GPUSize32"],
-      },
-    ],
-  );
-
-// DICTIONARY: GPUComputePassDescriptor
-const dictMembersGPUComputePassDescriptor = [
-  {
-    key: "timestampWrites",
-    converter: webidl.converters["GPUComputePassTimestampWrites"],
-  },
-];
-webidl.converters["GPUComputePassDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPUComputePassDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPUComputePassDescriptor,
-  );
-
-// INTERFACE: GPURenderPassEncoder
-webidl.converters.GPURenderPassEncoder = webidl.createInterfaceConverter(
-  "GPURenderPassEncoder",
-  GPURenderPassEncoder.prototype,
-);
-
-// ENUM: GPULoadOp
-webidl.converters["GPULoadOp"] = webidl.createEnumConverter("GPULoadOp", [
-  "load",
-  "clear",
-]);
-
-// DICTIONARY: GPUColorDict
-const dictMembersGPUColorDict = [
-  { key: "r", converter: webidl.converters["double"], required: true },
-  { key: "g", converter: webidl.converters["double"], required: true },
-  { key: "b", converter: webidl.converters["double"], required: true },
-  { key: "a", converter: webidl.converters["double"], required: true },
-];
-webidl.converters["GPUColorDict"] = webidl.createDictionaryConverter(
-  "GPUColorDict",
-  dictMembersGPUColorDict,
-);
-
-// TYPEDEF: GPUColor
-webidl.converters["GPUColor"] = (V, opts) => {
-  // Union for (sequence<double> or GPUColorDict)
-  if (V === null || V === undefined) {
-    return webidl.converters["GPUColorDict"](V, opts);
-  }
-  if (typeof V === "object") {
-    const method = V[SymbolIterator];
-    if (method !== undefined) {
-      // validate length of GPUColor
-      const length = 4;
-      if (V.length !== length) {
-        throw webidl.makeException(
-          TypeError,
-          `A sequence of number used as a GPUColor must have exactly ${length} elements, received ${V.length} elements`,
-          opts,
-        );
-      }
-      return webidl.converters["sequence<double>"](V, opts);
-    }
-    return webidl.converters["GPUColorDict"](V, opts);
-  }
-  throw webidl.makeException(
-    TypeError,
-    "can not be converted to sequence<double> or GPUColorDict",
-    opts,
-  );
-};
-
-// ENUM: GPUStoreOp
-webidl.converters["GPUStoreOp"] = webidl.createEnumConverter("GPUStoreOp", [
-  "store",
-  "discard",
-]);
-
-// DICTIONARY: GPURenderPassColorAttachment
-const dictMembersGPURenderPassColorAttachment = [
-  {
-    key: "view",
-    converter: webidl.converters["GPUTextureView"],
-    required: true,
-  },
-  { key: "resolveTarget", converter: webidl.converters["GPUTextureView"] },
-  {
-    key: "clearValue",
-    converter: webidl.converters["GPUColor"],
-  },
-  {
-    key: "loadOp",
-    converter: webidl.converters["GPULoadOp"],
-    required: true,
-  },
-  {
-    key: "storeOp",
-    converter: webidl.converters["GPUStoreOp"],
-    required: true,
-  },
-];
-webidl.converters["GPURenderPassColorAttachment"] = webidl
-  .createDictionaryConverter(
-    "GPURenderPassColorAttachment",
-    dictMembersGPURenderPassColorAttachment,
-  );
-
-// DICTIONARY: GPURenderPassDepthStencilAttachment
-const dictMembersGPURenderPassDepthStencilAttachment = [
-  {
-    key: "view",
-    converter: webidl.converters["GPUTextureView"],
-    required: true,
-  },
-  {
-    key: "depthClearValue",
-    converter: webidl.converters["float"],
-  },
-  {
-    key: "depthLoadOp",
-    converter: webidl.converters["GPULoadOp"],
-  },
-  {
-    key: "depthStoreOp",
-    converter: webidl.converters["GPUStoreOp"],
-  },
-  {
-    key: "depthReadOnly",
-    converter: webidl.converters["boolean"],
-    defaultValue: false,
-  },
-  {
-    key: "stencilClearValue",
-    converter: webidl.converters["GPUStencilValue"],
-    defaultValue: 0,
-  },
-  {
-    key: "stencilLoadOp",
-    converter: webidl.converters["GPULoadOp"],
-  },
-  {
-    key: "stencilStoreOp",
-    converter: webidl.converters["GPUStoreOp"],
-  },
-  {
-    key: "stencilReadOnly",
-    converter: webidl.converters["boolean"],
-    defaultValue: false,
-  },
-];
-webidl.converters["GPURenderPassDepthStencilAttachment"] = webidl
-  .createDictionaryConverter(
-    "GPURenderPassDepthStencilAttachment",
-    dictMembersGPURenderPassDepthStencilAttachment,
-  );
-
-// DICTIONARY: GPURenderPassTimestampWrites
-webidl.converters["GPURenderPassTimestampWrites"] = webidl
-  .createDictionaryConverter(
-    "GPURenderPassTimestampWrites",
-    [
-      {
-        key: "querySet",
-        converter: webidl.converters["GPUQuerySet"],
-        required: true,
-      },
-      {
-        key: "beginningOfPassWriteIndex",
-        converter: webidl.converters["GPUSize32"],
-      },
-      {
-        key: "endOfPassWriteIndex",
-        converter: webidl.converters["GPUSize32"],
-      },
-    ],
-  );
-
-// DICTIONARY: GPURenderPassDescriptor
-const dictMembersGPURenderPassDescriptor = [
-  {
-    key: "colorAttachments",
-    converter: webidl.createSequenceConverter(
-      webidl.createNullableConverter(
-        webidl.converters["GPURenderPassColorAttachment"],
-      ),
-    ),
-    required: true,
-  },
-  {
-    key: "depthStencilAttachment",
-    converter: webidl.converters["GPURenderPassDepthStencilAttachment"],
-  },
-  {
-    key: "occlusionQuerySet",
-    converter: webidl.converters["GPUQuerySet"],
-  },
-  {
-    key: "timestampWrites",
-    converter: webidl.converters["GPURenderPassTimestampWrites"],
-  },
-];
-webidl.converters["GPURenderPassDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPURenderPassDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPURenderPassDescriptor,
-  );
-
-// INTERFACE: GPURenderBundle
-webidl.converters.GPURenderBundle = webidl.createInterfaceConverter(
-  "GPURenderBundle",
-  GPURenderBundle.prototype,
-);
-webidl.converters["sequence<GPURenderBundle>"] = webidl
-  .createSequenceConverter(webidl.converters["GPURenderBundle"]);
-
-// DICTIONARY: GPURenderBundleDescriptor
-const dictMembersGPURenderBundleDescriptor = [];
-webidl.converters["GPURenderBundleDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPURenderBundleDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPURenderBundleDescriptor,
-  );
-
-// INTERFACE: GPURenderBundleEncoder
-webidl.converters.GPURenderBundleEncoder = webidl.createInterfaceConverter(
-  "GPURenderBundleEncoder",
-  GPURenderBundleEncoder.prototype,
-);
-
-// DICTIONARY: GPURenderPassLayout
-const dictMembersGPURenderPassLayout = [
-  {
-    key: "colorFormats",
-    converter: webidl.createSequenceConverter(
-      webidl.createNullableConverter(webidl.converters["GPUTextureFormat"]),
-    ),
-    required: true,
-  },
-  {
-    key: "depthStencilFormat",
-    converter: webidl.converters["GPUTextureFormat"],
-  },
-  {
-    key: "sampleCount",
-    converter: webidl.converters["GPUSize32"],
-    defaultValue: 1,
-  },
-];
-webidl.converters["GPURenderPassLayout"] = webidl
-  .createDictionaryConverter(
-    "GPURenderPassLayout",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPURenderPassLayout,
-  );
-
-// DICTIONARY: GPURenderBundleEncoderDescriptor
-const dictMembersGPURenderBundleEncoderDescriptor = [
-  {
-    key: "depthReadOnly",
-    converter: webidl.converters.boolean,
-    defaultValue: false,
-  },
-  {
-    key: "stencilReadOnly",
-    converter: webidl.converters.boolean,
-    defaultValue: false,
-  },
-];
-webidl.converters["GPURenderBundleEncoderDescriptor"] = webidl
-  .createDictionaryConverter(
-    "GPURenderBundleEncoderDescriptor",
-    dictMembersGPUObjectDescriptorBase,
-    dictMembersGPURenderPassLayout,
-    dictMembersGPURenderBundleEncoderDescriptor,
-  );
-
-// INTERFACE: GPUQueue
-webidl.converters.GPUQueue = webidl.createInterfaceConverter(
-  "GPUQueue",
-  GPUQueue.prototype,
-);
-
-// ENUM: GPUQueryType
-webidl.converters["GPUQueryType"] = webidl.createEnumConverter(
-  "GPUQueryType",
-  [
-    "occlusion",
-    "timestamp",
-  ],
-);
-
-// DICTIONARY: GPUQuerySetDescriptor
-const dictMembersGPUQuerySetDescriptor = [
-  {
-    key: "type",
-    converter: webidl.converters["GPUQueryType"],
-    required: true,
-  },
-  { key: "count", converter: webidl.converters["GPUSize32"], required: true },
-  {
-    key: "pipelineStatistics",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUPipelineStatisticName"],
-    ),
-    get defaultValue() {
-      return [];
-    },
-  },
-];
-webidl.converters["GPUQuerySetDescriptor"] = webidl.createDictionaryConverter(
-  "GPUQuerySetDescriptor",
-  dictMembersGPUObjectDescriptorBase,
-  dictMembersGPUQuerySetDescriptor,
-);
-
-// ENUM: GPUDeviceLostReason
-webidl.converters["GPUDeviceLostReason"] = webidl.createEnumConverter(
-  "GPUDeviceLostReason",
-  [
-    "destroyed",
-  ],
-);
-
-// // INTERFACE: GPUDeviceLostInfo
-// webidl.converters.GPUDeviceLostInfo = webidl.createInterfaceConverter(
-//   "GPUDeviceLostInfo",
-//   GPUDeviceLostInfo.prototype,
-// );
-
-// ENUM: GPUErrorFilter
-webidl.converters["GPUErrorFilter"] = webidl.createEnumConverter(
-  "GPUErrorFilter",
-  [
-    "out-of-memory",
-    "validation",
-    "internal",
-  ],
-);
-
-// INTERFACE: GPUOutOfMemoryError
-webidl.converters.GPUOutOfMemoryError = webidl.createInterfaceConverter(
-  "GPUOutOfMemoryError",
-  GPUOutOfMemoryError.prototype,
-);
-
-// INTERFACE: GPUValidationError
-webidl.converters.GPUValidationError = webidl.createInterfaceConverter(
-  "GPUValidationError",
-  GPUValidationError.prototype,
-);
-
-// TYPEDEF: GPUError
-webidl.converters["GPUError"] = webidl.converters.any /** put union here! **/;
-
-// // INTERFACE: GPUUncapturedErrorEvent
-// webidl.converters.GPUUncapturedErrorEvent = webidl.createInterfaceConverter(
-//   "GPUUncapturedErrorEvent",
-//   GPUUncapturedErrorEvent.prototype,
-// );
-
-// DICTIONARY: GPUUncapturedErrorEventInit
 const dictMembersGPUUncapturedErrorEventInit = [
   { key: "error", converter: webidl.converters["GPUError"], required: true },
 ];
@@ -7146,84 +756,18 @@ webidl.converters["GPUUncapturedErrorEventInit"] = webidl
     dictMembersGPUUncapturedErrorEventInit,
   );
 
-// TYPEDEF: GPUBufferDynamicOffset
-webidl.converters["GPUBufferDynamicOffset"] = (V, opts) =>
-  webidl.converters["unsigned long"](V, { ...opts, enforceRange: true });
+let gpu;
+function initGPU() {
+  if (!gpu) {
+    gpu = op_create_gpu(
+      webidl.brand,
+      setEventTargetData,
+      GPUUncapturedErrorEvent,
+    );
+  }
+}
 
-// TYPEDEF: GPUSignedOffset32
-webidl.converters["GPUSignedOffset32"] = (V, opts) =>
-  webidl.converters["long"](V, { ...opts, enforceRange: true });
-
-// TYPEDEF: GPUFlagsConstant
-webidl.converters["GPUFlagsConstant"] = webidl.converters["unsigned long"];
-
-// ENUM: GPUCanvasAlphaMode
-webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
-  "GPUCanvasAlphaMode",
-  [
-    "opaque",
-    "premultiplied",
-  ],
-);
-
-// NON-SPEC: ENUM: GPUPresentMode
-webidl.converters["GPUPresentMode"] = webidl.createEnumConverter(
-  "GPUPresentMode",
-  [
-    "autoVsync",
-    "autoNoVsync",
-    "fifo",
-    "fifoRelaxed",
-    "immediate",
-    "mailbox",
-  ],
-);
-
-// DICT: GPUCanvasConfiguration
-const dictMembersGPUCanvasConfiguration = [
-  { key: "device", converter: webidl.converters.GPUDevice, required: true },
-  {
-    key: "format",
-    converter: webidl.converters.GPUTextureFormat,
-    required: true,
-  },
-  {
-    key: "usage",
-    converter: webidl.converters["GPUTextureUsageFlags"],
-    defaultValue: GPUTextureUsage.RENDER_ATTACHMENT,
-  },
-  {
-    key: "alphaMode",
-    converter: webidl.converters["GPUCanvasAlphaMode"],
-    defaultValue: "opaque",
-  },
-
-  // Extended from spec
-  {
-    key: "presentMode",
-    converter: webidl.converters["GPUPresentMode"],
-  },
-  {
-    key: "viewFormats",
-    converter: webidl.createSequenceConverter(
-      webidl.converters["GPUTextureFormat"],
-    ),
-    get defaultValue() {
-      return [];
-    },
-  },
-];
-webidl.converters["GPUCanvasConfiguration"] = webidl
-  .createDictionaryConverter(
-    "GPUCanvasConfiguration",
-    dictMembersGPUCanvasConfiguration,
-  );
-
-const gpu = webidl.createBranded(GPU);
 export {
-  _device,
-  assertDevice,
-  createGPUTexture,
   GPU,
   gpu,
   GPUAdapter,
@@ -7260,4 +804,5 @@ export {
   GPUTextureView,
   GPUUncapturedErrorEvent,
   GPUValidationError,
+  initGPU,
 };
diff --git a/deno_webgpu/02_surface.js b/deno_webgpu/02_surface.js
index 1861b59b7..565097693 100644
--- a/deno_webgpu/02_surface.js
+++ b/deno_webgpu/02_surface.js
@@ -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 };
diff --git a/deno_webgpu/Cargo.toml b/deno_webgpu/Cargo.toml
index 9a55242d1..29786bf4c 100644
--- a/deno_webgpu/Cargo.toml
+++ b/deno_webgpu/Cargo.toml
@@ -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
 #
diff --git a/deno_webgpu/adapter.rs b/deno_webgpu/adapter.rs
new file mode 100644
index 000000000..ffa8827b3
--- /dev/null
+++ b/deno_webgpu/adapter.rs
@@ -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
+    }
+}
diff --git a/deno_webgpu/bind_group.rs b/deno_webgpu/bind_group.rs
new file mode 100644
index 000000000..f147c2178
--- /dev/null
+++ b/deno_webgpu/bind_group.rs
@@ -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)
+            })
+    }
+}
diff --git a/deno_webgpu/bind_group_layout.rs b/deno_webgpu/bind_group_layout.rs
new file mode 100644
index 000000000..319f626e7
--- /dev/null
+++ b/deno_webgpu/bind_group_layout.rs
@@ -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,
+        }
+    }
+}
diff --git a/deno_webgpu/binding.rs b/deno_webgpu/binding.rs
deleted file mode 100644
index 6d9ba92a0..000000000
--- a/deno_webgpu/binding.rs
+++ /dev/null
@@ -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)
-}
diff --git a/deno_webgpu/buffer.rs b/deno_webgpu/buffer.rs
index 978d31368..be95e9158 100644
--- a/deno_webgpu/buffer.rs
+++ b/deno_webgpu/buffer.rs
@@ -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()))
+    }
 }
diff --git a/deno_webgpu/bundle.rs b/deno_webgpu/bundle.rs
deleted file mode 100644
index 48bfbb18d..000000000
--- a/deno_webgpu/bundle.rs
+++ /dev/null
@@ -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())
-}
diff --git a/deno_webgpu/byow.rs b/deno_webgpu/byow.rs
index 17182f012..bfce74abf 100644
--- a/deno_webgpu/byow.rs
+++ b/deno_webgpu/byow.rs
@@ -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"))
 }
diff --git a/deno_webgpu/command_buffer.rs b/deno_webgpu/command_buffer.rs
new file mode 100644
index 000000000..6cb9d714c
--- /dev/null
+++ b/deno_webgpu/command_buffer.rs
@@ -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,
+}
diff --git a/deno_webgpu/command_encoder.rs b/deno_webgpu/command_encoder.rs
index 02adeb0b1..b357c2164 100644
--- a/deno_webgpu/command_encoder.rs
+++ b/deno_webgpu/command_encoder.rs
@@ -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,
+                &copy_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,
+                &copy_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,
+                &copy_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,
-        &copy_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,
-        &copy_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,
-        &copy_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))
-}
diff --git a/deno_webgpu/compute_pass.rs b/deno_webgpu/compute_pass.rs
index 10fd38945..4cd060818 100644
--- a/deno_webgpu/compute_pass.rs
+++ b/deno_webgpu/compute_pass.rs
@@ -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>,
 }
diff --git a/deno_webgpu/compute_pipeline.rs b/deno_webgpu/compute_pipeline.rs
new file mode 100644
index 000000000..eb29686bc
--- /dev/null
+++ b/deno_webgpu/compute_pipeline.rs
@@ -0,0 +1,82 @@
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+use deno_core::cppgc::Ptr;
+use deno_core::op2;
+use deno_core::webidl::WebIdlInterfaceConverter;
+use deno_core::GarbageCollected;
+use deno_core::WebIDL;
+use indexmap::IndexMap;
+
+use crate::bind_group_layout::GPUBindGroupLayout;
+use crate::shader::GPUShaderModule;
+use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode;
+use crate::Instance;
+
+pub struct GPUComputePipeline {
+    pub instance: Instance,
+    pub error_handler: super::error::ErrorHandler,
+
+    pub id: wgpu_core::id::ComputePipelineId,
+    pub label: String,
+}
+
+impl Drop for GPUComputePipeline {
+    fn drop(&mut self) {
+        self.instance.compute_pipeline_drop(self.id);
+    }
+}
+
+impl WebIdlInterfaceConverter for GPUComputePipeline {
+    const NAME: &'static str = "GPUComputePipeline";
+}
+
+impl GarbageCollected for GPUComputePipeline {}
+
+#[op2]
+impl GPUComputePipeline {
+    #[getter]
+    #[string]
+    fn label(&self) -> String {
+        self.label.clone()
+    }
+    #[setter]
+    #[string]
+    fn label(&self, #[webidl] _label: String) {
+        // TODO(@crowlKats): no-op, needs wpgu to implement changing the label
+    }
+
+    #[cppgc]
+    fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout {
+        let (id, err) = self
+            .instance
+            .compute_pipeline_get_bind_group_layout(self.id, index, None);
+
+        self.error_handler.push_error(err);
+
+        // TODO(wgpu): needs to support retrieving the label
+        GPUBindGroupLayout {
+            instance: self.instance.clone(),
+            id,
+            label: "".to_string(),
+        }
+    }
+}
+
+#[derive(WebIDL)]
+#[webidl(dictionary)]
+pub(crate) struct GPUComputePipelineDescriptor {
+    #[webidl(default = String::new())]
+    pub label: String,
+
+    pub compute: GPUProgrammableStage,
+    pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
+}
+
+#[derive(WebIDL)]
+#[webidl(dictionary)]
+pub(crate) struct GPUProgrammableStage {
+    pub module: Ptr<GPUShaderModule>,
+    pub entry_point: Option<String>,
+    #[webidl(default = Default::default())]
+    pub constants: IndexMap<String, f64>,
+}
diff --git a/deno_webgpu/device.rs b/deno_webgpu/device.rs
new file mode 100644
index 000000000..d4772e7cb
--- /dev/null
+++ b/deno_webgpu/device.rs
@@ -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"
+    }
+}
diff --git a/deno_webgpu/error.rs b/deno_webgpu/error.rs
index 8af30f62d..377ae9af6 100644
--- a/deno_webgpu/error.rs
+++ b/deno_webgpu/error.rs
@@ -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))
     }
 }
diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs
index 2527c25c6..44ed02ff0 100644
--- a/deno_webgpu/lib.rs
+++ b/deno_webgpu/lib.rs
@@ -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))
+    }
 }
diff --git a/deno_webgpu/pipeline.rs b/deno_webgpu/pipeline.rs
deleted file mode 100644
index c8ba5f16f..000000000
--- a/deno_webgpu/pipeline.rs
+++ /dev/null
@@ -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),
-    })
-}
diff --git a/deno_webgpu/pipeline_layout.rs b/deno_webgpu/pipeline_layout.rs
new file mode 100644
index 000000000..c785eea73
--- /dev/null
+++ b/deno_webgpu/pipeline_layout.rs
@@ -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>>,
+}
diff --git a/deno_webgpu/query_set.rs b/deno_webgpu/query_set.rs
new file mode 100644
index 000000000..cf10d6427
--- /dev/null
+++ b/deno_webgpu/query_set.rs
@@ -0,0 +1,87 @@
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+use deno_core::op2;
+use deno_core::webidl::WebIdlInterfaceConverter;
+use deno_core::GarbageCollected;
+use deno_core::WebIDL;
+use deno_error::JsErrorBox;
+
+use crate::Instance;
+
+pub struct GPUQuerySet {
+    pub instance: Instance,
+    pub id: wgpu_core::id::QuerySetId,
+    pub r#type: GPUQueryType,
+    pub count: u32,
+    pub label: String,
+}
+
+impl Drop for GPUQuerySet {
+    fn drop(&mut self) {
+        self.instance.query_set_drop(self.id);
+    }
+}
+
+impl WebIdlInterfaceConverter for GPUQuerySet {
+    const NAME: &'static str = "GPUQuerySet";
+}
+
+impl GarbageCollected for GPUQuerySet {}
+
+#[op2]
+impl GPUQuerySet {
+    #[getter]
+    #[string]
+    fn label(&self) -> String {
+        self.label.clone()
+    }
+    #[setter]
+    #[string]
+    fn label(&self, #[webidl] _label: String) {
+        // TODO(@crowlKats): no-op, needs wpgu to implement changing the label
+    }
+
+    #[fast]
+    fn destroy(&self) -> Result<(), JsErrorBox> {
+        Err(JsErrorBox::generic(
+            "This operation is currently not supported",
+        ))
+    }
+
+    #[getter]
+    #[string]
+    fn r#type(&self) -> &'static str {
+        self.r#type.as_str()
+    }
+
+    #[getter]
+    fn count(&self) -> u32 {
+        self.count
+    }
+}
+
+#[derive(WebIDL)]
+#[webidl(dictionary)]
+pub(crate) struct GPUQuerySetDescriptor {
+    #[webidl(default = String::new())]
+    pub label: String,
+
+    pub r#type: GPUQueryType,
+    #[options(enforce_range = true)]
+    pub count: u32,
+}
+
+#[derive(WebIDL, Clone)]
+#[webidl(enum)]
+pub(crate) enum GPUQueryType {
+    Occlusion,
+    Timestamp,
+}
+impl From<GPUQueryType> for wgpu_types::QueryType {
+    fn from(value: GPUQueryType) -> Self {
+        match value {
+            GPUQueryType::Occlusion => Self::Occlusion,
+            GPUQueryType::Timestamp => Self::Timestamp,
+        }
+    }
+}
diff --git a/deno_webgpu/queue.rs b/deno_webgpu/queue.rs
index a2e7d6a50..920640f9d 100644
--- a/deno_webgpu/queue.rs
+++ b/deno_webgpu/queue.rs
@@ -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))
-}
diff --git a/deno_webgpu/render_bundle.rs b/deno_webgpu/render_bundle.rs
new file mode 100644
index 000000000..de6dec878
--- /dev/null
+++ b/deno_webgpu/render_bundle.rs
@@ -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,
+}
diff --git a/deno_webgpu/render_pass.rs b/deno_webgpu/render_pass.rs
index 0be8a1645..cdb8bd8f6 100644
--- a/deno_webgpu/render_pass.rs
+++ b/deno_webgpu/render_pass.rs
@@ -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>,
 }
diff --git a/deno_webgpu/render_pipeline.rs b/deno_webgpu/render_pipeline.rs
new file mode 100644
index 000000000..fcd9d786d
--- /dev/null
+++ b/deno_webgpu/render_pipeline.rs
@@ -0,0 +1,550 @@
+// Copyright 2018-2025 the Deno authors. MIT license.
+
+use deno_core::cppgc::Ptr;
+use deno_core::op2;
+use deno_core::webidl::Nullable;
+use deno_core::webidl::WebIdlInterfaceConverter;
+use deno_core::GarbageCollected;
+use deno_core::WebIDL;
+use indexmap::IndexMap;
+
+use crate::bind_group_layout::GPUBindGroupLayout;
+use crate::sampler::GPUCompareFunction;
+use crate::shader::GPUShaderModule;
+use crate::texture::GPUTextureFormat;
+use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode;
+use crate::Instance;
+
+pub struct GPURenderPipeline {
+    pub instance: Instance,
+    pub error_handler: super::error::ErrorHandler,
+
+    pub id: wgpu_core::id::RenderPipelineId,
+    pub label: String,
+}
+
+impl Drop for GPURenderPipeline {
+    fn drop(&mut self) {
+        self.instance.render_pipeline_drop(self.id);
+    }
+}
+
+impl WebIdlInterfaceConverter for GPURenderPipeline {
+    const NAME: &'static str = "GPURenderPipeline";
+}
+
+impl GarbageCollected for GPURenderPipeline {}
+
+#[op2]
+impl GPURenderPipeline {
+    #[getter]
+    #[string]
+    fn label(&self) -> String {
+        self.label.clone()
+    }
+    #[setter]
+    #[string]
+    fn label(&self, #[webidl] _label: String) {
+        // TODO(@crowlKats): no-op, needs wpgu to implement changing the label
+    }
+
+    #[cppgc]
+    fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout {
+        let (id, err) = self
+            .instance
+            .render_pipeline_get_bind_group_layout(self.id, index, None);
+
+        self.error_handler.push_error(err);
+
+        // TODO(wgpu): needs to add a way to retrieve the label
+        GPUBindGroupLayout {
+            instance: self.instance.clone(),
+            id,
+            label: "".to_string(),
+        }
+    }
+}
+
+#[derive(WebIDL)]
+#[webidl(dictionary)]
+pub(crate) struct GPURenderPipelineDescriptor {
+    #[webidl(default = String::new())]
+    pub label: String,
+
+    pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode,
+    pub vertex: GPUVertexState,
+    pub primitive: GPUPrimitiveState,
+    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,
+        }
+    }
+}
diff --git a/deno_webgpu/sampler.rs b/deno_webgpu/sampler.rs
index df2b7d713..46c08bc74 100644
--- a/deno_webgpu/sampler.rs
+++ b/deno_webgpu/sampler.rs
@@ -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,
+        }
+    }
 }
diff --git a/deno_webgpu/shader.rs b/deno_webgpu/shader.rs
index 7a31b805e..6b91bebb0 100644
--- a/deno_webgpu/shader.rs
+++ b/deno_webgpu/shader.rs
@@ -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,
 }
diff --git a/deno_webgpu/surface.rs b/deno_webgpu/surface.rs
index 17ff8a546..8c8445a9a 100644
--- a/deno_webgpu/surface.rs
+++ b/deno_webgpu/surface.rs
@@ -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,
+        }
+    }
 }
diff --git a/deno_webgpu/texture.rs b/deno_webgpu/texture.rs
index decd52156..4ecaa3220 100644
--- a/deno_webgpu/texture.rs
+++ b/deno_webgpu/texture.rs
@@ -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,
+            },
+        }
+    }
 }
diff --git a/deno_webgpu/webgpu.idl b/deno_webgpu/webgpu.idl
index bdff4e4e2..df2aef498 100644
--- a/deno_webgpu/webgpu.idl
+++ b/deno_webgpu/webgpu.idl
@@ -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;
 };
 
diff --git a/deno_webgpu/webidl.rs b/deno_webgpu/webidl.rs
new file mode 100644
index 000000000..98099b8a0
--- /dev/null
+++ b/deno_webgpu/webidl.rs
@@ -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
+}