deno_webgpu/
lib.rs

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