Skip to main content

deno_webgpu/
lib.rs

1// Copyright 2018-2026 the Deno authors. MIT license.
2
3#![cfg(not(target_arch = "wasm32"))]
4#![warn(unsafe_op_in_unsafe_fn)]
5
6use std::cell::RefCell;
7use std::rc::Rc;
8use std::sync::Arc;
9
10use deno_core::GarbageCollected;
11use deno_core::OpState;
12use deno_core::cppgc::SameObject;
13use deno_core::op2;
14use deno_core::v8;
15pub use wgpu_core;
16pub use wgpu_types;
17use wgpu_types::PowerPreference;
18
19use crate::error::GPUGenericError;
20
21mod adapter;
22mod bind_group;
23mod bind_group_layout;
24pub mod buffer;
25mod byow;
26mod command_buffer;
27mod command_encoder;
28mod compute_pass;
29mod compute_pipeline;
30mod device;
31pub mod error;
32mod pipeline_layout;
33mod query_set;
34mod queue;
35mod render_bundle;
36mod render_pass;
37mod render_pipeline;
38mod sampler;
39mod shader;
40mod surface;
41pub mod texture;
42mod webidl;
43
44pub const UNSTABLE_FEATURE_NAME: &str = "webgpu";
45
46#[allow(clippy::print_stdout, reason = "cargo build script output")]
47pub fn print_linker_flags(name: &str) {
48  if cfg!(windows) {
49    // these dlls load slowly, so delay loading them
50    let dlls = [
51      // webgpu
52      "d3dcompiler_47",
53      "OPENGL32",
54      "gdi32",
55      "setupapi",
56      "oleaut32",
57      "combase",
58      // network related functions
59      "iphlpapi",
60      // timer functions
61      "winmm",
62      // debugging/diagnostics (only needed on panic/crash)
63      "dbghelp",
64      "psapi",
65    ];
66    for dll in dlls {
67      println!("cargo:rustc-link-arg-bin={name}=/delayload:{dll}.dll");
68    }
69    // enable delay loading
70    println!("cargo:rustc-link-arg-bin={name}=delayimp.lib");
71  }
72}
73
74pub type Instance = Arc<wgpu_core::global::Global>;
75
76deno_core::extension!(
77  deno_webgpu,
78  deps = [deno_webidl, deno_web],
79  ops = [
80    op_create_gpu,
81    device::op_webgpu_device_start_capture,
82    device::op_webgpu_device_stop_capture,
83  ],
84  objects = [
85    GPU,
86    adapter::GPUAdapter,
87    adapter::GPUAdapterInfo,
88    bind_group::GPUBindGroup,
89    bind_group_layout::GPUBindGroupLayout,
90    buffer::GPUBuffer,
91    command_buffer::GPUCommandBuffer,
92    command_encoder::GPUCommandEncoder,
93    compute_pass::GPUComputePassEncoder,
94    compute_pipeline::GPUComputePipeline,
95    device::GPUDevice,
96    device::GPUDeviceLostInfo,
97    pipeline_layout::GPUPipelineLayout,
98    query_set::GPUQuerySet,
99    queue::GPUQueue,
100    render_bundle::GPURenderBundle,
101    render_bundle::GPURenderBundleEncoder,
102    render_pass::GPURenderPassEncoder,
103    render_pipeline::GPURenderPipeline,
104    sampler::GPUSampler,
105    shader::GPUCompilationInfo,
106    shader::GPUCompilationMessage,
107    shader::GPUShaderModule,
108    adapter::GPUSupportedFeatures,
109    adapter::GPUSupportedLimits,
110    texture::GPUTexture,
111    texture::GPUTextureView,
112    texture::GPUExternalTexture,
113    byow::UnsafeWindowSurface,
114    surface::GPUCanvasContext,
115  ],
116  esm = ["00_init.js", "02_surface.js"],
117  lazy_loaded_esm = ["01_webgpu.js"],
118);
119
120#[op2]
121#[cppgc]
122pub fn op_create_gpu(
123  state: &mut OpState,
124  scope: &mut v8::PinScope<'_, '_>,
125  webidl_brand: v8::Local<v8::Value>,
126  set_event_target_data: v8::Local<v8::Value>,
127  error_event_class: v8::Local<v8::Value>,
128) -> GPU {
129  state.put(EventTargetSetup {
130    brand: v8::Global::new(scope, webidl_brand),
131    set_event_target_data: v8::Global::new(scope, set_event_target_data),
132  });
133  state.put(ErrorEventClass(v8::Global::new(scope, error_event_class)));
134  GPU
135}
136
137struct EventTargetSetup {
138  brand: v8::Global<v8::Value>,
139  set_event_target_data: v8::Global<v8::Value>,
140}
141struct ErrorEventClass(v8::Global<v8::Value>);
142
143pub struct GPU;
144
145// SAFETY: we're sure this can be GCed
146unsafe impl GarbageCollected for GPU {
147  fn trace(&self, _visitor: &mut v8::cppgc::Visitor) {}
148
149  fn get_name(&self) -> &'static std::ffi::CStr {
150    c"GPU"
151  }
152}
153
154#[op2]
155impl GPU {
156  #[constructor]
157  #[cppgc]
158  fn constructor(_: bool) -> Result<GPU, GPUGenericError> {
159    Err(GPUGenericError::InvalidConstructor)
160  }
161
162  #[cppgc]
163  async fn request_adapter(
164    &self,
165    state: Rc<RefCell<OpState>>,
166    #[webidl] options: adapter::GPURequestAdapterOptions,
167  ) -> Option<adapter::GPUAdapter> {
168    let mut state = state.borrow_mut();
169
170    let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else(
171      |_| wgpu_types::Backends::all(),
172      |s| wgpu_types::Backends::from_comma_list(&s),
173    );
174    let instance = if let Some(instance) = state.try_borrow::<Instance>() {
175      instance
176    } else {
177      state.put(Arc::new(wgpu_core::global::Global::new(
178        "webgpu",
179        &wgpu_types::InstanceDescriptor {
180          backends,
181          flags: wgpu_types::InstanceFlags::from_build_config(),
182          memory_budget_thresholds: wgpu_types::MemoryBudgetThresholds {
183            for_resource_creation: Some(97),
184            for_device_loss: Some(99),
185          },
186          backend_options: wgpu_types::BackendOptions {
187            dx12: wgpu_types::Dx12BackendOptions {
188              shader_compiler: wgpu_types::Dx12Compiler::Fxc,
189              ..Default::default()
190            },
191            gl: wgpu_types::GlBackendOptions::default(),
192            noop: wgpu_types::NoopBackendOptions::default(),
193          },
194        },
195        None,
196      )));
197      state.borrow::<Instance>()
198    };
199
200    let descriptor = wgpu_core::instance::RequestAdapterOptions {
201      power_preference: options
202        .power_preference
203        .map(|pp| match pp {
204          adapter::GPUPowerPreference::LowPower => PowerPreference::LowPower,
205          adapter::GPUPowerPreference::HighPerformance => {
206            PowerPreference::HighPerformance
207          }
208        })
209        .unwrap_or_default(),
210      force_fallback_adapter: options.force_fallback_adapter,
211      compatible_surface: None, // windowless
212    };
213    let id = instance.request_adapter(&descriptor, backends, None).ok()?;
214
215    Some(adapter::GPUAdapter {
216      instance: instance.clone(),
217      features: SameObject::new(),
218      limits: SameObject::new(),
219      info: Rc::new(SameObject::new()),
220      id,
221    })
222  }
223
224  #[string]
225  fn getPreferredCanvasFormat(&self) -> &'static str {
226    // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47
227    if cfg!(target_os = "android") {
228      texture::GPUTextureFormat::Rgba8unorm.as_str()
229    } else {
230      texture::GPUTextureFormat::Bgra8unorm.as_str()
231    }
232  }
233}
234
235fn transform_label<'a>(label: String) -> Option<std::borrow::Cow<'a, str>> {
236  if label.is_empty() {
237    None
238  } else {
239    Some(std::borrow::Cow::Owned(label))
240  }
241}