1#![allow(clippy::type_complexity)]
2
3mod defined_non_null_js_value;
4mod ext_bindings;
5#[allow(clippy::allow_attributes)]
6pub(crate) mod webgpu_sys;
7
8use alloc::{
9 boxed::Box,
10 format,
11 rc::Rc,
12 string::{String, ToString as _},
13 sync::Arc,
14 vec,
15 vec::Vec,
16};
17use core::{
18 cell::{Cell, OnceCell, RefCell},
19 fmt,
20 future::Future,
21 ops::Range,
22 pin::Pin,
23 sync::atomic::{AtomicU8, Ordering},
24 task::{self, Poll},
25};
26use wgt::Backends;
27
28use js_sys::Promise;
29use wasm_bindgen::{prelude::*, JsCast};
30
31use crate::{
32 dispatch::{self, BlasCompactCallback},
33 Blas, SurfaceTargetUnsafe, Tlas, WriteOnly,
34};
35
36use defined_non_null_js_value::DefinedNonNullJsValue;
37
38macro_rules! impl_send_sync {
46 ($name:ty) => {
47 #[cfg(send_sync)]
48 unsafe impl Send for $name {}
49 #[cfg(send_sync)]
50 unsafe impl Sync for $name {}
51 };
52}
53
54#[derive(Clone)]
55pub struct ContextWebGpu {
56 gpu: Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>,
58 ident: crate::cmp::Identifier,
60 requested_backends: Backends,
64}
65
66impl fmt::Debug for ContextWebGpu {
67 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68 f.debug_struct("ContextWebGpu")
69 .field("type", &"Web")
70 .finish()
71 }
72}
73
74impl crate::Error {
75 fn from_js(js_error: js_sys::Object) -> Self {
76 let source = Box::<dyn core::error::Error + Send + Sync>::from("<WebGPU Error>");
77 if let Some(js_error) = js_error.dyn_ref::<webgpu_sys::GpuValidationError>() {
78 crate::Error::Validation {
79 source,
80 description: js_error.message(),
81 }
82 } else if js_error.has_type::<webgpu_sys::GpuOutOfMemoryError>() {
83 crate::Error::OutOfMemory { source }
84 } else {
85 panic!("Unexpected error");
86 }
87 }
88}
89
90pub type DropCallback = Box<dyn FnOnce() + 'static>;
96
97pub(crate) struct DropGuard {
98 callback: Option<DropCallback>,
99}
100
101impl DropGuard {
102 fn new(callback: Option<DropCallback>) -> Self {
103 Self { callback }
104 }
105}
106
107impl Drop for DropGuard {
108 fn drop(&mut self) {
109 if let Some(cb) = self.callback.take() {
110 cb();
111 }
112 }
113}
114
115impl fmt::Debug for DropGuard {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 f.debug_struct("DropGuard").finish()
118 }
119}
120
121#[derive(Debug, Clone)]
122pub struct WebShaderModule {
123 module: webgpu_sys::GpuShaderModule,
124 compilation_info: WebShaderCompilationInfo,
125 ident: crate::cmp::Identifier,
127}
128
129#[derive(Debug, Clone)]
130enum WebShaderCompilationInfo {
131 Wgsl { source: String },
134 Transformed {
137 compilation_info: crate::CompilationInfo,
138 },
139}
140
141fn map_utf16_to_utf8_offset(utf16_offset: u32, text: &str) -> u32 {
142 let mut utf16_i = 0;
143 for (utf8_index, c) in text.char_indices() {
144 if utf16_i >= utf16_offset {
145 return utf8_index as u32;
146 }
147 utf16_i += c.len_utf16() as u32;
148 }
149 if utf16_i >= utf16_offset {
150 text.len() as u32
151 } else {
152 log::error!("UTF16 offset {utf16_offset} is out of bounds for string {text}");
153 u32::MAX
154 }
155}
156
157impl crate::CompilationMessage {
158 fn from_js(
159 js_message: webgpu_sys::GpuCompilationMessage,
160 compilation_info: &WebShaderCompilationInfo,
161 ) -> Self {
162 let message_type = match js_message.type_() {
163 webgpu_sys::GpuCompilationMessageType::Error => crate::CompilationMessageType::Error,
164 webgpu_sys::GpuCompilationMessageType::Warning => {
165 crate::CompilationMessageType::Warning
166 }
167 webgpu_sys::GpuCompilationMessageType::Info => crate::CompilationMessageType::Info,
168 _ => crate::CompilationMessageType::Error,
169 };
170 let utf16_offset = js_message.offset() as u32;
171 let utf16_length = js_message.length() as u32;
172 let span = match compilation_info {
173 WebShaderCompilationInfo::Wgsl { .. } if utf16_offset == 0 && utf16_length == 0 => None,
174 WebShaderCompilationInfo::Wgsl { source } => {
175 let offset = map_utf16_to_utf8_offset(utf16_offset, source);
176 let length = map_utf16_to_utf8_offset(utf16_length, &source[offset as usize..]);
177 let line_number = js_message.line_num() as u32; let prefix = &source[..offset as usize];
180 let line_start = prefix.rfind('\n').map(|pos| pos + 1).unwrap_or(0) as u32;
181 let line_position = offset - line_start + 1; Some(crate::SourceLocation {
184 offset,
185 length,
186 line_number,
187 line_position,
188 })
189 }
190 WebShaderCompilationInfo::Transformed { .. } => None,
191 };
192
193 crate::CompilationMessage {
194 message: js_message.message(),
195 message_type,
196 location: span,
197 }
198 }
199}
200
201pub(crate) struct MakeSendFuture<F, M> {
207 future: F,
208 map: M,
209}
210
211impl<F: Future, M: Fn(F::Output) -> T, T> Future for MakeSendFuture<F, M> {
212 type Output = T;
213
214 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
215 unsafe {
218 let this = self.get_unchecked_mut();
219 match Pin::new_unchecked(&mut this.future).poll(cx) {
220 task::Poll::Ready(value) => task::Poll::Ready((this.map)(value)),
221 task::Poll::Pending => task::Poll::Pending,
222 }
223 }
224 }
225}
226
227impl<F, M> MakeSendFuture<F, M> {
228 fn new(future: F, map: M) -> Self {
229 Self { future, map }
230 }
231}
232
233#[cfg(send_sync)]
234unsafe impl<F, M> Send for MakeSendFuture<F, M> {}
235
236fn map_texture_format(texture_format: wgt::TextureFormat) -> webgpu_sys::GpuTextureFormat {
237 use webgpu_sys::GpuTextureFormat as tf;
238 use wgt::TextureFormat;
239 match texture_format {
240 TextureFormat::R8Unorm => tf::R8unorm,
242 TextureFormat::R8Snorm => tf::R8snorm,
243 TextureFormat::R8Uint => tf::R8uint,
244 TextureFormat::R8Sint => tf::R8sint,
245 TextureFormat::R16Uint => tf::R16uint,
247 TextureFormat::R16Sint => tf::R16sint,
248 TextureFormat::R16Float => tf::R16float,
249 TextureFormat::Rg8Unorm => tf::Rg8unorm,
250 TextureFormat::Rg8Snorm => tf::Rg8snorm,
251 TextureFormat::Rg8Uint => tf::Rg8uint,
252 TextureFormat::Rg8Sint => tf::Rg8sint,
253 TextureFormat::R32Uint => tf::R32uint,
255 TextureFormat::R32Sint => tf::R32sint,
256 TextureFormat::R32Float => tf::R32float,
257 TextureFormat::Rg16Uint => tf::Rg16uint,
258 TextureFormat::Rg16Sint => tf::Rg16sint,
259 TextureFormat::Rg16Float => tf::Rg16float,
260 TextureFormat::Rgba8Unorm => tf::Rgba8unorm,
261 TextureFormat::Rgba8UnormSrgb => tf::Rgba8unormSrgb,
262 TextureFormat::Rgba8Snorm => tf::Rgba8snorm,
263 TextureFormat::Rgba8Uint => tf::Rgba8uint,
264 TextureFormat::Rgba8Sint => tf::Rgba8sint,
265 TextureFormat::Bgra8Unorm => tf::Bgra8unorm,
266 TextureFormat::Bgra8UnormSrgb => tf::Bgra8unormSrgb,
267 TextureFormat::Rgb9e5Ufloat => tf::Rgb9e5ufloat,
269 TextureFormat::Rgb10a2Uint => tf::Rgb10a2uint,
270 TextureFormat::Rgb10a2Unorm => tf::Rgb10a2unorm,
271 TextureFormat::Rg11b10Ufloat => tf::Rg11b10ufloat,
272 TextureFormat::Rg32Uint => tf::Rg32uint,
274 TextureFormat::Rg32Sint => tf::Rg32sint,
275 TextureFormat::Rg32Float => tf::Rg32float,
276 TextureFormat::Rgba16Uint => tf::Rgba16uint,
277 TextureFormat::Rgba16Sint => tf::Rgba16sint,
278 TextureFormat::Rgba16Float => tf::Rgba16float,
279 TextureFormat::Rgba32Uint => tf::Rgba32uint,
281 TextureFormat::Rgba32Sint => tf::Rgba32sint,
282 TextureFormat::Rgba32Float => tf::Rgba32float,
283 TextureFormat::Stencil8 => tf::Stencil8,
285 TextureFormat::Depth16Unorm => tf::Depth16unorm,
286 TextureFormat::Depth24Plus => tf::Depth24plus,
287 TextureFormat::Depth24PlusStencil8 => tf::Depth24plusStencil8,
288 TextureFormat::Depth32Float => tf::Depth32float,
289 TextureFormat::Depth32FloatStencil8 => tf::Depth32floatStencil8,
291
292 TextureFormat::Bc1RgbaUnorm => tf::Bc1RgbaUnorm,
293 TextureFormat::Bc1RgbaUnormSrgb => tf::Bc1RgbaUnormSrgb,
294 TextureFormat::Bc2RgbaUnorm => tf::Bc2RgbaUnorm,
295 TextureFormat::Bc2RgbaUnormSrgb => tf::Bc2RgbaUnormSrgb,
296 TextureFormat::Bc3RgbaUnorm => tf::Bc3RgbaUnorm,
297 TextureFormat::Bc3RgbaUnormSrgb => tf::Bc3RgbaUnormSrgb,
298 TextureFormat::Bc4RUnorm => tf::Bc4RUnorm,
299 TextureFormat::Bc4RSnorm => tf::Bc4RSnorm,
300 TextureFormat::Bc5RgUnorm => tf::Bc5RgUnorm,
301 TextureFormat::Bc5RgSnorm => tf::Bc5RgSnorm,
302 TextureFormat::Bc6hRgbUfloat => tf::Bc6hRgbUfloat,
303 TextureFormat::Bc6hRgbFloat => tf::Bc6hRgbFloat,
304 TextureFormat::Bc7RgbaUnorm => tf::Bc7RgbaUnorm,
305 TextureFormat::Bc7RgbaUnormSrgb => tf::Bc7RgbaUnormSrgb,
306 TextureFormat::Etc2Rgb8Unorm => tf::Etc2Rgb8unorm,
307 TextureFormat::Etc2Rgb8UnormSrgb => tf::Etc2Rgb8unormSrgb,
308 TextureFormat::Etc2Rgb8A1Unorm => tf::Etc2Rgb8a1unorm,
309 TextureFormat::Etc2Rgb8A1UnormSrgb => tf::Etc2Rgb8a1unormSrgb,
310 TextureFormat::Etc2Rgba8Unorm => tf::Etc2Rgba8unorm,
311 TextureFormat::Etc2Rgba8UnormSrgb => tf::Etc2Rgba8unormSrgb,
312 TextureFormat::EacR11Unorm => tf::EacR11unorm,
313 TextureFormat::EacR11Snorm => tf::EacR11snorm,
314 TextureFormat::EacRg11Unorm => tf::EacRg11unorm,
315 TextureFormat::EacRg11Snorm => tf::EacRg11snorm,
316 TextureFormat::Astc { block, channel } => match channel {
317 wgt::AstcChannel::Unorm => match block {
318 wgt::AstcBlock::B4x4 => tf::Astc4x4Unorm,
319 wgt::AstcBlock::B5x4 => tf::Astc5x4Unorm,
320 wgt::AstcBlock::B5x5 => tf::Astc5x5Unorm,
321 wgt::AstcBlock::B6x5 => tf::Astc6x5Unorm,
322 wgt::AstcBlock::B6x6 => tf::Astc6x6Unorm,
323 wgt::AstcBlock::B8x5 => tf::Astc8x5Unorm,
324 wgt::AstcBlock::B8x6 => tf::Astc8x6Unorm,
325 wgt::AstcBlock::B8x8 => tf::Astc8x8Unorm,
326 wgt::AstcBlock::B10x5 => tf::Astc10x5Unorm,
327 wgt::AstcBlock::B10x6 => tf::Astc10x6Unorm,
328 wgt::AstcBlock::B10x8 => tf::Astc10x8Unorm,
329 wgt::AstcBlock::B10x10 => tf::Astc10x10Unorm,
330 wgt::AstcBlock::B12x10 => tf::Astc12x10Unorm,
331 wgt::AstcBlock::B12x12 => tf::Astc12x12Unorm,
332 },
333 wgt::AstcChannel::UnormSrgb => match block {
334 wgt::AstcBlock::B4x4 => tf::Astc4x4UnormSrgb,
335 wgt::AstcBlock::B5x4 => tf::Astc5x4UnormSrgb,
336 wgt::AstcBlock::B5x5 => tf::Astc5x5UnormSrgb,
337 wgt::AstcBlock::B6x5 => tf::Astc6x5UnormSrgb,
338 wgt::AstcBlock::B6x6 => tf::Astc6x6UnormSrgb,
339 wgt::AstcBlock::B8x5 => tf::Astc8x5UnormSrgb,
340 wgt::AstcBlock::B8x6 => tf::Astc8x6UnormSrgb,
341 wgt::AstcBlock::B8x8 => tf::Astc8x8UnormSrgb,
342 wgt::AstcBlock::B10x5 => tf::Astc10x5UnormSrgb,
343 wgt::AstcBlock::B10x6 => tf::Astc10x6UnormSrgb,
344 wgt::AstcBlock::B10x8 => tf::Astc10x8UnormSrgb,
345 wgt::AstcBlock::B10x10 => tf::Astc10x10UnormSrgb,
346 wgt::AstcBlock::B12x10 => tf::Astc12x10UnormSrgb,
347 wgt::AstcBlock::B12x12 => tf::Astc12x12UnormSrgb,
348 },
349 wgt::AstcChannel::Hdr => {
350 unimplemented!("Format {texture_format:?} has no WebGPU equivalent")
351 }
352 },
353 _ => unimplemented!("Format {texture_format:?} has no WebGPU equivalent"),
354 }
355}
356
357fn map_texture_component_type(
358 sample_type: wgt::TextureSampleType,
359) -> webgpu_sys::GpuTextureSampleType {
360 use webgpu_sys::GpuTextureSampleType as ts;
361 use wgt::TextureSampleType;
362 match sample_type {
363 TextureSampleType::Float { filterable: true } => ts::Float,
364 TextureSampleType::Float { filterable: false } => ts::UnfilterableFloat,
365 TextureSampleType::Sint => ts::Sint,
366 TextureSampleType::Uint => ts::Uint,
367 TextureSampleType::Depth => ts::Depth,
368 }
369}
370
371fn map_cull_mode(cull_mode: Option<wgt::Face>) -> webgpu_sys::GpuCullMode {
372 use webgpu_sys::GpuCullMode as cm;
373 use wgt::Face;
374 match cull_mode {
375 None => cm::None,
376 Some(Face::Front) => cm::Front,
377 Some(Face::Back) => cm::Back,
378 }
379}
380
381fn map_front_face(front_face: wgt::FrontFace) -> webgpu_sys::GpuFrontFace {
382 use webgpu_sys::GpuFrontFace as ff;
383 use wgt::FrontFace;
384 match front_face {
385 FrontFace::Ccw => ff::Ccw,
386 FrontFace::Cw => ff::Cw,
387 }
388}
389
390fn map_primitive_state(primitive: &wgt::PrimitiveState) -> webgpu_sys::GpuPrimitiveState {
391 use webgpu_sys::GpuPrimitiveTopology as pt;
392 use wgt::PrimitiveTopology;
393
394 let mapped = webgpu_sys::GpuPrimitiveState::new();
395 mapped.set_cull_mode(map_cull_mode(primitive.cull_mode));
396 mapped.set_front_face(map_front_face(primitive.front_face));
397
398 if let Some(format) = primitive.strip_index_format {
399 mapped.set_strip_index_format(map_index_format(format));
400 }
401
402 mapped.set_topology(match primitive.topology {
403 PrimitiveTopology::PointList => pt::PointList,
404 PrimitiveTopology::LineList => pt::LineList,
405 PrimitiveTopology::LineStrip => pt::LineStrip,
406 PrimitiveTopology::TriangleList => pt::TriangleList,
407 PrimitiveTopology::TriangleStrip => pt::TriangleStrip,
408 });
409
410 mapped.set_unclipped_depth(primitive.unclipped_depth);
411
412 match primitive.polygon_mode {
413 wgt::PolygonMode::Fill => {}
414 wgt::PolygonMode::Line => panic!(
415 "{:?} is not enabled for this backend",
416 wgt::Features::POLYGON_MODE_LINE
417 ),
418 wgt::PolygonMode::Point => panic!(
419 "{:?} is not enabled for this backend",
420 wgt::Features::POLYGON_MODE_POINT
421 ),
422 }
423
424 mapped
425}
426
427fn map_compare_function(compare_fn: wgt::CompareFunction) -> webgpu_sys::GpuCompareFunction {
428 use webgpu_sys::GpuCompareFunction as cf;
429 use wgt::CompareFunction;
430 match compare_fn {
431 CompareFunction::Never => cf::Never,
432 CompareFunction::Less => cf::Less,
433 CompareFunction::Equal => cf::Equal,
434 CompareFunction::LessEqual => cf::LessEqual,
435 CompareFunction::Greater => cf::Greater,
436 CompareFunction::NotEqual => cf::NotEqual,
437 CompareFunction::GreaterEqual => cf::GreaterEqual,
438 CompareFunction::Always => cf::Always,
439 }
440}
441
442fn map_stencil_operation(op: wgt::StencilOperation) -> webgpu_sys::GpuStencilOperation {
443 use webgpu_sys::GpuStencilOperation as so;
444 use wgt::StencilOperation;
445 match op {
446 StencilOperation::Keep => so::Keep,
447 StencilOperation::Zero => so::Zero,
448 StencilOperation::Replace => so::Replace,
449 StencilOperation::Invert => so::Invert,
450 StencilOperation::IncrementClamp => so::IncrementClamp,
451 StencilOperation::DecrementClamp => so::DecrementClamp,
452 StencilOperation::IncrementWrap => so::IncrementWrap,
453 StencilOperation::DecrementWrap => so::DecrementWrap,
454 }
455}
456
457fn map_stencil_state_face(desc: &wgt::StencilFaceState) -> webgpu_sys::GpuStencilFaceState {
458 let mapped = webgpu_sys::GpuStencilFaceState::new();
459 mapped.set_compare(map_compare_function(desc.compare));
460 mapped.set_depth_fail_op(map_stencil_operation(desc.depth_fail_op));
461 mapped.set_fail_op(map_stencil_operation(desc.fail_op));
462 mapped.set_pass_op(map_stencil_operation(desc.pass_op));
463 mapped
464}
465
466fn map_depth_stencil_state(desc: &wgt::DepthStencilState) -> webgpu_sys::GpuDepthStencilState {
467 let mapped = webgpu_sys::GpuDepthStencilState::new(map_texture_format(desc.format));
468 if let Some(compare) = desc.depth_compare {
469 mapped.set_depth_compare(map_compare_function(compare));
470 }
471 if let Some(write_enabled) = desc.depth_write_enabled {
472 mapped.set_depth_write_enabled(write_enabled);
473 }
474 mapped.set_depth_bias(desc.bias.constant);
475 mapped.set_depth_bias_clamp(desc.bias.clamp);
476 mapped.set_depth_bias_slope_scale(desc.bias.slope_scale);
477 mapped.set_stencil_back(&map_stencil_state_face(&desc.stencil.back));
478 mapped.set_stencil_front(&map_stencil_state_face(&desc.stencil.front));
479 mapped.set_stencil_read_mask(desc.stencil.read_mask);
480 mapped.set_stencil_write_mask(desc.stencil.write_mask);
481 mapped
482}
483
484fn map_blend_component(desc: &wgt::BlendComponent) -> webgpu_sys::GpuBlendComponent {
485 let mapped = webgpu_sys::GpuBlendComponent::new();
486 mapped.set_dst_factor(map_blend_factor(desc.dst_factor));
487 mapped.set_operation(map_blend_operation(desc.operation));
488 mapped.set_src_factor(map_blend_factor(desc.src_factor));
489 mapped
490}
491
492fn map_blend_factor(factor: wgt::BlendFactor) -> webgpu_sys::GpuBlendFactor {
493 use webgpu_sys::GpuBlendFactor as bf;
494 use wgt::BlendFactor;
495 match factor {
496 BlendFactor::Zero => bf::Zero,
497 BlendFactor::One => bf::One,
498 BlendFactor::Src => bf::Src,
499 BlendFactor::OneMinusSrc => bf::OneMinusSrc,
500 BlendFactor::SrcAlpha => bf::SrcAlpha,
501 BlendFactor::OneMinusSrcAlpha => bf::OneMinusSrcAlpha,
502 BlendFactor::Dst => bf::Dst,
503 BlendFactor::OneMinusDst => bf::OneMinusDst,
504 BlendFactor::DstAlpha => bf::DstAlpha,
505 BlendFactor::OneMinusDstAlpha => bf::OneMinusDstAlpha,
506 BlendFactor::SrcAlphaSaturated => bf::SrcAlphaSaturated,
507 BlendFactor::Constant => bf::Constant,
508 BlendFactor::OneMinusConstant => bf::OneMinusConstant,
509 BlendFactor::Src1 => bf::Src1,
510 BlendFactor::OneMinusSrc1 => bf::OneMinusSrc1,
511 BlendFactor::Src1Alpha => bf::Src1Alpha,
512 BlendFactor::OneMinusSrc1Alpha => bf::OneMinusSrc1Alpha,
513 }
514}
515
516fn map_blend_operation(op: wgt::BlendOperation) -> webgpu_sys::GpuBlendOperation {
517 use webgpu_sys::GpuBlendOperation as bo;
518 use wgt::BlendOperation;
519 match op {
520 BlendOperation::Add => bo::Add,
521 BlendOperation::Subtract => bo::Subtract,
522 BlendOperation::ReverseSubtract => bo::ReverseSubtract,
523 BlendOperation::Min => bo::Min,
524 BlendOperation::Max => bo::Max,
525 }
526}
527
528fn map_index_format(format: wgt::IndexFormat) -> webgpu_sys::GpuIndexFormat {
529 use webgpu_sys::GpuIndexFormat as f;
530 use wgt::IndexFormat;
531 match format {
532 IndexFormat::Uint16 => f::Uint16,
533 IndexFormat::Uint32 => f::Uint32,
534 }
535}
536
537fn map_vertex_format(format: wgt::VertexFormat) -> webgpu_sys::GpuVertexFormat {
538 use webgpu_sys::GpuVertexFormat as vf;
539 use wgt::VertexFormat;
540 match format {
541 VertexFormat::Uint8 => vf::Uint8,
542 VertexFormat::Uint8x2 => vf::Uint8x2,
543 VertexFormat::Uint8x4 => vf::Uint8x4,
544 VertexFormat::Sint8 => vf::Sint8,
545 VertexFormat::Sint8x2 => vf::Sint8x2,
546 VertexFormat::Sint8x4 => vf::Sint8x4,
547 VertexFormat::Unorm8 => vf::Unorm8,
548 VertexFormat::Unorm8x2 => vf::Unorm8x2,
549 VertexFormat::Unorm8x4 => vf::Unorm8x4,
550 VertexFormat::Snorm8 => vf::Snorm8,
551 VertexFormat::Snorm8x2 => vf::Snorm8x2,
552 VertexFormat::Snorm8x4 => vf::Snorm8x4,
553 VertexFormat::Uint16 => vf::Uint16,
554 VertexFormat::Uint16x2 => vf::Uint16x2,
555 VertexFormat::Uint16x4 => vf::Uint16x4,
556 VertexFormat::Sint16 => vf::Sint16,
557 VertexFormat::Sint16x2 => vf::Sint16x2,
558 VertexFormat::Sint16x4 => vf::Sint16x4,
559 VertexFormat::Unorm16 => vf::Unorm16,
560 VertexFormat::Unorm16x2 => vf::Unorm16x2,
561 VertexFormat::Unorm16x4 => vf::Unorm16x4,
562 VertexFormat::Snorm16 => vf::Snorm16,
563 VertexFormat::Snorm16x2 => vf::Snorm16x2,
564 VertexFormat::Snorm16x4 => vf::Snorm16x4,
565 VertexFormat::Float16 => vf::Float16,
566 VertexFormat::Float16x2 => vf::Float16x2,
567 VertexFormat::Float16x4 => vf::Float16x4,
568 VertexFormat::Float32 => vf::Float32,
569 VertexFormat::Float32x2 => vf::Float32x2,
570 VertexFormat::Float32x3 => vf::Float32x3,
571 VertexFormat::Float32x4 => vf::Float32x4,
572 VertexFormat::Uint32 => vf::Uint32,
573 VertexFormat::Uint32x2 => vf::Uint32x2,
574 VertexFormat::Uint32x3 => vf::Uint32x3,
575 VertexFormat::Uint32x4 => vf::Uint32x4,
576 VertexFormat::Sint32 => vf::Sint32,
577 VertexFormat::Sint32x2 => vf::Sint32x2,
578 VertexFormat::Sint32x3 => vf::Sint32x3,
579 VertexFormat::Sint32x4 => vf::Sint32x4,
580 VertexFormat::Unorm10_10_10_2 => vf::Unorm1010102,
581 VertexFormat::Unorm8x4Bgra => vf::Unorm8x4Bgra,
582 VertexFormat::Float64
583 | VertexFormat::Float64x2
584 | VertexFormat::Float64x3
585 | VertexFormat::Float64x4 => {
586 panic!("VERTEX_ATTRIBUTE_64BIT feature must be enabled to use Double formats")
587 }
588 }
589}
590
591fn map_vertex_step_mode(mode: wgt::VertexStepMode) -> webgpu_sys::GpuVertexStepMode {
592 use webgpu_sys::GpuVertexStepMode as sm;
593 use wgt::VertexStepMode;
594 match mode {
595 VertexStepMode::Vertex => sm::Vertex,
596 VertexStepMode::Instance => sm::Instance,
597 }
598}
599
600fn map_extent_3d(extent: wgt::Extent3d) -> webgpu_sys::GpuExtent3dDict {
601 let mapped = webgpu_sys::GpuExtent3dDict::new(extent.width);
602 mapped.set_height(extent.height);
603 mapped.set_depth_or_array_layers(extent.depth_or_array_layers);
604 mapped
605}
606
607fn map_origin_2d(extent: wgt::Origin2d) -> webgpu_sys::GpuOrigin2dDict {
608 let mapped = webgpu_sys::GpuOrigin2dDict::new();
609 mapped.set_x(extent.x);
610 mapped.set_y(extent.y);
611 mapped
612}
613
614fn map_origin_3d(origin: wgt::Origin3d) -> webgpu_sys::GpuOrigin3dDict {
615 let mapped = webgpu_sys::GpuOrigin3dDict::new();
616 mapped.set_x(origin.x);
617 mapped.set_y(origin.y);
618 mapped.set_z(origin.z);
619 mapped
620}
621
622fn map_texture_dimension(
623 texture_dimension: wgt::TextureDimension,
624) -> webgpu_sys::GpuTextureDimension {
625 match texture_dimension {
626 wgt::TextureDimension::D1 => webgpu_sys::GpuTextureDimension::N1d,
627 wgt::TextureDimension::D2 => webgpu_sys::GpuTextureDimension::N2d,
628 wgt::TextureDimension::D3 => webgpu_sys::GpuTextureDimension::N3d,
629 }
630}
631
632fn map_texture_view_dimension(
633 texture_view_dimension: wgt::TextureViewDimension,
634) -> webgpu_sys::GpuTextureViewDimension {
635 use webgpu_sys::GpuTextureViewDimension as tvd;
636 match texture_view_dimension {
637 wgt::TextureViewDimension::D1 => tvd::N1d,
638 wgt::TextureViewDimension::D2 => tvd::N2d,
639 wgt::TextureViewDimension::D2Array => tvd::N2dArray,
640 wgt::TextureViewDimension::Cube => tvd::Cube,
641 wgt::TextureViewDimension::CubeArray => tvd::CubeArray,
642 wgt::TextureViewDimension::D3 => tvd::N3d,
643 }
644}
645
646fn map_buffer_copy_view(
647 view: crate::TexelCopyBufferInfo<'_>,
648) -> webgpu_sys::GpuTexelCopyBufferInfo {
649 let buffer = view.buffer.inner.as_webgpu();
650 let mapped = webgpu_sys::GpuTexelCopyBufferInfo::new(&buffer.inner);
651 if let Some(bytes_per_row) = view.layout.bytes_per_row {
652 mapped.set_bytes_per_row(bytes_per_row);
653 }
654 if let Some(rows_per_image) = view.layout.rows_per_image {
655 mapped.set_rows_per_image(rows_per_image);
656 }
657 mapped.set_offset_f64(view.layout.offset as f64);
658 mapped
659}
660
661fn map_texture_copy_view(
662 view: crate::TexelCopyTextureInfo<'_>,
663) -> webgpu_sys::GpuTexelCopyTextureInfo {
664 let texture = view.texture.inner.as_webgpu();
665 let mapped = webgpu_sys::GpuTexelCopyTextureInfo::new(&texture.inner);
666 mapped.set_mip_level(view.mip_level);
667 mapped.set_origin_gpu_origin_3d_dict(&map_origin_3d(view.origin));
668 mapped.set_aspect(map_texture_aspect(view.aspect));
669 mapped
670}
671
672fn map_tagged_texture_copy_view(
673 view: crate::CopyExternalImageDestInfo<&crate::api::Texture>,
674) -> webgpu_sys::GpuCopyExternalImageDestInfo {
675 let texture = view.texture.inner.as_webgpu();
676 let mapped = webgpu_sys::GpuCopyExternalImageDestInfo::new(&texture.inner);
677 mapped.set_mip_level(view.mip_level);
678 mapped.set_origin_gpu_origin_3d_dict(&map_origin_3d(view.origin));
679 mapped.set_aspect(map_texture_aspect(view.aspect));
680 mapped.set_premultiplied_alpha(view.premultiplied_alpha);
682 mapped
683}
684
685fn map_external_texture_copy_view(
686 view: &crate::CopyExternalImageSourceInfo,
687) -> webgpu_sys::GpuCopyExternalImageSourceInfo {
688 let mapped = match &view.source {
689 crate::ExternalImageSource::ImageBitmap(bitmap) => {
690 webgpu_sys::GpuCopyExternalImageSourceInfo::new(bitmap)
691 }
692 crate::ExternalImageSource::HTMLImageElement(image) => {
693 webgpu_sys::GpuCopyExternalImageSourceInfo::new_with_html_image_element(image)
694 }
695 crate::ExternalImageSource::HTMLVideoElement(video) => {
696 webgpu_sys::GpuCopyExternalImageSourceInfo::new_with_html_video_element(video)
697 }
698 crate::ExternalImageSource::ImageData(image_data) => {
699 webgpu_sys::GpuCopyExternalImageSourceInfo::new_with_image_data(image_data)
700 }
701 crate::ExternalImageSource::HTMLCanvasElement(canvas) => {
702 webgpu_sys::GpuCopyExternalImageSourceInfo::new_with_html_canvas_element(canvas)
703 }
704 crate::ExternalImageSource::OffscreenCanvas(offscreen_canvas) => {
705 webgpu_sys::GpuCopyExternalImageSourceInfo::new_with_offscreen_canvas(offscreen_canvas)
706 }
707 crate::ExternalImageSource::VideoFrame(video_frame) => {
708 webgpu_sys::GpuCopyExternalImageSourceInfo::new_with_video_frame(video_frame)
709 }
710 };
711 mapped.set_origin_gpu_origin_2d_dict(&map_origin_2d(view.origin));
712 mapped.set_flip_y(view.flip_y);
713 mapped
714}
715
716fn map_texture_aspect(aspect: wgt::TextureAspect) -> webgpu_sys::GpuTextureAspect {
717 match aspect {
718 wgt::TextureAspect::All => webgpu_sys::GpuTextureAspect::All,
719 wgt::TextureAspect::StencilOnly => webgpu_sys::GpuTextureAspect::StencilOnly,
720 wgt::TextureAspect::DepthOnly => webgpu_sys::GpuTextureAspect::DepthOnly,
721 wgt::TextureAspect::Plane0 | wgt::TextureAspect::Plane1 | wgt::TextureAspect::Plane2 => {
722 panic!("multi-plane textures are not supported")
723 }
724 }
725}
726
727fn map_filter_mode(mode: wgt::FilterMode) -> webgpu_sys::GpuFilterMode {
728 match mode {
729 wgt::FilterMode::Nearest => webgpu_sys::GpuFilterMode::Nearest,
730 wgt::FilterMode::Linear => webgpu_sys::GpuFilterMode::Linear,
731 }
732}
733
734fn map_mipmap_filter_mode(mode: wgt::MipmapFilterMode) -> webgpu_sys::GpuMipmapFilterMode {
735 match mode {
736 wgt::MipmapFilterMode::Nearest => webgpu_sys::GpuMipmapFilterMode::Nearest,
737 wgt::MipmapFilterMode::Linear => webgpu_sys::GpuMipmapFilterMode::Linear,
738 }
739}
740
741fn map_address_mode(mode: wgt::AddressMode) -> webgpu_sys::GpuAddressMode {
742 match mode {
743 wgt::AddressMode::ClampToEdge => webgpu_sys::GpuAddressMode::ClampToEdge,
744 wgt::AddressMode::Repeat => webgpu_sys::GpuAddressMode::Repeat,
745 wgt::AddressMode::MirrorRepeat => webgpu_sys::GpuAddressMode::MirrorRepeat,
746 wgt::AddressMode::ClampToBorder => panic!("Clamp to border is not supported"),
747 }
748}
749
750fn map_color(color: wgt::Color) -> webgpu_sys::GpuColorDict {
751 webgpu_sys::GpuColorDict::new(color.a, color.b, color.g, color.r)
752}
753
754fn map_store_op(store: crate::StoreOp) -> webgpu_sys::GpuStoreOp {
755 match store {
756 crate::StoreOp::Store => webgpu_sys::GpuStoreOp::Store,
757 crate::StoreOp::Discard => webgpu_sys::GpuStoreOp::Discard,
758 }
759}
760
761fn map_map_mode(mode: crate::MapMode) -> u32 {
762 match mode {
763 crate::MapMode::Read => webgpu_sys::gpu_map_mode::READ,
764 crate::MapMode::Write => webgpu_sys::gpu_map_mode::WRITE,
765 }
766}
767
768const FEATURES_MAPPING: [(wgt::Features, webgpu_sys::GpuFeatureName); 16] = [
769 (
770 wgt::Features::DEPTH_CLIP_CONTROL,
771 webgpu_sys::GpuFeatureName::DepthClipControl,
772 ),
773 (
774 wgt::Features::DEPTH32FLOAT_STENCIL8,
775 webgpu_sys::GpuFeatureName::Depth32floatStencil8,
776 ),
777 (
778 wgt::Features::TEXTURE_COMPRESSION_BC,
779 webgpu_sys::GpuFeatureName::TextureCompressionBc,
780 ),
781 (
782 wgt::Features::TEXTURE_COMPRESSION_BC_SLICED_3D,
783 webgpu_sys::GpuFeatureName::TextureCompressionBcSliced3d,
784 ),
785 (
786 wgt::Features::TEXTURE_COMPRESSION_ETC2,
787 webgpu_sys::GpuFeatureName::TextureCompressionEtc2,
788 ),
789 (
790 wgt::Features::TEXTURE_COMPRESSION_ASTC,
791 webgpu_sys::GpuFeatureName::TextureCompressionAstc,
792 ),
793 (
794 wgt::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D,
795 webgpu_sys::GpuFeatureName::TextureCompressionAstcSliced3d,
796 ),
797 (
798 wgt::Features::TIMESTAMP_QUERY,
799 webgpu_sys::GpuFeatureName::TimestampQuery,
800 ),
801 (
802 wgt::Features::INDIRECT_FIRST_INSTANCE,
803 webgpu_sys::GpuFeatureName::IndirectFirstInstance,
804 ),
805 (
806 wgt::Features::SHADER_F16,
807 webgpu_sys::GpuFeatureName::ShaderF16,
808 ),
809 (
810 wgt::Features::RG11B10UFLOAT_RENDERABLE,
811 webgpu_sys::GpuFeatureName::Rg11b10ufloatRenderable,
812 ),
813 (
814 wgt::Features::BGRA8UNORM_STORAGE,
815 webgpu_sys::GpuFeatureName::Bgra8unormStorage,
816 ),
817 (
818 wgt::Features::FLOAT32_FILTERABLE,
819 webgpu_sys::GpuFeatureName::Float32Filterable,
820 ),
821 (
822 wgt::Features::FLOAT32_BLENDABLE,
823 webgpu_sys::GpuFeatureName::Float32Blendable,
824 ),
825 (
826 wgt::Features::DUAL_SOURCE_BLENDING,
827 webgpu_sys::GpuFeatureName::DualSourceBlending,
828 ),
829 (
830 wgt::Features::CLIP_DISTANCES,
831 webgpu_sys::GpuFeatureName::ClipDistances,
832 ),
833];
834
835fn map_wgt_features(supported_features: webgpu_sys::GpuSupportedFeatures) -> wgt::Features {
836 let mut features = wgt::Features::empty();
837 for (wgpu_feat, web_feat) in FEATURES_MAPPING {
838 match wasm_bindgen::JsValue::from(web_feat).as_string() {
839 Some(value) if supported_features.has(&value) => features |= wgpu_feat,
840 _ => {}
841 }
842 }
843 features
844}
845
846fn map_wgt_limits(limits: webgpu_sys::GpuSupportedLimits) -> wgt::Limits {
847 wgt::Limits {
848 max_texture_dimension_1d: limits.max_texture_dimension_1d(),
849 max_texture_dimension_2d: limits.max_texture_dimension_2d(),
850 max_texture_dimension_3d: limits.max_texture_dimension_3d(),
851 max_texture_array_layers: limits.max_texture_array_layers(),
852 max_bind_groups: limits.max_bind_groups(),
853 max_bind_groups_plus_vertex_buffers: limits.max_bind_groups_plus_vertex_buffers(),
854 max_bindings_per_bind_group: limits.max_bindings_per_bind_group(),
855 max_dynamic_uniform_buffers_per_pipeline_layout: limits
856 .max_dynamic_uniform_buffers_per_pipeline_layout(),
857 max_dynamic_storage_buffers_per_pipeline_layout: limits
858 .max_dynamic_storage_buffers_per_pipeline_layout(),
859 max_sampled_textures_per_shader_stage: limits.max_sampled_textures_per_shader_stage(),
860 max_samplers_per_shader_stage: limits.max_samplers_per_shader_stage(),
861 max_storage_buffers_per_shader_stage: limits.max_storage_buffers_per_shader_stage(),
862 max_storage_textures_per_shader_stage: limits.max_storage_textures_per_shader_stage(),
863 max_uniform_buffers_per_shader_stage: limits.max_uniform_buffers_per_shader_stage(),
864 max_binding_array_elements_per_shader_stage: 0,
865 max_binding_array_sampler_elements_per_shader_stage: 0,
866 max_binding_array_acceleration_structure_elements_per_shader_stage: 0,
867 max_uniform_buffer_binding_size: limits.max_uniform_buffer_binding_size() as u64,
868 max_storage_buffer_binding_size: limits.max_storage_buffer_binding_size() as u64,
869 max_vertex_buffers: limits.max_vertex_buffers(),
870 max_buffer_size: limits.max_buffer_size() as u64,
871 max_vertex_attributes: limits.max_vertex_attributes(),
872 max_vertex_buffer_array_stride: limits.max_vertex_buffer_array_stride(),
873 max_inter_stage_shader_variables: limits.max_inter_stage_shader_variables(),
874 min_uniform_buffer_offset_alignment: limits.min_uniform_buffer_offset_alignment(),
875 min_storage_buffer_offset_alignment: limits.min_storage_buffer_offset_alignment(),
876 max_color_attachments: limits.max_color_attachments(),
877 max_color_attachment_bytes_per_sample: limits.max_color_attachment_bytes_per_sample(),
878 max_compute_workgroup_storage_size: limits.max_compute_workgroup_storage_size(),
879 max_compute_invocations_per_workgroup: limits.max_compute_invocations_per_workgroup(),
880 max_compute_workgroup_size_x: limits.max_compute_workgroup_size_x(),
881 max_compute_workgroup_size_y: limits.max_compute_workgroup_size_y(),
882 max_compute_workgroup_size_z: limits.max_compute_workgroup_size_z(),
883 max_compute_workgroups_per_dimension: limits.max_compute_workgroups_per_dimension(),
884 max_immediate_size: wgt::Limits::default().max_immediate_size,
885 max_non_sampler_bindings: wgt::Limits::default().max_non_sampler_bindings,
886
887 max_task_workgroup_total_count: wgt::Limits::default().max_task_workgroup_total_count,
888 max_task_workgroups_per_dimension: wgt::Limits::default().max_task_workgroups_per_dimension,
889 max_mesh_workgroup_total_count: wgt::Limits::default().max_mesh_workgroup_total_count,
890 max_mesh_workgroups_per_dimension: wgt::Limits::default().max_mesh_workgroups_per_dimension,
891 max_task_invocations_per_workgroup: wgt::Limits::default()
892 .max_task_invocations_per_workgroup,
893 max_task_invocations_per_dimension: wgt::Limits::default()
894 .max_task_invocations_per_dimension,
895 max_mesh_invocations_per_workgroup: wgt::Limits::default()
896 .max_mesh_invocations_per_workgroup,
897 max_mesh_invocations_per_dimension: wgt::Limits::default()
898 .max_mesh_invocations_per_dimension,
899 max_task_payload_size: wgt::Limits::default().max_task_payload_size,
900 max_mesh_output_vertices: wgt::Limits::default().max_mesh_output_vertices,
901 max_mesh_output_primitives: wgt::Limits::default().max_mesh_output_primitives,
902 max_mesh_output_layers: wgt::Limits::default().max_mesh_output_layers,
903 max_mesh_multiview_view_count: wgt::Limits::default().max_mesh_multiview_view_count,
904
905 max_blas_primitive_count: wgt::Limits::default().max_blas_primitive_count,
906 max_blas_geometry_count: wgt::Limits::default().max_blas_geometry_count,
907 max_tlas_instance_count: wgt::Limits::default().max_tlas_instance_count,
908 max_acceleration_structures_per_shader_stage: wgt::Limits::default()
909 .max_acceleration_structures_per_shader_stage,
910 max_buffers_and_acceleration_structures_per_shader_stage: wgt::Limits::default()
911 .max_buffers_and_acceleration_structures_per_shader_stage,
912
913 max_multiview_view_count: wgt::Limits::default().max_multiview_view_count,
914
915 max_ray_dispatch_count: wgt::Limits::default().max_ray_dispatch_count,
916 max_ray_recursion_depth: wgt::Limits::default().max_ray_recursion_depth,
917 }
918}
919
920fn map_adapter_info(adapter_info: &webgpu_sys::GpuAdapterInfo) -> wgt::AdapterInfo {
921 wgt::AdapterInfo {
923 name: adapter_info.description().to_string(),
924 vendor: 0,
925 device: 0,
926 device_type: if adapter_info.is_fallback_adapter() {
927 wgt::DeviceType::Cpu
928 } else {
929 wgt::DeviceType::Other
930 },
931 device_pci_bus_id: String::new(),
932 driver: String::new(),
933 driver_info: String::new(),
934 backend: wgt::Backend::BrowserWebGpu,
935 subgroup_min_size: adapter_info.subgroup_min_size(),
936 subgroup_max_size: adapter_info.subgroup_max_size(),
937 transient_saves_memory: None,
938 limit_bucket: None,
939 }
940}
941
942fn map_js_sys_limits(limits: &wgt::Limits) -> js_sys::Object<js_sys::Number> {
943 let object = js_sys::Object::<js_sys::Number>::new_typed();
944
945 macro_rules! set_properties {
946 (($from:expr) => ($on:expr) : $(($js_ident:ident, $rs_ident:ident)),* $(,)?) => {
947 $(
948 ::js_sys::Reflect::set(
949 &$on,
950 &::wasm_bindgen::JsValue::from(stringify!($js_ident)),
951 &::wasm_bindgen::JsValue::from($from.$rs_ident as f64)
956 )
957 .expect("Setting Object properties should never fail.");
958 )*
959 }
960 }
961
962 set_properties![
964 (limits) => (object):
965 (maxTextureDimension1D, max_texture_dimension_1d),
966 (maxTextureDimension2D, max_texture_dimension_2d),
967 (maxTextureDimension3D, max_texture_dimension_3d),
968 (maxTextureArrayLayers, max_texture_array_layers),
969 (maxBindGroups, max_bind_groups),
970 (maxBindGroupsPlusVertexBuffers, max_bind_groups_plus_vertex_buffers),
971 (maxBindingsPerBindGroup, max_bindings_per_bind_group),
972 (maxDynamicUniformBuffersPerPipelineLayout, max_dynamic_uniform_buffers_per_pipeline_layout),
973 (maxDynamicStorageBuffersPerPipelineLayout, max_dynamic_storage_buffers_per_pipeline_layout),
974 (maxSampledTexturesPerShaderStage, max_sampled_textures_per_shader_stage),
975 (maxSamplersPerShaderStage, max_samplers_per_shader_stage),
976 (maxStorageBuffersPerShaderStage, max_storage_buffers_per_shader_stage),
977 (maxStorageTexturesPerShaderStage, max_storage_textures_per_shader_stage),
978 (maxUniformBuffersPerShaderStage, max_uniform_buffers_per_shader_stage),
979 (maxUniformBufferBindingSize, max_uniform_buffer_binding_size),
980 (maxStorageBufferBindingSize, max_storage_buffer_binding_size),
981 (minUniformBufferOffsetAlignment, min_uniform_buffer_offset_alignment),
982 (minStorageBufferOffsetAlignment, min_storage_buffer_offset_alignment),
983 (maxVertexBuffers, max_vertex_buffers),
984 (maxBufferSize, max_buffer_size),
985 (maxVertexAttributes, max_vertex_attributes),
986 (maxVertexBufferArrayStride, max_vertex_buffer_array_stride),
987 (maxInterStageShaderVariables, max_inter_stage_shader_variables),
988 (maxColorAttachments, max_color_attachments),
989 (maxColorAttachmentBytesPerSample, max_color_attachment_bytes_per_sample),
990 (maxComputeWorkgroupStorageSize, max_compute_workgroup_storage_size),
991 (maxComputeInvocationsPerWorkgroup, max_compute_invocations_per_workgroup),
992 (maxComputeWorkgroupSizeX, max_compute_workgroup_size_x),
993 (maxComputeWorkgroupSizeY, max_compute_workgroup_size_y),
994 (maxComputeWorkgroupSizeZ, max_compute_workgroup_size_z),
995 (maxComputeWorkgroupsPerDimension, max_compute_workgroups_per_dimension),
996 ];
997
998 object
999}
1000
1001fn future_request_adapter(
1002 result: Result<js_sys::JsNullable<webgpu_sys::GpuAdapter>, wasm_bindgen::JsValue>,
1003 requested_backends: Backends,
1004) -> Result<dispatch::DispatchAdapter, wgt::RequestAdapterError> {
1005 result
1006 .map_err(|_| request_adapter_null_error(requested_backends))
1007 .and_then(|adapter| match adapter.into_option() {
1008 Some(adapter) => Ok(WebAdapter {
1009 inner: adapter,
1010 ident: crate::cmp::Identifier::create(),
1011 }
1012 .into()),
1013 None => Err(request_adapter_null_error(requested_backends)),
1014 })
1015}
1016
1017fn request_adapter_null_error(requested_backends: Backends) -> wgt::RequestAdapterError {
1019 wgt::RequestAdapterError::NotFound {
1020 active_backends: Backends::BROWSER_WEBGPU,
1021 requested_backends,
1022 supported_backends: Backends::BROWSER_WEBGPU,
1025 no_fallback_backends: Backends::empty(),
1026 no_adapter_backends: Backends::BROWSER_WEBGPU,
1027 incompatible_surface_backends: Backends::empty(),
1028 }
1029}
1030
1031fn future_request_device(
1032 result: Result<webgpu_sys::GpuDevice, wasm_bindgen::JsValue>,
1033) -> Result<(dispatch::DispatchDevice, dispatch::DispatchQueue), crate::RequestDeviceError> {
1034 result
1035 .map(|device| {
1036 let queue = device.queue();
1037
1038 rgba16float_probe::probe_rgba16float_canvas_support(&device);
1043
1044 (
1045 WebDevice {
1046 inner: device,
1047 ident: crate::cmp::Identifier::create(),
1048 error_scope_count: Rc::new(Cell::new(0)),
1049 }
1050 .into(),
1051 WebQueue {
1052 inner: queue,
1053 ident: crate::cmp::Identifier::create(),
1054 }
1055 .into(),
1056 )
1057 })
1058 .map_err(|error_value| crate::RequestDeviceError {
1059 inner: crate::RequestDeviceErrorKind::WebGpu(format!("{error_value:?}")),
1061 })
1062}
1063
1064fn future_pop_error_scope(
1065 result: Result<js_sys::JsNullable<webgpu_sys::GpuError>, wasm_bindgen::JsValue>,
1066) -> Option<crate::Error> {
1067 Some(crate::Error::from_js(result.ok()?.into_option()?.into()))
1068}
1069
1070fn future_compilation_info(
1071 result: Result<webgpu_sys::GpuCompilationInfo, wasm_bindgen::JsValue>,
1072 base_compilation_info: &WebShaderCompilationInfo,
1073) -> crate::CompilationInfo {
1074 let base_messages = match base_compilation_info {
1075 WebShaderCompilationInfo::Transformed { compilation_info } => {
1076 compilation_info.messages.iter().cloned()
1077 }
1078 _ => [].iter().cloned(),
1079 };
1080
1081 let messages =
1082 match result {
1083 Ok(info) => base_messages
1084 .chain(info.messages().into_iter().map(|message| {
1085 crate::CompilationMessage::from_js(message, base_compilation_info)
1086 }))
1087 .collect(),
1088 Err(_v) => base_messages
1089 .chain(core::iter::once(crate::CompilationMessage {
1090 message: "Getting compilation info failed".to_string(),
1091 message_type: crate::CompilationMessageType::Error,
1092 location: None,
1093 }))
1094 .collect(),
1095 };
1096
1097 crate::CompilationInfo { messages }
1098}
1099
1100fn register_then_closures<F, T>(
1103 promise: &Promise<js_sys::Undefined>,
1104 callback: F,
1105 success_value: T,
1106 failure_value: T,
1107) where
1108 F: FnOnce(T) + 'static,
1109 T: 'static,
1110{
1111 let rc_callback: Rc<RefCell<Option<(_, _, F)>>> = Rc::new(RefCell::new(None));
1121
1122 let rc_callback_clone1 = rc_callback.clone();
1123 let rc_callback_clone2 = rc_callback.clone();
1124
1125 let closure_success = wasm_bindgen::closure::Closure::once(move |_| {
1126 let (success_closure, rejection_closure, callback) =
1127 rc_callback_clone1.borrow_mut().take().unwrap();
1128 callback(success_value);
1129 drop((success_closure, rejection_closure));
1131 Ok(())
1132 });
1133 let closure_rejected = wasm_bindgen::closure::Closure::once(move |_| {
1134 let (success_closure, rejection_closure, callback) =
1135 rc_callback_clone2.borrow_mut().take().unwrap();
1136 callback(failure_value);
1137 drop((success_closure, rejection_closure));
1139 Ok(())
1140 });
1141
1142 let _ = promise.then_with_reject(&closure_success, &closure_rejected);
1145
1146 *rc_callback.borrow_mut() = Some((closure_success, closure_rejected, callback));
1147}
1148
1149impl ContextWebGpu {
1150 fn create_surface_from_context(
1155 &self,
1156 canvas: Canvas,
1157 context_result: Result<Option<js_sys::Object>, wasm_bindgen::JsValue>,
1158 ) -> Result<dispatch::DispatchSurface, crate::CreateSurfaceError> {
1159 let context: js_sys::Object = match context_result {
1160 Ok(Some(context)) => context,
1161 Ok(None) => {
1162 return Err(crate::CreateSurfaceError {
1169 inner: crate::CreateSurfaceErrorKind::Web(
1170 String::from(
1171 "canvas.getContext() returned null; webgpu not available or canvas already in use"
1172 )
1173 )
1174 });
1175 }
1176 Err(js_error) => {
1177 return Err(crate::CreateSurfaceError {
1180 inner: crate::CreateSurfaceErrorKind::Web(format!(
1181 "canvas.getContext() threw exception {js_error:?}",
1182 )),
1183 });
1184 }
1185 };
1186
1187 let context: webgpu_sys::GpuCanvasContext = context.unchecked_into();
1194
1195 Ok(WebSurface {
1196 gpu: self.gpu.clone(),
1197 context,
1198 canvas,
1199 configure_failed: Cell::new(false),
1200 ident: crate::cmp::Identifier::create(),
1201 }
1202 .into())
1203 }
1204}
1205
1206#[wasm_bindgen]
1211extern "C" {
1212 type Global;
1213
1214 #[wasm_bindgen(method, getter, js_name = Window)]
1215 fn window(this: &Global) -> JsValue;
1216
1217 #[wasm_bindgen(method, getter, js_name = WorkerGlobalScope)]
1218 fn worker(this: &Global) -> JsValue;
1219}
1220
1221#[derive(Debug, Clone)]
1222pub enum Canvas {
1223 Canvas(web_sys::HtmlCanvasElement),
1224 Offscreen(web_sys::OffscreenCanvas),
1225}
1226
1227#[derive(Debug, Clone, Copy)]
1228pub struct BrowserGpuPropertyInaccessible;
1229
1230pub fn get_browser_gpu_property(
1244) -> Result<Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>, BrowserGpuPropertyInaccessible> {
1245 let global: Global = js_sys::global().unchecked_into();
1246
1247 let maybe_undefined_gpu: webgpu_sys::Gpu = if !global.window().is_undefined() {
1248 let navigator = global.unchecked_into::<web_sys::Window>().navigator();
1249 ext_bindings::NavigatorGpu::gpu(&navigator)
1250 } else if !global.worker().is_undefined() {
1251 let navigator = global
1252 .unchecked_into::<web_sys::WorkerGlobalScope>()
1253 .navigator();
1254 ext_bindings::NavigatorGpu::gpu(&navigator)
1255 } else {
1256 return Err(BrowserGpuPropertyInaccessible);
1257 };
1258 Ok(DefinedNonNullJsValue::new(maybe_undefined_gpu))
1259}
1260
1261#[derive(Debug, Clone)]
1262pub struct WebAdapter {
1263 pub(crate) inner: webgpu_sys::GpuAdapter,
1264 ident: crate::cmp::Identifier,
1266}
1267
1268#[derive(Debug, Clone)]
1269pub struct WebDevice {
1270 pub(crate) inner: webgpu_sys::GpuDevice,
1271 ident: crate::cmp::Identifier,
1273 error_scope_count: Rc<Cell<u32>>,
1275}
1276
1277#[derive(Debug, Clone)]
1278pub struct WebQueue {
1279 pub(crate) inner: webgpu_sys::GpuQueue,
1280 ident: crate::cmp::Identifier,
1282}
1283
1284#[derive(Debug, Clone)]
1285pub struct WebBindGroupLayout {
1286 pub(crate) inner: webgpu_sys::GpuBindGroupLayout,
1287 ident: crate::cmp::Identifier,
1289}
1290
1291#[derive(Debug, Clone)]
1292pub struct WebBindGroup {
1293 pub(crate) inner: webgpu_sys::GpuBindGroup,
1294 ident: crate::cmp::Identifier,
1296}
1297
1298#[derive(Debug, Clone)]
1299pub struct WebTextureView {
1300 pub(crate) inner: webgpu_sys::GpuTextureView,
1301 ident: crate::cmp::Identifier,
1303}
1304
1305#[derive(Debug, Clone)]
1306pub struct WebSampler {
1307 pub(crate) inner: webgpu_sys::GpuSampler,
1308 ident: crate::cmp::Identifier,
1310}
1311
1312#[derive(Debug, Clone)]
1315struct WebBufferMapState {
1316 pub mapped_buffer: Option<js_sys::ArrayBuffer>,
1318 pub range: Range<wgt::BufferAddress>,
1320}
1321
1322#[derive(Debug, Clone)]
1327pub struct WebBuffer {
1328 pub(crate) inner: webgpu_sys::GpuBuffer,
1330 mapping: Rc<RefCell<WebBufferMapState>>,
1332 ident: crate::cmp::Identifier,
1334}
1335
1336impl WebBuffer {
1337 fn new(inner: webgpu_sys::GpuBuffer, desc: &crate::BufferDescriptor<'_>) -> Self {
1339 Self {
1340 inner,
1341 mapping: Rc::new(RefCell::new(WebBufferMapState {
1342 mapped_buffer: None,
1343 range: 0..desc.size,
1344 })),
1345 ident: crate::cmp::Identifier::create(),
1346 }
1347 }
1348
1349 fn get_mapped_range(
1351 &self,
1352 sub_range: Range<wgt::BufferAddress>,
1353 ) -> Result<js_sys::Uint8Array, crate::MapRangeError> {
1354 let mut mapping = self.mapping.borrow_mut();
1355 let range = mapping.range.clone();
1356 if mapping.mapped_buffer.is_none() {
1357 let buffer = self
1358 .inner
1359 .get_mapped_range_with_f64_and_f64(
1360 range.start as f64,
1361 (range.end - range.start) as f64,
1362 )
1363 .map_err(|e| crate::MapRangeError(format!("{e:?}")))?;
1364 mapping.mapped_buffer = Some(buffer);
1365 }
1366 let array_buffer = mapping.mapped_buffer.as_ref().unwrap();
1367 Ok(js_sys::Uint8Array::new_with_byte_offset_and_length(
1368 array_buffer,
1369 (sub_range.start - range.start) as u32,
1370 (sub_range.end - sub_range.start) as u32,
1371 ))
1372 }
1373
1374 fn set_mapped_range(&self, range: Range<wgt::BufferAddress>) {
1376 self.mapping.borrow_mut().range = range;
1377 }
1378}
1379
1380#[derive(Debug, Clone)]
1381pub struct WebTexture {
1382 pub(crate) inner: webgpu_sys::GpuTexture,
1383 drop_guard: Option<Rc<DropGuard>>,
1394 ident: crate::cmp::Identifier,
1396}
1397
1398#[derive(Debug, Clone)]
1399pub struct WebExternalTexture {
1400 ident: crate::cmp::Identifier,
1402}
1403
1404#[derive(Debug, Clone)]
1405pub struct WebBlas {
1406 ident: crate::cmp::Identifier,
1408}
1409
1410#[derive(Debug, Clone)]
1411pub struct WebTlas {
1412 ident: crate::cmp::Identifier,
1414}
1415
1416#[derive(Debug, Clone)]
1417pub struct WebQuerySet {
1418 pub(crate) inner: webgpu_sys::GpuQuerySet,
1419 ident: crate::cmp::Identifier,
1421}
1422
1423#[derive(Debug, Clone)]
1424pub struct WebPipelineLayout {
1425 pub(crate) inner: webgpu_sys::GpuPipelineLayout,
1426 ident: crate::cmp::Identifier,
1428}
1429
1430#[derive(Debug, Clone)]
1431pub struct WebRenderPipeline {
1432 pub(crate) inner: webgpu_sys::GpuRenderPipeline,
1433 ident: crate::cmp::Identifier,
1435}
1436
1437#[derive(Debug, Clone)]
1438pub struct WebComputePipeline {
1439 pub(crate) inner: webgpu_sys::GpuComputePipeline,
1440 ident: crate::cmp::Identifier,
1442}
1443
1444#[derive(Debug, Clone)]
1445pub struct WebPipelineCache {
1446 ident: crate::cmp::Identifier,
1448}
1449
1450#[derive(Debug, Clone)]
1451pub struct WebCommandEncoder {
1452 pub(crate) inner: webgpu_sys::GpuCommandEncoder,
1453 ident: crate::cmp::Identifier,
1455}
1456
1457#[derive(Debug)]
1458pub struct WebComputePassEncoder {
1459 pub(crate) inner: webgpu_sys::GpuComputePassEncoder,
1460 ident: crate::cmp::Identifier,
1462}
1463
1464#[derive(Debug)]
1465pub struct WebRenderPassEncoder {
1466 pub(crate) inner: webgpu_sys::GpuRenderPassEncoder,
1467 ident: crate::cmp::Identifier,
1469}
1470
1471#[derive(Debug)]
1472pub struct WebCommandBuffer {
1473 pub(crate) inner: webgpu_sys::GpuCommandBuffer,
1474 ident: crate::cmp::Identifier,
1476}
1477
1478#[derive(Debug, Clone)]
1479pub struct WebRenderBundleEncoder {
1480 pub(crate) inner: webgpu_sys::GpuRenderBundleEncoder,
1481 ident: crate::cmp::Identifier,
1483}
1484
1485#[derive(Debug, Clone)]
1486pub struct WebRenderBundle {
1487 pub(crate) inner: webgpu_sys::GpuRenderBundle,
1488 ident: crate::cmp::Identifier,
1490}
1491
1492#[derive(Debug, Clone)]
1493pub struct WebSurface {
1494 gpu: Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>,
1495 canvas: Canvas,
1496 context: webgpu_sys::GpuCanvasContext,
1497 configure_failed: Cell<bool>,
1506 ident: crate::cmp::Identifier,
1508}
1509
1510#[derive(Debug, Clone)]
1511pub struct WebSurfaceOutputDetail {
1512 ident: crate::cmp::Identifier,
1514}
1515
1516#[derive(Debug)]
1517pub struct WebQueueWriteBuffer {
1518 inner: Box<[u8]>,
1519 ident: crate::cmp::Identifier,
1521}
1522
1523#[derive(Debug)]
1524pub struct WebBufferMappedRange {
1525 actual_mapping: js_sys::Uint8Array,
1526 temporary_mapping: OnceCell<Vec<u8>>,
1529 temporary_mapping_modified: bool,
1531 ident: crate::cmp::Identifier,
1533}
1534
1535impl_send_sync!(ContextWebGpu);
1536impl_send_sync!(WebAdapter);
1537impl_send_sync!(WebDevice);
1538impl_send_sync!(WebQueue);
1539impl_send_sync!(WebShaderModule);
1540impl_send_sync!(WebBindGroupLayout);
1541impl_send_sync!(WebBindGroup);
1542impl_send_sync!(WebTextureView);
1543impl_send_sync!(WebSampler);
1544impl_send_sync!(WebBuffer);
1545impl_send_sync!(WebTexture);
1546impl_send_sync!(WebExternalTexture);
1547impl_send_sync!(WebBlas);
1548impl_send_sync!(WebTlas);
1549impl_send_sync!(WebQuerySet);
1550impl_send_sync!(WebPipelineLayout);
1551impl_send_sync!(WebRenderPipeline);
1552impl_send_sync!(WebComputePipeline);
1553impl_send_sync!(WebPipelineCache);
1554impl_send_sync!(WebCommandEncoder);
1555impl_send_sync!(WebComputePassEncoder);
1556impl_send_sync!(WebRenderPassEncoder);
1557impl_send_sync!(WebCommandBuffer);
1558impl_send_sync!(WebRenderBundleEncoder);
1559impl_send_sync!(WebRenderBundle);
1560impl_send_sync!(WebSurface);
1561impl_send_sync!(WebSurfaceOutputDetail);
1562impl_send_sync!(WebQueueWriteBuffer);
1563impl_send_sync!(WebBufferMappedRange);
1564
1565crate::cmp::impl_eq_ord_hash_proxy!(ContextWebGpu => .ident);
1566crate::cmp::impl_eq_ord_hash_proxy!(WebAdapter => .ident);
1567crate::cmp::impl_eq_ord_hash_proxy!(WebDevice => .ident);
1568crate::cmp::impl_eq_ord_hash_proxy!(WebQueue => .ident);
1569crate::cmp::impl_eq_ord_hash_proxy!(WebShaderModule => .ident);
1570crate::cmp::impl_eq_ord_hash_proxy!(WebBindGroupLayout => .ident);
1571crate::cmp::impl_eq_ord_hash_proxy!(WebBindGroup => .ident);
1572crate::cmp::impl_eq_ord_hash_proxy!(WebTextureView => .ident);
1573crate::cmp::impl_eq_ord_hash_proxy!(WebSampler => .ident);
1574crate::cmp::impl_eq_ord_hash_proxy!(WebBuffer => .ident);
1575crate::cmp::impl_eq_ord_hash_proxy!(WebTexture => .ident);
1576crate::cmp::impl_eq_ord_hash_proxy!(WebExternalTexture => .ident);
1577crate::cmp::impl_eq_ord_hash_proxy!(WebBlas => .ident);
1578crate::cmp::impl_eq_ord_hash_proxy!(WebTlas => .ident);
1579crate::cmp::impl_eq_ord_hash_proxy!(WebQuerySet => .ident);
1580crate::cmp::impl_eq_ord_hash_proxy!(WebPipelineLayout => .ident);
1581crate::cmp::impl_eq_ord_hash_proxy!(WebRenderPipeline => .ident);
1582crate::cmp::impl_eq_ord_hash_proxy!(WebComputePipeline => .ident);
1583crate::cmp::impl_eq_ord_hash_proxy!(WebPipelineCache => .ident);
1584crate::cmp::impl_eq_ord_hash_proxy!(WebCommandEncoder => .ident);
1585crate::cmp::impl_eq_ord_hash_proxy!(WebComputePassEncoder => .ident);
1586crate::cmp::impl_eq_ord_hash_proxy!(WebRenderPassEncoder => .ident);
1587crate::cmp::impl_eq_ord_hash_proxy!(WebCommandBuffer => .ident);
1588crate::cmp::impl_eq_ord_hash_proxy!(WebRenderBundleEncoder => .ident);
1589crate::cmp::impl_eq_ord_hash_proxy!(WebRenderBundle => .ident);
1590crate::cmp::impl_eq_ord_hash_proxy!(WebSurface => .ident);
1591crate::cmp::impl_eq_ord_hash_proxy!(WebSurfaceOutputDetail => .ident);
1592crate::cmp::impl_eq_ord_hash_proxy!(WebQueueWriteBuffer => .ident);
1593crate::cmp::impl_eq_ord_hash_proxy!(WebBufferMappedRange => .ident);
1594
1595impl dispatch::InstanceInterface for ContextWebGpu {
1596 fn new(desc: crate::InstanceDescriptor) -> Self
1597 where
1598 Self: Sized,
1599 {
1600 let Ok(gpu) = get_browser_gpu_property() else {
1601 panic!(
1602 "Accessing the GPU is only supported on the main thread or from a dedicated worker"
1603 );
1604 };
1605
1606 ContextWebGpu {
1607 gpu,
1608 requested_backends: desc.backends,
1609 ident: crate::cmp::Identifier::create(),
1610 }
1611 }
1612
1613 unsafe fn create_surface(
1614 &self,
1615 target: crate::SurfaceTargetUnsafe,
1616 ) -> Result<dispatch::DispatchSurface, crate::CreateSurfaceError> {
1617 match target {
1618 SurfaceTargetUnsafe::RawHandle {
1619 raw_display_handle: _,
1620 raw_window_handle,
1621 } => {
1622 let canvas_element: web_sys::HtmlCanvasElement = match raw_window_handle {
1623 raw_window_handle::RawWindowHandle::Web(handle) => {
1624 let canvas_node: wasm_bindgen::JsValue = web_sys::window()
1625 .and_then(|win| win.document())
1626 .and_then(|doc| {
1627 doc.query_selector_all(&format!(
1628 "[data-raw-handle=\"{}\"]",
1629 handle.id
1630 ))
1631 .ok()
1632 })
1633 .and_then(|nodes| nodes.get(0))
1634 .expect("expected to find single canvas")
1635 .into();
1636 canvas_node.into()
1637 }
1638 raw_window_handle::RawWindowHandle::WebCanvas(handle) => {
1639 let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
1640 value.clone().unchecked_into()
1641 }
1642 raw_window_handle::RawWindowHandle::WebOffscreenCanvas(handle) => {
1643 let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
1644 let canvas: web_sys::OffscreenCanvas = value.clone().unchecked_into();
1645 let context_result = canvas.get_context("webgpu");
1646
1647 return self.create_surface_from_context(
1648 Canvas::Offscreen(canvas),
1649 context_result,
1650 );
1651 }
1652 _ => panic!("expected valid handle for canvas"),
1653 };
1654
1655 let context_result = canvas_element.get_context("webgpu");
1656 self.create_surface_from_context(Canvas::Canvas(canvas_element), context_result)
1657 }
1658 }
1659 }
1660
1661 fn request_adapter(
1662 &self,
1663 options: &crate::RequestAdapterOptions<'_, '_>,
1664 ) -> Pin<Box<dyn dispatch::RequestAdapterFuture>> {
1665 let requested_backends = self.requested_backends;
1666
1667 if !(requested_backends.contains(wgt::Backends::BROWSER_WEBGPU)) {
1671 return Box::pin(core::future::ready(Err(
1672 wgt::RequestAdapterError::NotFound {
1673 active_backends: Backends::BROWSER_WEBGPU,
1674 requested_backends,
1675 supported_backends: Backends::BROWSER_WEBGPU,
1678 no_fallback_backends: Backends::default(),
1679 no_adapter_backends: Backends::default(),
1680 incompatible_surface_backends: Backends::default(),
1681 },
1682 )));
1683 }
1684 let mapped_options = webgpu_sys::GpuRequestAdapterOptions::new();
1685 let mapped_power_preference = match options.power_preference {
1686 wgt::PowerPreference::None => None,
1687 wgt::PowerPreference::LowPower => Some(webgpu_sys::GpuPowerPreference::LowPower),
1688 wgt::PowerPreference::HighPerformance => {
1689 Some(webgpu_sys::GpuPowerPreference::HighPerformance)
1690 }
1691 };
1692 if let Some(mapped_pref) = mapped_power_preference {
1693 mapped_options.set_power_preference(mapped_pref);
1694 }
1695
1696 if let Some(gpu) = &self.gpu {
1697 let adapter_promise = gpu.request_adapter_with_options(&mapped_options);
1698 Box::pin(MakeSendFuture::new(
1699 wasm_bindgen_futures::JsFuture::from(adapter_promise),
1700 move |result| future_request_adapter(result, requested_backends),
1701 ))
1702 } else {
1703 Box::pin(core::future::ready(Err(request_adapter_null_error(
1706 requested_backends,
1707 ))))
1708 }
1709 }
1710 fn enumerate_adapters(
1711 &self,
1712 _backends: crate::Backends,
1713 ) -> Pin<Box<dyn dispatch::EnumerateAdapterFuture>> {
1714 let future = self.request_adapter(&crate::RequestAdapterOptions::default());
1715 let enumerate_future = async move {
1716 let adapter = future.await;
1717 match adapter {
1718 Ok(a) => vec![a],
1719 Err(_) => vec![],
1720 }
1721 };
1722 Box::pin(enumerate_future)
1723 }
1724
1725 fn poll_all_devices(&self, _force_wait: bool) -> bool {
1726 true
1728 }
1729
1730 #[cfg(feature = "wgsl")]
1731 fn wgsl_language_features(&self) -> crate::WgslLanguageFeatures {
1732 let mut wgsl_language_features = crate::WgslLanguageFeatures::empty();
1733 if let Some(gpu) = &self.gpu {
1734 gpu.wgsl_language_features()
1735 .keys()
1736 .into_iter()
1737 .map(|wlf| wlf.expect("`WgslLanguageFeatures` elements should be valid"))
1738 .map(|wlf| {
1739 wlf.as_string()
1740 .expect("`WgslLanguageFeatures` should be string set")
1741 })
1742 .filter_map(|wlf| match wlf.as_str() {
1743 "readonly_and_readwrite_storage_textures" => {
1744 Some(crate::WgslLanguageFeatures::ReadOnlyAndReadWriteStorageTextures)
1745 }
1746 "packed_4x8_integer_dot_product" => {
1747 Some(crate::WgslLanguageFeatures::Packed4x8IntegerDotProduct)
1748 }
1749 "unrestricted_pointer_parameters" => {
1750 Some(crate::WgslLanguageFeatures::UnrestrictedPointerParameters)
1751 }
1752 "pointer_composite_access" => {
1753 Some(crate::WgslLanguageFeatures::PointerCompositeAccess)
1754 }
1755 _ => None,
1756 })
1757 .for_each(|wlf| {
1758 wgsl_language_features |= wlf;
1759 })
1760 }
1761 wgsl_language_features
1762 }
1763}
1764
1765impl Drop for ContextWebGpu {
1766 fn drop(&mut self) {
1767 }
1769}
1770
1771impl dispatch::AdapterInterface for WebAdapter {
1772 fn request_device(
1773 &self,
1774 desc: &crate::DeviceDescriptor<'_>,
1775 ) -> Pin<Box<dyn dispatch::RequestDeviceFuture>> {
1776 if !matches!(desc.trace, wgt::Trace::Off) {
1777 log::warn!("The `trace` parameter is not supported on the WebGPU backend.");
1778 }
1779
1780 let mapped_desc = webgpu_sys::GpuDeviceDescriptor::new();
1781
1782 let required_limits = map_js_sys_limits(&desc.required_limits);
1783 mapped_desc.set_required_limits(&required_limits);
1784
1785 let required_features = FEATURES_MAPPING
1786 .iter()
1787 .copied()
1788 .flat_map(|(flag, value)| {
1789 if desc.required_features.contains(flag) {
1790 Some(
1791 wasm_bindgen::JsValue::from(value)
1792 .dyn_into::<js_sys::JsString>()
1793 .unwrap(),
1794 )
1795 } else {
1796 None
1797 }
1798 })
1799 .collect::<Vec<js_sys::JsString>>();
1800 mapped_desc.set_required_features(&required_features);
1801
1802 if let Some(label) = desc.label {
1803 mapped_desc.set_label(label);
1804 }
1805
1806 let device_promise = self.inner.request_device_with_descriptor(&mapped_desc);
1807
1808 Box::pin(MakeSendFuture::new(
1809 wasm_bindgen_futures::JsFuture::from(device_promise),
1810 future_request_device,
1811 ))
1812 }
1813
1814 fn is_surface_supported(&self, _surface: &dispatch::DispatchSurface) -> bool {
1815 true
1817 }
1818
1819 fn features(&self) -> crate::Features {
1820 map_wgt_features(self.inner.features())
1821 }
1822
1823 fn limits(&self) -> crate::Limits {
1824 map_wgt_limits(self.inner.limits())
1825 }
1826
1827 fn downlevel_capabilities(&self) -> crate::DownlevelCapabilities {
1828 crate::DownlevelCapabilities::default()
1830 }
1831
1832 fn get_info(&self) -> crate::AdapterInfo {
1833 map_adapter_info(&self.inner.info())
1834 }
1835
1836 fn get_texture_format_features(
1837 &self,
1838 format: crate::TextureFormat,
1839 ) -> crate::TextureFormatFeatures {
1840 format.guaranteed_format_features(dispatch::AdapterInterface::features(self))
1841 }
1842
1843 fn get_presentation_timestamp(&self) -> crate::PresentationTimestamp {
1844 crate::PresentationTimestamp::INVALID_TIMESTAMP
1845 }
1846
1847 fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1848 Vec::new()
1849 }
1850}
1851impl Drop for WebAdapter {
1852 fn drop(&mut self) {
1853 }
1855}
1856
1857impl WebDevice {
1858 pub(crate) fn wrap_external_texture(
1872 &self,
1873 texture: webgpu_sys::GpuTexture,
1874 drop_callback: Option<DropCallback>,
1875 ) -> dispatch::DispatchTexture {
1876 WebTexture {
1877 inner: texture,
1878 drop_guard: Some(Rc::new(DropGuard::new(drop_callback))),
1879 ident: crate::cmp::Identifier::create(),
1880 }
1881 .into()
1882 }
1883}
1884
1885impl dispatch::DeviceInterface for WebDevice {
1886 fn features(&self) -> crate::Features {
1887 map_wgt_features(self.inner.features())
1888 }
1889
1890 fn limits(&self) -> crate::Limits {
1891 map_wgt_limits(self.inner.limits())
1892 }
1893
1894 fn adapter_info(&self) -> crate::AdapterInfo {
1895 map_adapter_info(&self.inner.adapter_info())
1896 }
1897
1898 fn create_shader_module(
1899 &self,
1900 desc: crate::ShaderModuleDescriptor<'_>,
1901 _shader_runtime_checks: crate::ShaderRuntimeChecks,
1902 ) -> dispatch::DispatchShaderModule {
1903 let shader_module_result = match desc.source {
1904 #[cfg(feature = "spirv")]
1905 crate::ShaderSource::SpirV(ref spv) => {
1906 use naga::front;
1907
1908 let options = naga::front::spv::Options {
1909 adjust_coordinate_space: false,
1910 strict_capabilities: true,
1911 block_ctx_dump_prefix: None,
1912 };
1913 let spv_parser = front::spv::Frontend::new(spv.iter().cloned(), &options);
1914 spv_parser
1915 .parse()
1916 .map_err(|inner| {
1917 crate::CompilationInfo::from(naga::error::ShaderError {
1918 source: String::new(),
1919 label: desc.label.map(|s| s.to_string()),
1920 inner: Box::new(inner),
1921 })
1922 })
1923 .and_then(|spv_module| {
1924 validate_transformed_shader_module(&spv_module, "", &desc).map(|v| {
1925 (
1926 v,
1927 WebShaderCompilationInfo::Transformed {
1928 compilation_info: crate::CompilationInfo { messages: vec![] },
1929 },
1930 )
1931 })
1932 })
1933 }
1934 #[cfg(feature = "glsl")]
1935 crate::ShaderSource::Glsl {
1936 ref shader,
1937 stage,
1938 defines,
1939 } => {
1940 use naga::front;
1941
1942 let options = front::glsl::Options {
1944 stage,
1945 defines: defines
1946 .iter()
1947 .map(|&(key, value)| (String::from(key), String::from(value)))
1948 .collect(),
1949 };
1950 let mut parser = front::glsl::Frontend::default();
1951 parser
1952 .parse(&options, shader)
1953 .map_err(|inner| {
1954 crate::CompilationInfo::from(naga::error::ShaderError {
1955 source: shader.to_string(),
1956 label: desc.label.map(|s| s.to_string()),
1957 inner: Box::new(inner),
1958 })
1959 })
1960 .and_then(|glsl_module| {
1961 validate_transformed_shader_module(&glsl_module, shader, &desc).map(|v| {
1962 (
1963 v,
1964 WebShaderCompilationInfo::Transformed {
1965 compilation_info: crate::CompilationInfo { messages: vec![] },
1966 },
1967 )
1968 })
1969 })
1970 }
1971 #[cfg(feature = "wgsl")]
1972 crate::ShaderSource::Wgsl(ref code) => {
1973 let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(code);
1974 Ok((
1975 shader_module,
1976 WebShaderCompilationInfo::Wgsl {
1977 source: code.to_string(),
1978 },
1979 ))
1980 }
1981 #[cfg(feature = "naga-ir")]
1982 crate::ShaderSource::Naga(ref module) => {
1983 validate_transformed_shader_module(module, "", &desc).map(|v| {
1984 (
1985 v,
1986 WebShaderCompilationInfo::Transformed {
1987 compilation_info: crate::CompilationInfo { messages: vec![] },
1988 },
1989 )
1990 })
1991 }
1992 crate::ShaderSource::Dummy(_) => {
1993 panic!("found `ShaderSource::Dummy`")
1994 }
1995 };
1996
1997 #[cfg(naga)]
1998 fn validate_transformed_shader_module(
1999 module: &naga::Module,
2000 source: &str,
2001 desc: &crate::ShaderModuleDescriptor<'_>,
2002 ) -> Result<webgpu_sys::GpuShaderModuleDescriptor, crate::CompilationInfo> {
2003 use naga::{back, valid};
2004 let mut validator =
2005 valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all());
2006 let module_info = validator.validate(module).map_err(|err| {
2007 crate::CompilationInfo::from(naga::error::ShaderError {
2008 source: source.to_string(),
2009 label: desc.label.map(|s| s.to_string()),
2010 inner: Box::new(err),
2011 })
2012 })?;
2013
2014 let writer_flags = naga::back::wgsl::WriterFlags::empty();
2015 let wgsl_text = back::wgsl::write_string(module, &module_info, writer_flags).unwrap();
2016 Ok(webgpu_sys::GpuShaderModuleDescriptor::new(
2017 wgsl_text.as_str(),
2018 ))
2019 }
2020 let (descriptor, compilation_info) = match shader_module_result {
2021 Ok(v) => v,
2022 Err(compilation_info) => (
2023 webgpu_sys::GpuShaderModuleDescriptor::new(""),
2024 WebShaderCompilationInfo::Transformed { compilation_info },
2025 ),
2026 };
2027 if let Some(label) = desc.label {
2028 descriptor.set_label(label);
2029 }
2030 WebShaderModule {
2031 module: self.inner.create_shader_module(&descriptor),
2032 compilation_info,
2033 ident: crate::cmp::Identifier::create(),
2034 }
2035 .into()
2036 }
2037
2038 unsafe fn create_shader_module_passthrough(
2039 &self,
2040 desc: &crate::ShaderModuleDescriptorPassthrough<'_>,
2041 ) -> dispatch::DispatchShaderModule {
2042 let shader_module_result = if let Some(ref code) = desc.wgsl {
2043 let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(code);
2044 Ok((
2045 shader_module,
2046 WebShaderCompilationInfo::Wgsl {
2047 source: code.to_string(),
2048 },
2049 ))
2050 } else {
2051 Err(crate::CompilationInfo {
2052 messages: vec![crate::CompilationMessage {
2053 message:
2054 "Passthrough shader not compiled for WGSL on WebGPU backend (wgpu error)"
2055 .to_string(),
2056 location: None,
2057 message_type: crate::CompilationMessageType::Error,
2058 }],
2059 })
2060 };
2061 let (descriptor, compilation_info) = match shader_module_result {
2062 Ok(v) => v,
2063 Err(compilation_info) => (
2064 webgpu_sys::GpuShaderModuleDescriptor::new(""),
2065 WebShaderCompilationInfo::Transformed { compilation_info },
2066 ),
2067 };
2068 if let Some(label) = desc.label {
2069 descriptor.set_label(label);
2070 }
2071 WebShaderModule {
2072 module: self.inner.create_shader_module(&descriptor),
2073 compilation_info,
2074 ident: crate::cmp::Identifier::create(),
2075 }
2076 .into()
2077 }
2078
2079 fn create_bind_group_layout(
2080 &self,
2081 desc: &crate::BindGroupLayoutDescriptor<'_>,
2082 ) -> dispatch::DispatchBindGroupLayout {
2083 let mapped_bindings = desc
2084 .entries
2085 .iter()
2086 .map(|bind| {
2087 let mapped_entry =
2088 webgpu_sys::GpuBindGroupLayoutEntry::new(bind.binding, bind.visibility.bits());
2089
2090 match bind.ty {
2091 wgt::BindingType::Buffer {
2092 ty,
2093 has_dynamic_offset,
2094 min_binding_size,
2095 } => {
2096 let buffer = webgpu_sys::GpuBufferBindingLayout::new();
2097 buffer.set_has_dynamic_offset(has_dynamic_offset);
2098 if let Some(size) = min_binding_size {
2099 buffer.set_min_binding_size_f64(size.get() as f64);
2100 }
2101 buffer.set_type(match ty {
2102 wgt::BufferBindingType::Uniform => {
2103 webgpu_sys::GpuBufferBindingType::Uniform
2104 }
2105 wgt::BufferBindingType::Storage { read_only: false } => {
2106 webgpu_sys::GpuBufferBindingType::Storage
2107 }
2108 wgt::BufferBindingType::Storage { read_only: true } => {
2109 webgpu_sys::GpuBufferBindingType::ReadOnlyStorage
2110 }
2111 });
2112 mapped_entry.set_buffer(&buffer);
2113 }
2114 wgt::BindingType::Sampler(ty) => {
2115 let sampler = webgpu_sys::GpuSamplerBindingLayout::new();
2116 sampler.set_type(match ty {
2117 wgt::SamplerBindingType::NonFiltering => {
2118 webgpu_sys::GpuSamplerBindingType::NonFiltering
2119 }
2120 wgt::SamplerBindingType::Filtering => {
2121 webgpu_sys::GpuSamplerBindingType::Filtering
2122 }
2123 wgt::SamplerBindingType::Comparison => {
2124 webgpu_sys::GpuSamplerBindingType::Comparison
2125 }
2126 });
2127 mapped_entry.set_sampler(&sampler);
2128 }
2129 wgt::BindingType::Texture {
2130 multisampled,
2131 sample_type,
2132 view_dimension,
2133 } => {
2134 let texture = webgpu_sys::GpuTextureBindingLayout::new();
2135 texture.set_multisampled(multisampled);
2136 texture.set_sample_type(map_texture_component_type(sample_type));
2137 texture.set_view_dimension(map_texture_view_dimension(view_dimension));
2138 mapped_entry.set_texture(&texture);
2139 }
2140 wgt::BindingType::StorageTexture {
2141 access,
2142 format,
2143 view_dimension,
2144 } => {
2145 let mapped_access = match access {
2146 wgt::StorageTextureAccess::WriteOnly => {
2147 webgpu_sys::GpuStorageTextureAccess::WriteOnly
2148 }
2149 wgt::StorageTextureAccess::ReadOnly => {
2150 webgpu_sys::GpuStorageTextureAccess::ReadOnly
2151 }
2152 wgt::StorageTextureAccess::ReadWrite => {
2153 webgpu_sys::GpuStorageTextureAccess::ReadWrite
2154 }
2155 wgt::StorageTextureAccess::Atomic => {
2156 unreachable!()
2158 }
2159 };
2160 let storage_texture = webgpu_sys::GpuStorageTextureBindingLayout::new(
2161 map_texture_format(format),
2162 );
2163 storage_texture.set_access(mapped_access);
2164 storage_texture
2165 .set_view_dimension(map_texture_view_dimension(view_dimension));
2166 mapped_entry.set_storage_texture(&storage_texture);
2167 }
2168 wgt::BindingType::AccelerationStructure { .. } => todo!(),
2169 wgt::BindingType::ExternalTexture => {
2170 mapped_entry.set_external_texture(
2171 &webgpu_sys::GpuExternalTextureBindingLayout::new(),
2172 );
2173 }
2174 }
2175
2176 mapped_entry
2177 })
2178 .collect::<Vec<webgpu_sys::GpuBindGroupLayoutEntry>>();
2179
2180 let mapped_desc = webgpu_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings);
2181 if let Some(label) = desc.label {
2182 mapped_desc.set_label(label);
2183 }
2184 let bind_group_layout = self.inner.create_bind_group_layout(&mapped_desc).unwrap();
2185
2186 WebBindGroupLayout {
2187 inner: bind_group_layout,
2188 ident: crate::cmp::Identifier::create(),
2189 }
2190 .into()
2191 }
2192
2193 fn create_bind_group(
2194 &self,
2195 desc: &crate::BindGroupDescriptor<'_>,
2196 ) -> dispatch::DispatchBindGroup {
2197 let mapped_entries = desc
2198 .entries
2199 .iter()
2200 .map(|binding| match binding.resource {
2201 crate::BindingResource::Buffer(crate::BufferBinding {
2202 buffer,
2203 offset,
2204 size,
2205 }) => {
2206 let buffer = buffer.inner.as_webgpu();
2207 let mapped_buffer_binding = webgpu_sys::GpuBufferBinding::new(&buffer.inner);
2208 mapped_buffer_binding.set_offset_f64(offset as f64);
2209 if let Some(s) = size {
2210 mapped_buffer_binding.set_size_f64(s.get() as f64);
2211 }
2212
2213 webgpu_sys::GpuBindGroupEntry::new_with_gpu_buffer_binding(
2214 binding.binding,
2215 &mapped_buffer_binding,
2216 )
2217 }
2218 crate::BindingResource::BufferArray(..) => {
2219 panic!("Web backend does not support arrays of buffers")
2220 }
2221 crate::BindingResource::Sampler(sampler) => {
2222 let sampler = &sampler.inner.as_webgpu().inner;
2223 webgpu_sys::GpuBindGroupEntry::new(binding.binding, sampler)
2224 }
2225 crate::BindingResource::SamplerArray(..) => {
2226 panic!("Web backend does not support arrays of samplers")
2227 }
2228 crate::BindingResource::TextureView(texture_view) => {
2229 let texture_view = &texture_view.inner.as_webgpu().inner;
2230 webgpu_sys::GpuBindGroupEntry::new_with_gpu_texture_view(
2231 binding.binding,
2232 texture_view,
2233 )
2234 }
2235 crate::BindingResource::TextureViewArray(..) => {
2236 panic!("Web backend does not support BINDING_INDEXING extension")
2237 }
2238 crate::BindingResource::AccelerationStructure(_) => {
2239 unimplemented!("Raytracing not implemented for web")
2240 }
2241 crate::BindingResource::AccelerationStructureArray(_) => {
2242 unimplemented!("Raytracing not implemented for web")
2243 }
2244 crate::BindingResource::ExternalTexture(_) => {
2245 unimplemented!("ExternalTexture not implemented for web")
2246 }
2247 })
2248 .collect::<Vec<webgpu_sys::GpuBindGroupEntry>>();
2249
2250 let bgl = &desc.layout.inner.as_webgpu().inner;
2251 let mapped_desc = webgpu_sys::GpuBindGroupDescriptor::new(&mapped_entries, bgl);
2252 if let Some(label) = desc.label {
2253 mapped_desc.set_label(label);
2254 }
2255 let bind_group = self.inner.create_bind_group(&mapped_desc);
2256
2257 WebBindGroup {
2258 inner: bind_group,
2259 ident: crate::cmp::Identifier::create(),
2260 }
2261 .into()
2262 }
2263
2264 fn create_pipeline_layout(
2265 &self,
2266 desc: &crate::PipelineLayoutDescriptor<'_>,
2267 ) -> dispatch::DispatchPipelineLayout {
2268 let temp_layouts = desc
2269 .bind_group_layouts
2270 .iter()
2271 .map(|bgl| match bgl {
2272 Some(bgl) => js_sys::JsNullable::wrap(bgl.inner.as_webgpu().inner.clone()),
2273 None => js_sys::JsNullable::new(),
2274 })
2275 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuBindGroupLayout>>>();
2276 let mapped_desc = webgpu_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts);
2277 if let Some(label) = desc.label {
2278 mapped_desc.set_label(label);
2279 }
2280
2281 let pipeline_layout = self.inner.create_pipeline_layout(&mapped_desc);
2282
2283 WebPipelineLayout {
2284 inner: pipeline_layout,
2285 ident: crate::cmp::Identifier::create(),
2286 }
2287 .into()
2288 }
2289
2290 fn create_render_pipeline(
2291 &self,
2292 desc: &crate::RenderPipelineDescriptor<'_>,
2293 ) -> dispatch::DispatchRenderPipeline {
2294 let module = desc.vertex.module.inner.as_webgpu();
2295 let mapped_vertex_state = webgpu_sys::GpuVertexState::new(&module.module);
2296 insert_constants_map(
2297 &mapped_vertex_state,
2298 desc.vertex.compilation_options.constants,
2299 );
2300 if let Some(ep) = desc.vertex.entry_point {
2301 mapped_vertex_state.set_entry_point(ep);
2302 }
2303
2304 let buffers = desc
2305 .vertex
2306 .buffers
2307 .iter()
2308 .map(|vbuf| match vbuf {
2309 Some(vbuf) => {
2310 let mapped_attributes = vbuf
2311 .attributes
2312 .iter()
2313 .map(|attr| {
2314 webgpu_sys::GpuVertexAttribute::new_with_f64(
2315 map_vertex_format(attr.format),
2316 attr.offset as f64,
2317 attr.shader_location,
2318 )
2319 })
2320 .collect::<Vec<webgpu_sys::GpuVertexAttribute>>();
2321
2322 let mapped_vbuf = webgpu_sys::GpuVertexBufferLayout::new_with_f64(
2323 vbuf.array_stride as f64,
2324 &mapped_attributes,
2325 );
2326 mapped_vbuf.set_step_mode(map_vertex_step_mode(vbuf.step_mode));
2327 js_sys::JsNullable::wrap(mapped_vbuf)
2328 }
2329 None => js_sys::JsNullable::new(),
2330 })
2331 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuVertexBufferLayout>>>();
2332
2333 mapped_vertex_state.set_buffers(&buffers);
2334
2335 let mapped_desc = match desc.layout {
2336 Some(layout) => webgpu_sys::GpuRenderPipelineDescriptor::new(
2337 &layout.inner.as_webgpu().inner,
2338 &mapped_vertex_state,
2339 ),
2340 None => webgpu_sys::GpuRenderPipelineDescriptor::new_with_gpu_auto_layout_mode(
2341 webgpu_sys::GpuAutoLayoutMode::Auto,
2342 &mapped_vertex_state,
2343 ),
2344 };
2345
2346 if let Some(label) = desc.label {
2347 mapped_desc.set_label(label);
2348 }
2349
2350 if let Some(ref depth_stencil) = desc.depth_stencil {
2351 mapped_desc.set_depth_stencil(&map_depth_stencil_state(depth_stencil));
2352 }
2353
2354 if let Some(ref frag) = desc.fragment {
2355 let targets = frag
2356 .targets
2357 .iter()
2358 .map(|target| match target {
2359 Some(target) => {
2360 let mapped_format = map_texture_format(target.format);
2361 let mapped_color_state =
2362 webgpu_sys::GpuColorTargetState::new(mapped_format);
2363 if let Some(ref bs) = target.blend {
2364 let alpha = map_blend_component(&bs.alpha);
2365 let color = map_blend_component(&bs.color);
2366 let mapped_blend_state = webgpu_sys::GpuBlendState::new(&alpha, &color);
2367 mapped_color_state.set_blend(&mapped_blend_state);
2368 }
2369 mapped_color_state.set_write_mask(target.write_mask.bits());
2370 js_sys::JsNullable::wrap(mapped_color_state)
2371 }
2372 None => js_sys::JsNullable::new(),
2373 })
2374 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuColorTargetState>>>();
2375 let module = frag.module.inner.as_webgpu();
2376 let mapped_fragment_desc = webgpu_sys::GpuFragmentState::new(&module.module, &targets);
2377 insert_constants_map(&mapped_fragment_desc, frag.compilation_options.constants);
2378 if let Some(ep) = frag.entry_point {
2379 mapped_fragment_desc.set_entry_point(ep);
2380 }
2381 mapped_desc.set_fragment(&mapped_fragment_desc);
2382 }
2383
2384 let mapped_multisample = webgpu_sys::GpuMultisampleState::new();
2385 mapped_multisample.set_count(desc.multisample.count);
2386 mapped_multisample.set_mask(desc.multisample.mask as u32);
2387 mapped_multisample
2388 .set_alpha_to_coverage_enabled(desc.multisample.alpha_to_coverage_enabled);
2389 mapped_desc.set_multisample(&mapped_multisample);
2390
2391 let mapped_primitive = map_primitive_state(&desc.primitive);
2392 mapped_desc.set_primitive(&mapped_primitive);
2393
2394 let render_pipeline = self.inner.create_render_pipeline(&mapped_desc).unwrap();
2395
2396 WebRenderPipeline {
2397 inner: render_pipeline,
2398 ident: crate::cmp::Identifier::create(),
2399 }
2400 .into()
2401 }
2402
2403 fn create_mesh_pipeline(
2404 &self,
2405 _desc: &crate::MeshPipelineDescriptor<'_>,
2406 ) -> dispatch::DispatchRenderPipeline {
2407 panic!("MESH_SHADER feature must be enabled to call create_mesh_pipeline")
2408 }
2409
2410 fn create_compute_pipeline(
2411 &self,
2412 desc: &crate::ComputePipelineDescriptor<'_>,
2413 ) -> dispatch::DispatchComputePipeline {
2414 let shader_module = desc.module.inner.as_webgpu();
2415 let mapped_compute_stage = webgpu_sys::GpuProgrammableStage::new(&shader_module.module);
2416 insert_constants_map(&mapped_compute_stage, desc.compilation_options.constants);
2417 if let Some(ep) = desc.entry_point {
2418 mapped_compute_stage.set_entry_point(ep);
2419 }
2420 let mapped_desc = match desc.layout {
2421 Some(layout) => webgpu_sys::GpuComputePipelineDescriptor::new(
2422 &layout.inner.as_webgpu().inner,
2423 &mapped_compute_stage,
2424 ),
2425 None => webgpu_sys::GpuComputePipelineDescriptor::new_with_gpu_auto_layout_mode(
2426 webgpu_sys::GpuAutoLayoutMode::Auto,
2427 &mapped_compute_stage,
2428 ),
2429 };
2430
2431 if let Some(label) = desc.label {
2432 mapped_desc.set_label(label);
2433 }
2434
2435 let compute_pipeline = self.inner.create_compute_pipeline(&mapped_desc);
2436
2437 WebComputePipeline {
2438 inner: compute_pipeline,
2439 ident: crate::cmp::Identifier::create(),
2440 }
2441 .into()
2442 }
2443
2444 unsafe fn create_pipeline_cache(
2445 &self,
2446 _desc: &crate::PipelineCacheDescriptor<'_>,
2447 ) -> dispatch::DispatchPipelineCache {
2448 WebPipelineCache {
2449 ident: crate::cmp::Identifier::create(),
2450 }
2451 .into()
2452 }
2453
2454 fn create_buffer(&self, desc: &crate::BufferDescriptor<'_>) -> dispatch::DispatchBuffer {
2455 let mapped_desc =
2456 webgpu_sys::GpuBufferDescriptor::new_with_f64(desc.size as f64, desc.usage.bits());
2457 mapped_desc.set_mapped_at_creation(desc.mapped_at_creation);
2458 if let Some(label) = desc.label {
2459 mapped_desc.set_label(label);
2460 }
2461 WebBuffer::new(self.inner.create_buffer(&mapped_desc).unwrap(), desc).into()
2462 }
2463
2464 fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> dispatch::DispatchTexture {
2465 let mapped_desc = webgpu_sys::GpuTextureDescriptor::new_with_gpu_extent_3d_dict(
2466 map_texture_format(desc.format),
2467 &map_extent_3d(desc.size),
2468 desc.usage.bits(),
2469 );
2470 if let Some(label) = desc.label {
2471 mapped_desc.set_label(label);
2472 }
2473 mapped_desc.set_dimension(map_texture_dimension(desc.dimension));
2474 mapped_desc.set_mip_level_count(desc.mip_level_count);
2475 mapped_desc.set_sample_count(desc.sample_count);
2476 let mapped_view_formats = desc
2477 .view_formats
2478 .iter()
2479 .map(|format| {
2480 wasm_bindgen::JsValue::from(map_texture_format(*format))
2481 .dyn_into::<js_sys::JsString>()
2482 .unwrap()
2483 })
2484 .collect::<Vec<js_sys::JsString>>();
2485 mapped_desc.set_view_formats(&mapped_view_formats);
2486
2487 let texture = self.inner.create_texture(&mapped_desc).unwrap();
2488 WebTexture {
2489 inner: texture,
2490 drop_guard: None,
2491 ident: crate::cmp::Identifier::create(),
2492 }
2493 .into()
2494 }
2495
2496 fn create_external_texture(
2497 &self,
2498 _desc: &crate::ExternalTextureDescriptor<'_>,
2499 _planes: &[&crate::TextureView],
2500 ) -> dispatch::DispatchExternalTexture {
2501 unimplemented!("ExternalTexture not implemented for web");
2502 }
2503
2504 fn create_blas(
2505 &self,
2506 _desc: &crate::CreateBlasDescriptor<'_>,
2507 _sizes: crate::BlasGeometrySizeDescriptors,
2508 ) -> (Option<u64>, dispatch::DispatchBlas) {
2509 unimplemented!("Raytracing not implemented for web");
2510 }
2511
2512 fn create_tlas(&self, _desc: &crate::CreateTlasDescriptor<'_>) -> dispatch::DispatchTlas {
2513 unimplemented!("Raytracing not implemented for web");
2514 }
2515
2516 fn create_sampler(&self, desc: &crate::SamplerDescriptor<'_>) -> dispatch::DispatchSampler {
2517 let mapped_desc = webgpu_sys::GpuSamplerDescriptor::new();
2518 mapped_desc.set_address_mode_u(map_address_mode(desc.address_mode_u));
2519 mapped_desc.set_address_mode_v(map_address_mode(desc.address_mode_v));
2520 mapped_desc.set_address_mode_w(map_address_mode(desc.address_mode_w));
2521 if let Some(compare) = desc.compare {
2522 mapped_desc.set_compare(map_compare_function(compare));
2523 }
2524 mapped_desc.set_lod_max_clamp(desc.lod_max_clamp);
2525 mapped_desc.set_lod_min_clamp(desc.lod_min_clamp);
2526 mapped_desc.set_mag_filter(map_filter_mode(desc.mag_filter));
2527 mapped_desc.set_min_filter(map_filter_mode(desc.min_filter));
2528 mapped_desc.set_mipmap_filter(map_mipmap_filter_mode(desc.mipmap_filter));
2529 mapped_desc.set_max_anisotropy(desc.anisotropy_clamp);
2530 if let Some(label) = desc.label {
2531 mapped_desc.set_label(label);
2532 }
2533
2534 let sampler = self.inner.create_sampler_with_descriptor(&mapped_desc);
2535
2536 WebSampler {
2537 inner: sampler,
2538 ident: crate::cmp::Identifier::create(),
2539 }
2540 .into()
2541 }
2542
2543 fn create_query_set(&self, desc: &crate::QuerySetDescriptor<'_>) -> dispatch::DispatchQuerySet {
2544 let ty = match desc.ty {
2545 wgt::QueryType::Occlusion => webgpu_sys::GpuQueryType::Occlusion,
2546 wgt::QueryType::Timestamp => webgpu_sys::GpuQueryType::Timestamp,
2547 wgt::QueryType::PipelineStatistics(_) => unreachable!(),
2548 };
2549 let mapped_desc = webgpu_sys::GpuQuerySetDescriptor::new(desc.count, ty);
2550 if let Some(label) = desc.label {
2551 mapped_desc.set_label(label);
2552 }
2553
2554 let query_set = self.inner.create_query_set(&mapped_desc).unwrap();
2555
2556 WebQuerySet {
2557 inner: query_set,
2558 ident: crate::cmp::Identifier::create(),
2559 }
2560 .into()
2561 }
2562
2563 fn create_command_encoder(
2564 &self,
2565 desc: &crate::CommandEncoderDescriptor<'_>,
2566 ) -> dispatch::DispatchCommandEncoder {
2567 let mapped_desc = webgpu_sys::GpuCommandEncoderDescriptor::new();
2568 if let Some(label) = desc.label {
2569 mapped_desc.set_label(label);
2570 }
2571
2572 let command_encoder = self
2573 .inner
2574 .create_command_encoder_with_descriptor(&mapped_desc);
2575
2576 WebCommandEncoder {
2577 inner: command_encoder,
2578 ident: crate::cmp::Identifier::create(),
2579 }
2580 .into()
2581 }
2582
2583 fn create_render_bundle_encoder(
2584 &self,
2585 desc: &crate::RenderBundleEncoderDescriptor<'_>,
2586 ) -> dispatch::DispatchRenderBundleEncoder {
2587 let mapped_color_formats = desc
2588 .color_formats
2589 .iter()
2590 .map(|cf| match cf {
2591 Some(cf) => js_sys::JsNullable::wrap(
2592 wasm_bindgen::JsValue::from(map_texture_format(*cf))
2593 .dyn_into::<js_sys::JsString>()
2594 .unwrap(),
2595 ),
2596 None => js_sys::JsNullable::new(),
2597 })
2598 .collect::<Vec<js_sys::JsNullable<js_sys::JsString>>>();
2599 let mapped_desc = webgpu_sys::GpuRenderBundleEncoderDescriptor::new(&mapped_color_formats);
2600 if let Some(label) = desc.label {
2601 mapped_desc.set_label(label);
2602 }
2603 if let Some(ds) = desc.depth_stencil {
2604 mapped_desc.set_depth_stencil_format(map_texture_format(ds.format));
2605 mapped_desc.set_depth_read_only(ds.depth_read_only);
2606 mapped_desc.set_stencil_read_only(ds.stencil_read_only);
2607 }
2608 mapped_desc.set_sample_count(desc.sample_count);
2609
2610 let render_bundle_encoder = self
2611 .inner
2612 .create_render_bundle_encoder(&mapped_desc)
2613 .unwrap();
2614
2615 WebRenderBundleEncoder {
2616 inner: render_bundle_encoder,
2617 ident: crate::cmp::Identifier::create(),
2618 }
2619 .into()
2620 }
2621
2622 fn set_device_lost_callback(&self, device_lost_callback: dispatch::BoxDeviceLostCallback) {
2623 let closure = Closure::once(move |info: webgpu_sys::GpuDeviceLostInfo| {
2624 device_lost_callback(
2625 match info.reason() {
2626 webgpu_sys::GpuDeviceLostReason::Destroyed => {
2627 crate::DeviceLostReason::Destroyed
2628 }
2629 webgpu_sys::GpuDeviceLostReason::Unknown => crate::DeviceLostReason::Unknown,
2630 _ => crate::DeviceLostReason::Unknown,
2631 },
2632 info.message(),
2633 );
2634 });
2635 let _ = self.inner.lost().then(&closure);
2636 closure.forget();
2639 }
2640
2641 fn on_uncaptured_error(&self, handler: Arc<dyn crate::UncapturedErrorHandler>) {
2642 let f = Closure::wrap(Box::new(move |event: webgpu_sys::GpuUncapturedErrorEvent| {
2643 let error = crate::Error::from_js(event.error().value_of());
2644 handler(error);
2645 }) as Box<dyn FnMut(_)>);
2646 self.inner
2647 .set_onuncapturederror(Some(f.as_ref().unchecked_ref()));
2648 f.forget();
2651 }
2652
2653 fn push_error_scope(&self, filter: crate::ErrorFilter) -> u32 {
2654 let index = self.error_scope_count.get();
2655 self.error_scope_count.set(
2656 index
2657 .checked_add(1)
2658 .expect("Greater than 2^32 nested error scopes"),
2659 );
2660 self.inner.push_error_scope(match filter {
2661 crate::ErrorFilter::OutOfMemory => webgpu_sys::GpuErrorFilter::OutOfMemory,
2662 crate::ErrorFilter::Validation => webgpu_sys::GpuErrorFilter::Validation,
2663 crate::ErrorFilter::Internal => webgpu_sys::GpuErrorFilter::Internal,
2664 });
2665 index
2666 }
2667
2668 fn pop_error_scope(&self, index: u32) -> Pin<Box<dyn dispatch::PopErrorScopeFuture>> {
2669 let current_scope_count = self.error_scope_count.get();
2670 let is_panicking = crate::util::is_panicking();
2671 if current_scope_count == 0 && !is_panicking {
2672 panic!("Mismatched pop_error_scope call: no error scope for this thread. Error scopes are thread-local.");
2673 }
2674 if index + 1 != current_scope_count && !is_panicking {
2675 panic!(
2676 "Mismatched pop_error_scope call: error scopes must be popped in reverse order."
2677 );
2678 }
2679 self.error_scope_count.set(index);
2682
2683 let error_promise = self.inner.pop_error_scope();
2684 Box::pin(MakeSendFuture::new(
2685 wasm_bindgen_futures::JsFuture::from(error_promise),
2686 future_pop_error_scope,
2687 ))
2688 }
2689
2690 unsafe fn start_graphics_debugger_capture(&self) {
2691 }
2693
2694 unsafe fn stop_graphics_debugger_capture(&self) {
2695 }
2697
2698 fn poll(&self, _poll_type: wgt::PollType<u64>) -> Result<crate::PollStatus, crate::PollError> {
2699 Ok(crate::PollStatus::QueueEmpty)
2701 }
2702
2703 fn get_internal_counters(&self) -> crate::InternalCounters {
2704 crate::InternalCounters::default()
2705 }
2706
2707 fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
2708 None
2709 }
2710
2711 fn destroy(&self) {
2712 self.inner.destroy();
2713 }
2714}
2715
2716impl Drop for WebDevice {
2717 fn drop(&mut self) {
2718 }
2720}
2721
2722impl dispatch::QueueInterface for WebQueue {
2723 fn write_buffer(
2724 &self,
2725 buffer: &dispatch::DispatchBuffer,
2726 offset: crate::BufferAddress,
2727 data: &[u8],
2728 ) {
2729 let buffer = buffer.as_webgpu();
2730 self.inner
2731 .write_buffer_with_f64_and_u8_slice_and_f64_and_f64(
2732 &buffer.inner,
2733 offset as f64,
2734 data,
2735 0f64,
2736 data.len() as f64,
2737 )
2738 .unwrap();
2739 }
2740
2741 fn create_staging_buffer(
2742 &self,
2743 size: crate::BufferSize,
2744 ) -> Option<dispatch::DispatchQueueWriteBuffer> {
2745 Some(
2746 WebQueueWriteBuffer {
2747 inner: vec![0; size.get() as usize].into_boxed_slice(),
2748 ident: crate::cmp::Identifier::create(),
2749 }
2750 .into(),
2751 )
2752 }
2753
2754 fn validate_write_buffer(
2755 &self,
2756 buffer: &dispatch::DispatchBuffer,
2757 offset: wgt::BufferAddress,
2758 size: wgt::BufferSize,
2759 ) -> Option<()> {
2760 let buffer = buffer.as_webgpu();
2761
2762 let usage = wgt::BufferUsages::from_bits_truncate(buffer.inner.usage());
2763 if !usage.contains(wgt::BufferUsages::COPY_DST) {
2765 log::error!("Destination buffer is missing the `COPY_DST` usage flag");
2766 return None;
2767 }
2768 let write_size = u64::from(size);
2769 if !write_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
2770 log::error!("Copy size {size} does not respect `COPY_BUFFER_ALIGNMENT`");
2771 return None;
2772 }
2773 if !offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
2774 log::error!(
2775 "Buffer offset {offset} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`"
2776 );
2777 return None;
2778 }
2779 if write_size + offset > buffer.inner.size() as u64 {
2780 log::error!("copy of {}..{} would end up overrunning the bounds of the destination buffer of size {}", offset, offset + write_size, buffer.inner.size());
2781 return None;
2782 }
2783 Some(())
2784 }
2785
2786 fn write_staging_buffer(
2787 &self,
2788 buffer: &dispatch::DispatchBuffer,
2789 offset: crate::BufferAddress,
2790 staging_buffer: &dispatch::DispatchQueueWriteBuffer,
2791 ) {
2792 let staging_buffer = staging_buffer.as_webgpu();
2793
2794 dispatch::QueueInterface::write_buffer(self, buffer, offset, &staging_buffer.inner)
2795 }
2796
2797 fn write_texture(
2798 &self,
2799 texture: crate::TexelCopyTextureInfo<'_>,
2800 data: &[u8],
2801 data_layout: crate::TexelCopyBufferLayout,
2802 size: crate::Extent3d,
2803 ) {
2804 let mapped_data_layout = webgpu_sys::GpuTexelCopyBufferLayout::new();
2805 if let Some(bytes_per_row) = data_layout.bytes_per_row {
2806 mapped_data_layout.set_bytes_per_row(bytes_per_row);
2807 }
2808 if let Some(rows_per_image) = data_layout.rows_per_image {
2809 mapped_data_layout.set_rows_per_image(rows_per_image);
2810 }
2811 mapped_data_layout.set_offset_f64(data_layout.offset as f64);
2812
2813 self.inner
2814 .write_texture_with_u8_slice_and_gpu_extent_3d_dict(
2815 &map_texture_copy_view(texture),
2816 data,
2817 &mapped_data_layout,
2818 &map_extent_3d(size),
2819 )
2820 .unwrap();
2821 }
2822
2823 fn copy_external_image_to_texture(
2824 &self,
2825 source: &crate::CopyExternalImageSourceInfo,
2826 dest: crate::CopyExternalImageDestInfo<&crate::api::Texture>,
2827 size: crate::Extent3d,
2828 ) {
2829 self.inner
2830 .copy_external_image_to_texture_with_gpu_extent_3d_dict(
2831 &map_external_texture_copy_view(source),
2832 &map_tagged_texture_copy_view(dest),
2833 &map_extent_3d(size),
2834 )
2835 .unwrap();
2836 }
2837
2838 fn submit(
2839 &self,
2840 command_buffers: &mut dyn Iterator<Item = dispatch::DispatchCommandBuffer>,
2841 ) -> u64 {
2842 let temp_command_buffers = command_buffers.collect::<Vec<_>>();
2843
2844 let array = temp_command_buffers
2845 .iter()
2846 .map(|buffer| buffer.as_webgpu().inner.clone())
2847 .collect::<Vec<webgpu_sys::GpuCommandBuffer>>();
2848
2849 self.inner.submit(&array);
2850
2851 0
2852 }
2853
2854 fn get_timestamp_period(&self) -> f32 {
2855 1.0
2857 }
2858
2859 fn on_submitted_work_done(&self, callback: dispatch::BoxSubmittedWorkDoneCallback) {
2860 let promise = self.inner.on_submitted_work_done();
2861 wasm_bindgen_futures::spawn_local(async move {
2862 match wasm_bindgen_futures::JsFuture::from(promise).await {
2863 Ok(_) => callback(),
2864 Err(error) => {
2865 log::error!("on_submitted_work_done promise failed: {error:?}");
2866 callback();
2867 }
2868 }
2869 });
2870 }
2871
2872 fn compact_blas(
2873 &self,
2874 _blas: &dispatch::DispatchBlas,
2875 ) -> (Option<u64>, dispatch::DispatchBlas) {
2876 unimplemented!("Raytracing not implemented for web")
2877 }
2878
2879 fn present(&self, _detail: &dispatch::DispatchSurfaceOutputDetail) {
2880 }
2882}
2883impl Drop for WebQueue {
2884 fn drop(&mut self) {
2885 }
2887}
2888
2889impl dispatch::ShaderModuleInterface for WebShaderModule {
2890 fn get_compilation_info(&self) -> Pin<Box<dyn dispatch::ShaderCompilationInfoFuture>> {
2891 let compilation_info_promise = self.module.get_compilation_info();
2892 let map_future = Box::new({
2893 let compilation_info = self.compilation_info.clone();
2894 move |result| future_compilation_info(result, &compilation_info)
2895 });
2896 Box::pin(MakeSendFuture::new(
2897 wasm_bindgen_futures::JsFuture::from(compilation_info_promise),
2898 map_future,
2899 ))
2900 }
2901}
2902impl Drop for WebShaderModule {
2903 fn drop(&mut self) {
2904 }
2906}
2907
2908impl dispatch::BindGroupLayoutInterface for WebBindGroupLayout {}
2909impl Drop for WebBindGroupLayout {
2910 fn drop(&mut self) {
2911 }
2913}
2914
2915impl dispatch::BindGroupInterface for WebBindGroup {}
2916impl Drop for WebBindGroup {
2917 fn drop(&mut self) {
2918 }
2920}
2921
2922impl dispatch::TextureViewInterface for WebTextureView {}
2923impl Drop for WebTextureView {
2924 fn drop(&mut self) {
2925 }
2927}
2928
2929impl dispatch::SamplerInterface for WebSampler {}
2930impl Drop for WebSampler {
2931 fn drop(&mut self) {
2932 }
2934}
2935
2936impl dispatch::BufferInterface for WebBuffer {
2937 fn map_async(
2938 &self,
2939 mode: crate::MapMode,
2940 range: Range<crate::BufferAddress>,
2941 callback: dispatch::BufferMapCallback,
2942 ) {
2943 let map_promise = self.inner.map_async_with_f64_and_f64(
2944 map_map_mode(mode),
2945 range.start as f64,
2946 (range.end - range.start) as f64,
2947 );
2948
2949 self.set_mapped_range(range);
2950
2951 register_then_closures(&map_promise, callback, Ok(()), Err(crate::BufferAsyncError));
2952 }
2953
2954 fn get_mapped_range(
2955 &self,
2956 sub_range: Range<crate::BufferAddress>,
2957 ) -> Result<dispatch::DispatchBufferMappedRange, crate::MapRangeError> {
2958 let actual_mapping = self.get_mapped_range(sub_range)?;
2959 Ok(WebBufferMappedRange {
2960 actual_mapping,
2961 temporary_mapping: OnceCell::new(),
2962 temporary_mapping_modified: false,
2963 ident: crate::cmp::Identifier::create(),
2964 }
2965 .into())
2966 }
2967
2968 fn unmap(&self) {
2969 self.inner.unmap();
2970 self.mapping.borrow_mut().mapped_buffer = None;
2971 }
2972
2973 fn destroy(&self) {
2974 self.inner.destroy();
2975 }
2976}
2977impl Drop for WebBuffer {
2978 fn drop(&mut self) {
2979 }
2981}
2982
2983impl dispatch::TextureInterface for WebTexture {
2984 fn create_view(
2985 &self,
2986 desc: &crate::TextureViewDescriptor<'_>,
2987 ) -> dispatch::DispatchTextureView {
2988 let mapped = webgpu_sys::GpuTextureViewDescriptor::new();
2989 if let Some(dim) = desc.dimension {
2990 mapped.set_dimension(map_texture_view_dimension(dim));
2991 }
2992 if let Some(format) = desc.format {
2993 mapped.set_format(map_texture_format(format));
2994 }
2995 mapped.set_aspect(map_texture_aspect(desc.aspect));
2996 mapped.set_base_array_layer(desc.base_array_layer);
2997 if let Some(count) = desc.array_layer_count {
2998 mapped.set_array_layer_count(count);
2999 }
3000 mapped.set_base_mip_level(desc.base_mip_level);
3001 if let Some(count) = desc.mip_level_count {
3002 mapped.set_mip_level_count(count);
3003 }
3004 if let Some(label) = desc.label {
3005 mapped.set_label(label);
3006 }
3007 mapped.set_usage(desc.usage.unwrap_or(wgt::TextureUsages::empty()).bits());
3008
3009 let view = self.inner.create_view_with_descriptor(&mapped).unwrap();
3010
3011 WebTextureView {
3012 inner: view,
3013 ident: crate::cmp::Identifier::create(),
3014 }
3015 .into()
3016 }
3017
3018 fn destroy(&self) {
3019 if self.drop_guard.is_none() {
3020 self.inner.destroy();
3021 }
3022 }
3023}
3024impl Drop for WebTexture {
3025 fn drop(&mut self) {
3026 }
3029}
3030
3031impl dispatch::ExternalTextureInterface for WebExternalTexture {
3032 fn destroy(&self) {
3033 unimplemented!("ExternalTexture not implemented for web");
3034 }
3035}
3036impl Drop for WebExternalTexture {
3037 fn drop(&mut self) {
3038 unimplemented!("ExternalTexture not implemented for web");
3039 }
3040}
3041
3042impl dispatch::BlasInterface for WebBlas {
3043 fn prepare_compact_async(&self, _callback: BlasCompactCallback) {
3044 unimplemented!("Raytracing not implemented for web")
3045 }
3046 fn ready_for_compaction(&self) -> bool {
3047 unimplemented!("Raytracing not implemented for web")
3048 }
3049}
3050impl Drop for WebBlas {
3051 fn drop(&mut self) {
3052 }
3054}
3055
3056impl dispatch::TlasInterface for WebTlas {}
3057impl Drop for WebTlas {
3058 fn drop(&mut self) {
3059 }
3061}
3062
3063impl dispatch::QuerySetInterface for WebQuerySet {
3064 fn destroy(&self) {
3065 self.inner.destroy();
3066 }
3067}
3068
3069impl Drop for WebQuerySet {
3070 fn drop(&mut self) {
3071 }
3073}
3074
3075impl dispatch::PipelineLayoutInterface for WebPipelineLayout {}
3076impl Drop for WebPipelineLayout {
3077 fn drop(&mut self) {
3078 }
3080}
3081
3082impl dispatch::RenderPipelineInterface for WebRenderPipeline {
3083 fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {
3084 let bind_group_layout = self.inner.get_bind_group_layout(index);
3085
3086 WebBindGroupLayout {
3087 inner: bind_group_layout,
3088 ident: crate::cmp::Identifier::create(),
3089 }
3090 .into()
3091 }
3092}
3093impl Drop for WebRenderPipeline {
3094 fn drop(&mut self) {
3095 }
3097}
3098
3099impl dispatch::ComputePipelineInterface for WebComputePipeline {
3100 fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {
3101 let bind_group_layout = self.inner.get_bind_group_layout(index);
3102
3103 WebBindGroupLayout {
3104 inner: bind_group_layout,
3105 ident: crate::cmp::Identifier::create(),
3106 }
3107 .into()
3108 }
3109}
3110impl Drop for WebComputePipeline {
3111 fn drop(&mut self) {
3112 }
3114}
3115
3116impl dispatch::CommandEncoderInterface for WebCommandEncoder {
3117 fn copy_buffer_to_buffer(
3118 &self,
3119 source: &dispatch::DispatchBuffer,
3120 source_offset: crate::BufferAddress,
3121 destination: &dispatch::DispatchBuffer,
3122 destination_offset: crate::BufferAddress,
3123 copy_size: Option<crate::BufferAddress>,
3124 ) {
3125 let source = source.as_webgpu();
3126 let destination = destination.as_webgpu();
3127
3128 if let Some(size) = copy_size {
3129 self.inner
3130 .copy_buffer_to_buffer_with_f64_and_f64_and_f64(
3131 &source.inner,
3132 source_offset as f64,
3133 &destination.inner,
3134 destination_offset as f64,
3135 size as f64,
3136 )
3137 .unwrap();
3138 } else {
3139 self.inner
3140 .copy_buffer_to_buffer_with_f64_and_f64(
3141 &source.inner,
3142 source_offset as f64,
3143 &destination.inner,
3144 destination_offset as f64,
3145 )
3146 .unwrap();
3147 }
3148 }
3149
3150 fn copy_buffer_to_texture(
3151 &self,
3152 source: crate::TexelCopyBufferInfo<'_>,
3153 destination: crate::TexelCopyTextureInfo<'_>,
3154 copy_size: crate::Extent3d,
3155 ) {
3156 self.inner
3157 .copy_buffer_to_texture_with_gpu_extent_3d_dict(
3158 &map_buffer_copy_view(source),
3159 &map_texture_copy_view(destination),
3160 &map_extent_3d(copy_size),
3161 )
3162 .unwrap();
3163 }
3164
3165 fn copy_texture_to_buffer(
3166 &self,
3167 source: crate::TexelCopyTextureInfo<'_>,
3168 destination: crate::TexelCopyBufferInfo<'_>,
3169 copy_size: crate::Extent3d,
3170 ) {
3171 self.inner
3172 .copy_texture_to_buffer_with_gpu_extent_3d_dict(
3173 &map_texture_copy_view(source),
3174 &map_buffer_copy_view(destination),
3175 &map_extent_3d(copy_size),
3176 )
3177 .unwrap();
3178 }
3179
3180 fn copy_texture_to_texture(
3181 &self,
3182 source: crate::TexelCopyTextureInfo<'_>,
3183 destination: crate::TexelCopyTextureInfo<'_>,
3184 copy_size: crate::Extent3d,
3185 ) {
3186 self.inner
3187 .copy_texture_to_texture_with_gpu_extent_3d_dict(
3188 &map_texture_copy_view(source),
3189 &map_texture_copy_view(destination),
3190 &map_extent_3d(copy_size),
3191 )
3192 .unwrap();
3193 }
3194
3195 fn begin_compute_pass(
3196 &self,
3197 desc: &crate::ComputePassDescriptor<'_>,
3198 ) -> dispatch::DispatchComputePass {
3199 let mapped_desc = webgpu_sys::GpuComputePassDescriptor::new();
3200 if let Some(label) = desc.label {
3201 mapped_desc.set_label(label);
3202 }
3203
3204 if let Some(ref timestamp_writes) = desc.timestamp_writes {
3205 let query_set = timestamp_writes.query_set.inner.as_webgpu();
3206 let writes = webgpu_sys::GpuComputePassTimestampWrites::new(&query_set.inner);
3207 if let Some(index) = timestamp_writes.beginning_of_pass_write_index {
3208 writes.set_beginning_of_pass_write_index(index);
3209 }
3210 if let Some(index) = timestamp_writes.end_of_pass_write_index {
3211 writes.set_end_of_pass_write_index(index);
3212 }
3213 mapped_desc.set_timestamp_writes(&writes);
3214 }
3215
3216 let compute_pass = self.inner.begin_compute_pass_with_descriptor(&mapped_desc);
3217
3218 WebComputePassEncoder {
3219 inner: compute_pass,
3220 ident: crate::cmp::Identifier::create(),
3221 }
3222 .into()
3223 }
3224
3225 fn begin_render_pass(
3226 &self,
3227 desc: &crate::RenderPassDescriptor<'_>,
3228 ) -> dispatch::DispatchRenderPass {
3229 let mapped_color_attachments = desc
3230 .color_attachments
3231 .iter()
3232 .map(|attachment| match attachment {
3233 Some(ca) => {
3234 let (load_op, clear_value) = match ca.ops.load {
3235 crate::LoadOp::Clear(color) => {
3236 (webgpu_sys::GpuLoadOp::Clear, Some(map_color(color)))
3237 }
3238 crate::LoadOp::DontCare(_token) => {
3239 (
3242 webgpu_sys::GpuLoadOp::Clear,
3243 Some(map_color(crate::Color::BLACK)),
3244 )
3245 }
3246 crate::LoadOp::Load => (webgpu_sys::GpuLoadOp::Load, None),
3247 };
3248
3249 let view = &ca.view.inner.as_webgpu().inner;
3250
3251 let mapped_color_attachment =
3252 webgpu_sys::GpuRenderPassColorAttachment::new_with_gpu_texture_view(
3253 load_op,
3254 map_store_op(ca.ops.store),
3255 view,
3256 );
3257 if let Some(cv) = clear_value {
3258 mapped_color_attachment.set_clear_value_gpu_color_dict(&cv);
3259 }
3260 if let Some(rt) = ca.resolve_target {
3261 let resolve_target_view = &rt.inner.as_webgpu().inner;
3262 mapped_color_attachment
3263 .set_resolve_target_gpu_texture_view(resolve_target_view);
3264 }
3265 mapped_color_attachment.set_store_op(map_store_op(ca.ops.store));
3266
3267 js_sys::JsNullable::wrap(mapped_color_attachment)
3268 }
3269 None => js_sys::JsNullable::new(),
3270 })
3271 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuRenderPassColorAttachment>>>();
3272
3273 let mapped_desc = webgpu_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments);
3274
3275 if let Some(label) = desc.label {
3276 mapped_desc.set_label(label);
3277 }
3278
3279 if let Some(dsa) = &desc.depth_stencil_attachment {
3280 let depth_stencil_attachment = &dsa.view.inner.as_webgpu().inner;
3281 let mapped_depth_stencil_attachment =
3282 webgpu_sys::GpuRenderPassDepthStencilAttachment::new_with_gpu_texture_view(
3283 depth_stencil_attachment,
3284 );
3285 if let Some(ref ops) = dsa.depth_ops {
3286 let load_op = match ops.load {
3287 crate::LoadOp::Clear(v) => {
3288 mapped_depth_stencil_attachment.set_depth_clear_value(v);
3289 webgpu_sys::GpuLoadOp::Clear
3290 }
3291 crate::LoadOp::DontCare(_token) => {
3292 mapped_depth_stencil_attachment.set_depth_clear_value(1.0);
3294 webgpu_sys::GpuLoadOp::Clear
3295 }
3296 crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load,
3297 };
3298 mapped_depth_stencil_attachment.set_depth_load_op(load_op);
3299 mapped_depth_stencil_attachment.set_depth_store_op(map_store_op(ops.store));
3300 }
3301 mapped_depth_stencil_attachment.set_depth_read_only(dsa.depth_ops.is_none());
3302 if let Some(ref ops) = dsa.stencil_ops {
3303 let load_op = match ops.load {
3304 crate::LoadOp::Clear(v) => {
3305 mapped_depth_stencil_attachment.set_stencil_clear_value(v);
3306 webgpu_sys::GpuLoadOp::Clear
3307 }
3308 crate::LoadOp::DontCare(_token) => {
3309 mapped_depth_stencil_attachment.set_stencil_clear_value(0);
3311 webgpu_sys::GpuLoadOp::Clear
3312 }
3313 crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load,
3314 };
3315 mapped_depth_stencil_attachment.set_stencil_load_op(load_op);
3316 mapped_depth_stencil_attachment.set_stencil_store_op(map_store_op(ops.store));
3317 }
3318 mapped_depth_stencil_attachment.set_stencil_read_only(dsa.stencil_ops.is_none());
3319 mapped_desc.set_depth_stencil_attachment(&mapped_depth_stencil_attachment);
3320 }
3321
3322 if let Some(ref timestamp_writes) = desc.timestamp_writes {
3323 let query_set = ×tamp_writes.query_set.inner.as_webgpu().inner;
3324 let writes = webgpu_sys::GpuRenderPassTimestampWrites::new(query_set);
3325 if let Some(index) = timestamp_writes.beginning_of_pass_write_index {
3326 writes.set_beginning_of_pass_write_index(index);
3327 }
3328 if let Some(index) = timestamp_writes.end_of_pass_write_index {
3329 writes.set_end_of_pass_write_index(index);
3330 }
3331 mapped_desc.set_timestamp_writes(&writes);
3332 }
3333
3334 let render_pass = self.inner.begin_render_pass(&mapped_desc).unwrap();
3335
3336 WebRenderPassEncoder {
3337 inner: render_pass,
3338 ident: crate::cmp::Identifier::create(),
3339 }
3340 .into()
3341 }
3342
3343 fn finish(&mut self) -> dispatch::DispatchCommandBuffer {
3344 let label = self.inner.label();
3345 let buffer = if label.is_empty() {
3346 self.inner.finish()
3347 } else {
3348 let mapped_desc = webgpu_sys::GpuCommandBufferDescriptor::new();
3349 mapped_desc.set_label(&label);
3350
3351 self.inner.finish_with_descriptor(&mapped_desc)
3352 };
3353
3354 WebCommandBuffer {
3355 inner: buffer,
3356 ident: crate::cmp::Identifier::create(),
3357 }
3358 .into()
3359 }
3360
3361 fn clear_texture(
3362 &self,
3363 _texture: &dispatch::DispatchTexture,
3364 _subresource_range: &crate::ImageSubresourceRange,
3365 ) {
3366 unimplemented!("clear_texture is not yet implemented");
3367 }
3368
3369 fn clear_buffer(
3370 &self,
3371 buffer: &dispatch::DispatchBuffer,
3372 offset: crate::BufferAddress,
3373 size: Option<crate::BufferAddress>,
3374 ) {
3375 let buffer = buffer.as_webgpu();
3376
3377 match size {
3378 Some(size) => {
3379 self.inner
3380 .clear_buffer_with_f64_and_f64(&buffer.inner, offset as f64, size as f64)
3381 }
3382 None => self
3383 .inner
3384 .clear_buffer_with_f64(&buffer.inner, offset as f64),
3385 }
3386 }
3387
3388 fn insert_debug_marker(&self, label: &str) {
3389 self.inner.insert_debug_marker(label)
3390 }
3391
3392 fn push_debug_group(&self, group_label: &str) {
3393 self.inner.push_debug_group(group_label)
3394 }
3395
3396 fn pop_debug_group(&self) {
3397 self.inner.pop_debug_group()
3398 }
3399
3400 fn write_timestamp(&self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {
3401 panic!("TIMESTAMP_QUERY_INSIDE_ENCODERS feature must be enabled to call write_timestamp on a command encoder.")
3404 }
3405
3406 fn resolve_query_set(
3407 &self,
3408 query_set: &dispatch::DispatchQuerySet,
3409 first_query: u32,
3410 query_count: u32,
3411 destination: &dispatch::DispatchBuffer,
3412 destination_offset: crate::BufferAddress,
3413 ) {
3414 let query_set = &query_set.as_webgpu().inner;
3415 let destination = &destination.as_webgpu().inner;
3416
3417 self.inner.resolve_query_set_with_u32(
3418 query_set,
3419 first_query,
3420 query_count,
3421 destination,
3422 destination_offset as u32,
3423 );
3424 }
3425
3426 fn mark_acceleration_structures_built<'a>(
3427 &self,
3428 _blas: &mut dyn Iterator<Item = &'a Blas>,
3429 _tlas: &mut dyn Iterator<Item = &'a Tlas>,
3430 ) {
3431 unimplemented!("Raytracing not implemented for web");
3432 }
3433
3434 fn build_acceleration_structures<'a>(
3435 &self,
3436 _blas: &mut dyn Iterator<Item = &'a crate::BlasBuildEntry<'a>>,
3437 _tlas: &mut dyn Iterator<Item = &'a crate::Tlas>,
3438 ) {
3439 unimplemented!("Raytracing not implemented for web");
3440 }
3441
3442 fn transition_resources<'a>(
3443 &mut self,
3444 _buffer_transitions: &mut dyn Iterator<
3445 Item = wgt::BufferTransition<&'a dispatch::DispatchBuffer>,
3446 >,
3447 _texture_transitions: &mut dyn Iterator<
3448 Item = wgt::TextureTransition<&'a dispatch::DispatchTexture>,
3449 >,
3450 ) {
3451 }
3453}
3454impl Drop for WebCommandEncoder {
3455 fn drop(&mut self) {
3456 }
3458}
3459
3460impl dispatch::PipelineCacheInterface for WebPipelineCache {
3461 fn get_data(&self) -> Option<Vec<u8>> {
3462 todo!()
3463 }
3464}
3465impl Drop for WebPipelineCache {
3466 fn drop(&mut self) {
3467 }
3469}
3470
3471impl dispatch::ComputePassInterface for WebComputePassEncoder {
3472 fn set_pipeline(&mut self, pipeline: &dispatch::DispatchComputePipeline) {
3473 let pipeline = &pipeline.as_webgpu().inner;
3474 self.inner.set_pipeline(pipeline);
3475 }
3476
3477 fn set_bind_group(
3478 &mut self,
3479 index: u32,
3480 bind_group: Option<&dispatch::DispatchBindGroup>,
3481 offsets: &[crate::DynamicOffset],
3482 ) {
3483 let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);
3484
3485 if offsets.is_empty() {
3486 self.inner.set_bind_group(index, bind_group);
3487 } else {
3488 self.inner
3489 .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(
3490 index,
3491 bind_group,
3492 offsets,
3493 0f64,
3494 offsets.len() as u32,
3495 )
3496 .unwrap();
3497 }
3498 }
3499
3500 fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {
3501 panic!("IMMEDIATES feature must be enabled to call set_immediates")
3502 }
3503
3504 fn insert_debug_marker(&mut self, label: &str) {
3505 self.inner.insert_debug_marker(label);
3506 }
3507
3508 fn push_debug_group(&mut self, group_label: &str) {
3509 self.inner.push_debug_group(group_label);
3510 }
3511
3512 fn pop_debug_group(&mut self) {
3513 self.inner.pop_debug_group();
3514 }
3515
3516 fn write_timestamp(&mut self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {
3517 panic!("TIMESTAMP_QUERY_INSIDE_PASSES feature must be enabled to call write_timestamp in a compute pass.")
3518 }
3519
3520 fn begin_pipeline_statistics_query(
3521 &mut self,
3522 _query_set: &dispatch::DispatchQuerySet,
3523 _query_index: u32,
3524 ) {
3525 }
3527
3528 fn end_pipeline_statistics_query(&mut self) {
3529 }
3531
3532 fn dispatch_workgroups(&mut self, x: u32, y: u32, z: u32) {
3533 self.inner
3534 .dispatch_workgroups_with_workgroup_count_y_and_workgroup_count_z(x, y, z);
3535 }
3536
3537 fn dispatch_workgroups_indirect(
3538 &mut self,
3539 indirect_buffer: &dispatch::DispatchBuffer,
3540 indirect_offset: crate::BufferAddress,
3541 ) {
3542 let indirect_buffer = indirect_buffer.as_webgpu();
3543
3544 self.inner
3545 .dispatch_workgroups_indirect_with_f64(&indirect_buffer.inner, indirect_offset as f64);
3546 }
3547
3548 fn transition_resources<'a>(
3549 &mut self,
3550 _buffer_transitions: &mut dyn Iterator<
3551 Item = wgt::BufferTransition<&'a dispatch::DispatchBuffer>,
3552 >,
3553 _texture_transitions: &mut dyn Iterator<
3554 Item = wgt::TextureTransition<&'a dispatch::DispatchTextureView>,
3555 >,
3556 ) {
3557 }
3559}
3560impl Drop for WebComputePassEncoder {
3561 fn drop(&mut self) {
3562 self.inner.end();
3563 }
3564}
3565
3566impl dispatch::RenderPassInterface for WebRenderPassEncoder {
3567 fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {
3568 let pipeline = &pipeline.as_webgpu().inner;
3569
3570 self.inner.set_pipeline(pipeline);
3571 }
3572
3573 fn set_bind_group(
3574 &mut self,
3575 index: u32,
3576 bind_group: Option<&dispatch::DispatchBindGroup>,
3577 offsets: &[crate::DynamicOffset],
3578 ) {
3579 let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);
3580
3581 if offsets.is_empty() {
3582 self.inner.set_bind_group(index, bind_group);
3583 } else {
3584 self.inner
3585 .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(
3586 index,
3587 bind_group,
3588 offsets,
3589 0f64,
3590 offsets.len() as u32,
3591 )
3592 .unwrap();
3593 }
3594 }
3595
3596 fn set_index_buffer(
3597 &mut self,
3598 buffer: &dispatch::DispatchBuffer,
3599 index_format: crate::IndexFormat,
3600 offset: crate::BufferAddress,
3601 size: Option<crate::BufferSize>,
3602 ) {
3603 let buffer = buffer.as_webgpu();
3604 let index_format = map_index_format(index_format);
3605
3606 if let Some(size) = size {
3607 self.inner.set_index_buffer_with_f64_and_f64(
3608 &buffer.inner,
3609 index_format,
3610 offset as f64,
3611 size.get() as f64,
3612 );
3613 } else {
3614 self.inner
3615 .set_index_buffer_with_f64(&buffer.inner, index_format, offset as f64);
3616 }
3617 }
3618
3619 fn set_vertex_buffer(
3620 &mut self,
3621 slot: u32,
3622 buffer: Option<&dispatch::DispatchBuffer>,
3623 offset: crate::BufferAddress,
3624 size: Option<crate::BufferSize>,
3625 ) {
3626 let buffer = buffer.map(|buffer| &buffer.as_webgpu().inner);
3627
3628 if let Some(size) = size {
3629 self.inner.set_vertex_buffer_with_f64_and_f64(
3630 slot,
3631 buffer,
3632 offset as f64,
3633 size.get() as f64,
3634 );
3635 } else {
3636 self.inner
3637 .set_vertex_buffer_with_f64(slot, buffer, offset as f64);
3638 }
3639 }
3640
3641 fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {
3642 panic!("IMMEDIATES feature must be enabled to call set_immediates")
3643 }
3644
3645 fn set_blend_constant(&mut self, color: crate::Color) {
3646 self.inner
3647 .set_blend_constant_with_gpu_color_dict(&map_color(color))
3648 .unwrap();
3649 }
3650
3651 fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {
3652 self.inner.set_scissor_rect(x, y, width, height);
3653 }
3654
3655 fn set_viewport(
3656 &mut self,
3657 x: f32,
3658 y: f32,
3659 width: f32,
3660 height: f32,
3661 min_depth: f32,
3662 max_depth: f32,
3663 ) {
3664 self.inner
3665 .set_viewport(x, y, width, height, min_depth, max_depth);
3666 }
3667
3668 fn set_stencil_reference(&mut self, reference: u32) {
3669 self.inner.set_stencil_reference(reference);
3670 }
3671
3672 fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
3673 self.inner
3674 .draw_with_instance_count_and_first_vertex_and_first_instance(
3675 vertices.end - vertices.start,
3676 instances.end - instances.start,
3677 vertices.start,
3678 instances.start,
3679 );
3680 }
3681
3682 fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
3683 self.inner
3684 .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(
3685 indices.end - indices.start,
3686 instances.end - instances.start,
3687 indices.start,
3688 base_vertex,
3689 instances.start,
3690 )
3691 }
3692
3693 fn draw_mesh_tasks(&mut self, _group_count_x: u32, _group_count_y: u32, _group_count_z: u32) {
3694 panic!("MESH_SHADER feature must be enabled to call draw_mesh_tasks")
3695 }
3696
3697 fn draw_indirect(
3698 &mut self,
3699 indirect_buffer: &dispatch::DispatchBuffer,
3700 indirect_offset: crate::BufferAddress,
3701 ) {
3702 let buffer = indirect_buffer.as_webgpu();
3703 self.inner
3704 .draw_indirect_with_f64(&buffer.inner, indirect_offset as f64);
3705 }
3706
3707 fn draw_indexed_indirect(
3708 &mut self,
3709 indirect_buffer: &dispatch::DispatchBuffer,
3710 indirect_offset: crate::BufferAddress,
3711 ) {
3712 let buffer = indirect_buffer.as_webgpu();
3713 self.inner
3714 .draw_indexed_indirect_with_f64(&buffer.inner, indirect_offset as f64);
3715 }
3716
3717 fn draw_mesh_tasks_indirect(
3718 &mut self,
3719 _indirect_buffer: &dispatch::DispatchBuffer,
3720 _indirect_offset: crate::BufferAddress,
3721 ) {
3722 panic!("MESH_SHADER feature must be enabled to call draw_mesh_tasks_indirect")
3723 }
3724
3725 fn multi_draw_indirect(
3726 &mut self,
3727 indirect_buffer: &dispatch::DispatchBuffer,
3728 indirect_offset: crate::BufferAddress,
3729 count: u32,
3730 ) {
3731 let buffer = indirect_buffer.as_webgpu();
3732
3733 for i in 0..count {
3734 let offset = indirect_offset + i as crate::BufferAddress * 16;
3735 self.inner
3736 .draw_indirect_with_f64(&buffer.inner, offset as f64);
3737 }
3738 }
3739
3740 fn multi_draw_indexed_indirect(
3741 &mut self,
3742 indirect_buffer: &dispatch::DispatchBuffer,
3743 indirect_offset: crate::BufferAddress,
3744 count: u32,
3745 ) {
3746 let buffer = indirect_buffer.as_webgpu();
3747
3748 for i in 0..count {
3749 let offset = indirect_offset + i as crate::BufferAddress * 20;
3750 self.inner
3751 .draw_indexed_indirect_with_f64(&buffer.inner, offset as f64);
3752 }
3753 }
3754
3755 fn multi_draw_mesh_tasks_indirect(
3756 &mut self,
3757 _indirect_buffer: &dispatch::DispatchBuffer,
3758 _indirect_offset: crate::BufferAddress,
3759 _count: u32,
3760 ) {
3761 panic!("MESH_SHADER feature must be enabled to call multi_draw_mesh_tasks_indirect")
3762 }
3763
3764 fn multi_draw_indirect_count(
3765 &mut self,
3766 _indirect_buffer: &dispatch::DispatchBuffer,
3767 _indirect_offset: crate::BufferAddress,
3768 _count_buffer: &dispatch::DispatchBuffer,
3769 _count_buffer_offset: crate::BufferAddress,
3770 _max_count: u32,
3771 ) {
3772 panic!(
3773 "MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indirect_count"
3774 )
3775 }
3776
3777 fn multi_draw_indexed_indirect_count(
3778 &mut self,
3779 _indirect_buffer: &dispatch::DispatchBuffer,
3780 _indirect_offset: crate::BufferAddress,
3781 _count_buffer: &dispatch::DispatchBuffer,
3782 _count_buffer_offset: crate::BufferAddress,
3783 _max_count: u32,
3784 ) {
3785 panic!("MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indexed_indirect_count")
3786 }
3787
3788 fn multi_draw_mesh_tasks_indirect_count(
3789 &mut self,
3790 _indirect_buffer: &dispatch::DispatchBuffer,
3791 _indirect_offset: crate::BufferAddress,
3792 _count_buffer: &dispatch::DispatchBuffer,
3793 _count_buffer_offset: crate::BufferAddress,
3794 _max_count: u32,
3795 ) {
3796 panic!("MESH_SHADER feature must be enabled to call multi_draw_mesh_tasks_indirect_count")
3797 }
3798
3799 fn insert_debug_marker(&mut self, label: &str) {
3800 self.inner.insert_debug_marker(label);
3801 }
3802
3803 fn push_debug_group(&mut self, group_label: &str) {
3804 self.inner.push_debug_group(group_label);
3805 }
3806
3807 fn pop_debug_group(&mut self) {
3808 self.inner.pop_debug_group();
3809 }
3810
3811 fn write_timestamp(&mut self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {
3812 panic!("TIMESTAMP_QUERY_INSIDE_PASSES feature must be enabled to call write_timestamp in a render pass.")
3813 }
3814
3815 fn begin_occlusion_query(&mut self, query_index: u32) {
3816 self.inner.begin_occlusion_query(query_index);
3817 }
3818
3819 fn end_occlusion_query(&mut self) {
3820 self.inner.end_occlusion_query();
3821 }
3822
3823 fn begin_pipeline_statistics_query(
3824 &mut self,
3825 _query_set: &dispatch::DispatchQuerySet,
3826 _query_index: u32,
3827 ) {
3828 }
3830
3831 fn end_pipeline_statistics_query(&mut self) {
3832 }
3834
3835 fn execute_bundles(
3836 &mut self,
3837 render_bundles: &mut dyn Iterator<Item = &dispatch::DispatchRenderBundle>,
3838 ) {
3839 let mapped = render_bundles
3840 .map(|bundle| bundle.as_webgpu().inner.clone())
3841 .collect::<Vec<webgpu_sys::GpuRenderBundle>>();
3842 self.inner.execute_bundles(&mapped);
3843 }
3844}
3845impl Drop for WebRenderPassEncoder {
3846 fn drop(&mut self) {
3847 self.inner.end();
3848 }
3849}
3850
3851impl dispatch::CommandBufferInterface for WebCommandBuffer {}
3852impl Drop for WebCommandBuffer {
3853 fn drop(&mut self) {
3854 }
3856}
3857
3858impl dispatch::RenderBundleEncoderInterface for WebRenderBundleEncoder {
3859 fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {
3860 let pipeline = &pipeline.as_webgpu().inner;
3861 self.inner.set_pipeline(pipeline);
3862 }
3863
3864 fn set_bind_group(
3865 &mut self,
3866 index: u32,
3867 bind_group: Option<&dispatch::DispatchBindGroup>,
3868 offsets: &[crate::DynamicOffset],
3869 ) {
3870 let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);
3871
3872 if offsets.is_empty() {
3873 self.inner.set_bind_group(index, bind_group);
3874 } else {
3875 self.inner
3876 .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(
3877 index,
3878 bind_group,
3879 offsets,
3880 0f64,
3881 offsets.len() as u32,
3882 )
3883 .unwrap();
3884 }
3885 }
3886
3887 fn set_index_buffer(
3888 &mut self,
3889 buffer: &dispatch::DispatchBuffer,
3890 index_format: crate::IndexFormat,
3891 offset: crate::BufferAddress,
3892 size: Option<crate::BufferSize>,
3893 ) {
3894 let buffer = buffer.as_webgpu();
3895 let index_format = map_index_format(index_format);
3896
3897 if let Some(size) = size {
3898 self.inner.set_index_buffer_with_f64_and_f64(
3899 &buffer.inner,
3900 index_format,
3901 offset as f64,
3902 size.get() as f64,
3903 );
3904 } else {
3905 self.inner
3906 .set_index_buffer_with_f64(&buffer.inner, index_format, offset as f64);
3907 }
3908 }
3909
3910 fn set_vertex_buffer(
3911 &mut self,
3912 slot: u32,
3913 buffer: Option<&dispatch::DispatchBuffer>,
3914 offset: crate::BufferAddress,
3915 size: Option<crate::BufferSize>,
3916 ) {
3917 let buffer = buffer.map(|buffer| &buffer.as_webgpu().inner);
3918
3919 if let Some(size) = size {
3920 self.inner.set_vertex_buffer_with_f64_and_f64(
3921 slot,
3922 buffer,
3923 offset as f64,
3924 size.get() as f64,
3925 );
3926 } else {
3927 self.inner
3928 .set_vertex_buffer_with_f64(slot, buffer, offset as f64);
3929 }
3930 }
3931
3932 fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {
3933 panic!("IMMEDIATES feature must be enabled to call set_immediates")
3934 }
3935
3936 fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
3937 self.inner
3938 .draw_with_instance_count_and_first_vertex_and_first_instance(
3939 vertices.end - vertices.start,
3940 instances.end - instances.start,
3941 vertices.start,
3942 instances.start,
3943 );
3944 }
3945
3946 fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
3947 self.inner
3948 .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(
3949 indices.end - indices.start,
3950 instances.end - instances.start,
3951 indices.start,
3952 base_vertex,
3953 instances.start,
3954 )
3955 }
3956
3957 fn draw_indirect(
3958 &mut self,
3959 indirect_buffer: &dispatch::DispatchBuffer,
3960 indirect_offset: crate::BufferAddress,
3961 ) {
3962 let buffer = indirect_buffer.as_webgpu();
3963 self.inner
3964 .draw_indirect_with_f64(&buffer.inner, indirect_offset as f64);
3965 }
3966
3967 fn draw_indexed_indirect(
3968 &mut self,
3969 indirect_buffer: &dispatch::DispatchBuffer,
3970 indirect_offset: crate::BufferAddress,
3971 ) {
3972 let buffer = indirect_buffer.as_webgpu();
3973 self.inner
3974 .draw_indexed_indirect_with_f64(&buffer.inner, indirect_offset as f64);
3975 }
3976
3977 fn finish(self, desc: &crate::RenderBundleDescriptor<'_>) -> dispatch::DispatchRenderBundle
3978 where
3979 Self: Sized,
3980 {
3981 let bundle = match desc.label {
3982 Some(label) => {
3983 let mapped_desc = webgpu_sys::GpuRenderBundleDescriptor::new();
3984 mapped_desc.set_label(label);
3985 self.inner.finish_with_descriptor(&mapped_desc)
3986 }
3987 None => self.inner.finish(),
3988 };
3989
3990 WebRenderBundle {
3991 inner: bundle,
3992 ident: crate::cmp::Identifier::create(),
3993 }
3994 .into()
3995 }
3996
3997 #[cfg(custom)]
3998 fn finish_boxed(
3999 self: Box<Self>,
4000 desc: &crate::RenderBundleDescriptor<'_>,
4001 ) -> dispatch::DispatchRenderBundle {
4002 (*self).finish(desc)
4003 }
4004}
4005impl Drop for WebRenderBundleEncoder {
4006 fn drop(&mut self) {
4007 }
4009}
4010
4011impl dispatch::RenderBundleInterface for WebRenderBundle {}
4012impl Drop for WebRenderBundle {
4013 fn drop(&mut self) {
4014 }
4016}
4017
4018fn match_media_query(query: &str) -> Option<bool> {
4021 let list = web_sys::window()?.match_media(query).ok().flatten()?;
4022 Some(list.matches())
4023}
4024
4025fn environment_color_gamut() -> Option<wgt::DisplayGamut> {
4028 if match_media_query("(color-gamut: rec2020)") == Some(true) {
4029 Some(wgt::DisplayGamut::Rec2020)
4030 } else if match_media_query("(color-gamut: p3)") == Some(true) {
4031 Some(wgt::DisplayGamut::DisplayP3)
4032 } else if match_media_query("(color-gamut: srgb)") == Some(true) {
4033 Some(wgt::DisplayGamut::Srgb)
4034 } else {
4035 None
4036 }
4037}
4038
4039mod rgba16float_probe {
4043 use super::*;
4044
4045 static RGBA16FLOAT_CANVAS_SUPPORT: AtomicU8 = AtomicU8::new(PROBE_UNKNOWN);
4056 const PROBE_UNKNOWN: u8 = 0;
4057 const PROBE_UNSUPPORTED: u8 = 1;
4058 const PROBE_SUPPORTED: u8 = 2;
4059
4060 pub(super) fn rgba16float_canvas_supported() -> bool {
4067 RGBA16FLOAT_CANVAS_SUPPORT.load(Ordering::Relaxed) != PROBE_UNSUPPORTED
4068 }
4069
4070 pub(super) fn probe_rgba16float_canvas_support(device: &webgpu_sys::GpuDevice) {
4078 if RGBA16FLOAT_CANVAS_SUPPORT.load(Ordering::Relaxed) != PROBE_UNKNOWN {
4079 return;
4080 }
4081 let Some(supported) = try_configure_rgba16float_canvas(device) else {
4084 return;
4085 };
4086 RGBA16FLOAT_CANVAS_SUPPORT.store(
4087 if supported {
4088 PROBE_SUPPORTED
4089 } else {
4090 PROBE_UNSUPPORTED
4091 },
4092 Ordering::Relaxed,
4093 );
4094 }
4095
4096 fn try_configure_rgba16float_canvas(device: &webgpu_sys::GpuDevice) -> Option<bool> {
4099 let canvas = web_sys::OffscreenCanvas::new(1, 1).ok()?;
4100 let context = canvas.get_context("webgpu").ok()??;
4101 let context: webgpu_sys::GpuCanvasContext = context.unchecked_into();
4102 let config = webgpu_sys::GpuCanvasConfiguration::new(
4103 device,
4104 map_texture_format(wgt::TextureFormat::Rgba16Float),
4105 );
4106 let supported = context.configure(&config).is_ok();
4107 if supported {
4108 context.unconfigure();
4110 }
4111 Some(supported)
4112 }
4113}
4114
4115impl dispatch::SurfaceInterface for WebSurface {
4116 fn get_capabilities(&self, _adapter: &dispatch::DispatchAdapter) -> wgt::SurfaceCapabilities {
4117 let mut formats = vec![
4118 wgt::TextureFormat::Rgba8Unorm,
4119 wgt::TextureFormat::Bgra8Unorm,
4120 ];
4121 if rgba16float_probe::rgba16float_canvas_supported() {
4124 formats.push(wgt::TextureFormat::Rgba16Float);
4125 }
4126 let mut mapped_formats = formats.iter().map(|format| map_texture_format(*format));
4127 let preferred_format = self
4130 .gpu
4131 .as_ref()
4132 .expect("Caller could not have created an adapter if gpu is undefined.")
4133 .get_preferred_canvas_format();
4134 if let Some(index) = mapped_formats.position(|format| format == preferred_format) {
4135 formats.swap(0, index);
4136 }
4137
4138 wgt::SurfaceCapabilities {
4139 format_capabilities: formats
4140 .iter()
4141 .map(|&format| {
4142 let mut color_spaces =
4146 wgt::SurfaceColorSpaces::SRGB | wgt::SurfaceColorSpaces::DISPLAY_P3;
4147 if format == wgt::TextureFormat::Rgba16Float {
4163 color_spaces |= wgt::SurfaceColorSpaces::EXTENDED_SRGB
4164 | wgt::SurfaceColorSpaces::EXTENDED_DISPLAY_P3;
4165 }
4166 wgt::SurfaceFormatCapabilities {
4167 format,
4168 color_spaces,
4169 }
4170 })
4171 .collect(),
4172 formats,
4174 present_modes: vec![wgt::PresentMode::Fifo],
4176 alpha_modes: vec![wgt::CompositeAlphaMode::Opaque],
4177 usages: wgt::TextureUsages::RENDER_ATTACHMENT
4180 | wgt::TextureUsages::COPY_SRC
4181 | wgt::TextureUsages::COPY_DST
4182 | wgt::TextureUsages::TEXTURE_BINDING,
4183 }
4184 }
4185
4186 fn display_hdr_info(&self, _adapter: &dispatch::DispatchAdapter) -> wgt::DisplayHdrInfo {
4187 let high_dynamic_range = match_media_query("(dynamic-range: high)");
4193 let gamut = environment_color_gamut();
4194
4195 let coarse = if high_dynamic_range.is_some() || gamut.is_some() {
4196 Some(wgt::DisplayCoarseRange {
4197 high_dynamic_range,
4198 gamut,
4199 })
4200 } else {
4201 None
4202 };
4203 wgt::DisplayHdrInfo {
4204 luminance: None,
4205 headroom: None,
4206 chromaticity: None,
4207 coarse,
4208 bits_per_color: None,
4209 }
4210 }
4211
4212 fn configure(&self, device: &dispatch::DispatchDevice, config: &crate::SurfaceConfiguration) {
4213 let device = device.as_webgpu();
4214
4215 match self.canvas {
4216 Canvas::Canvas(ref canvas) => {
4217 canvas.set_width(config.width);
4218 canvas.set_height(config.height);
4219 }
4220 Canvas::Offscreen(ref canvas) => {
4221 canvas.set_width(config.width);
4222 canvas.set_height(config.height);
4223 }
4224 }
4225
4226 if let wgt::PresentMode::Mailbox | wgt::PresentMode::Immediate = config.present_mode {
4227 panic!("Only FIFO/Auto* is supported on web");
4228 }
4229 if let wgt::CompositeAlphaMode::PostMultiplied | wgt::CompositeAlphaMode::Inherit =
4230 config.alpha_mode
4231 {
4232 panic!("Only Opaque/Auto or PreMultiplied alpha mode are supported on web");
4233 }
4234 let alpha_mode = match config.alpha_mode {
4235 wgt::CompositeAlphaMode::PreMultiplied => webgpu_sys::GpuCanvasAlphaMode::Premultiplied,
4236 _ => webgpu_sys::GpuCanvasAlphaMode::Opaque,
4237 };
4238 let mapped = webgpu_sys::GpuCanvasConfiguration::new(
4239 &device.inner,
4240 map_texture_format(config.format),
4241 );
4242 mapped.set_usage(config.usage.bits());
4243 mapped.set_alpha_mode(alpha_mode);
4244 match config.color_space {
4245 wgt::SurfaceColorSpace::Auto | wgt::SurfaceColorSpace::Srgb => {}
4249 wgt::SurfaceColorSpace::DisplayP3 => {
4250 js_sys::Reflect::set(
4253 &mapped,
4254 &JsValue::from_str("colorSpace"),
4255 &JsValue::from_str("display-p3"),
4256 )
4257 .expect("Setting the canvas configuration color space should never fail");
4258 }
4259 wgt::SurfaceColorSpace::ExtendedSrgb => {
4260 let tone_mapping = webgpu_sys::GpuCanvasToneMapping::new();
4266 tone_mapping.set_mode(webgpu_sys::GpuCanvasToneMappingMode::Extended);
4267 mapped.set_tone_mapping(&tone_mapping);
4268 }
4269 wgt::SurfaceColorSpace::ExtendedDisplayP3 => {
4270 js_sys::Reflect::set(
4275 &mapped,
4276 &JsValue::from_str("colorSpace"),
4277 &JsValue::from_str("display-p3"),
4278 )
4279 .expect("Setting the canvas configuration color space should never fail");
4280 let tone_mapping = webgpu_sys::GpuCanvasToneMapping::new();
4281 tone_mapping.set_mode(webgpu_sys::GpuCanvasToneMappingMode::Extended);
4282 mapped.set_tone_mapping(&tone_mapping);
4283 }
4284 cs @ (wgt::SurfaceColorSpace::ExtendedSrgbLinear
4285 | wgt::SurfaceColorSpace::Bt2100Pq
4286 | wgt::SurfaceColorSpace::Bt2100Hlg) => {
4287 self.configure_failed.set(true);
4296 log::error!(
4297 "Surface color space {cs:?} is not supported on the WebGPU backend; \
4298 the surface will report as lost. Check `get_capabilities` before \
4299 configuring."
4300 );
4301 return;
4302 }
4303 }
4304 let mapped_view_formats = config
4305 .view_formats
4306 .iter()
4307 .map(|format| {
4308 wasm_bindgen::JsValue::from(map_texture_format(*format))
4309 .dyn_into::<js_sys::JsString>()
4310 .unwrap()
4311 })
4312 .collect::<Vec<js_sys::JsString>>();
4313 mapped.set_view_formats(&mapped_view_formats);
4314 match self.context.configure(&mapped) {
4325 Ok(()) => self.configure_failed.set(false),
4326 Err(err) => {
4327 self.configure_failed.set(true);
4328 log::error!(
4329 "Surface configuration failed: {err:?}. The browser may not support \
4330 this canvas format (for example, Firefox does not yet support \
4331 `rgba16float` canvases). The surface will report as lost until it \
4332 is successfully reconfigured."
4333 );
4334 }
4335 }
4336 }
4337
4338 fn get_current_texture(
4339 &self,
4340 ) -> (
4341 Option<dispatch::DispatchTexture>,
4342 crate::SurfaceStatus,
4343 dispatch::DispatchSurfaceOutputDetail,
4344 ) {
4345 let detail = WebSurfaceOutputDetail {
4346 ident: crate::cmp::Identifier::create(),
4347 };
4348
4349 if self.configure_failed.get() {
4352 return (None, crate::SurfaceStatus::Lost, detail.into());
4353 }
4354
4355 let surface_texture = match self.context.get_current_texture() {
4356 Ok(surface_texture) => surface_texture,
4357 Err(err) => {
4358 log::error!("`getCurrentTexture` failed: {err:?}");
4359 return (None, crate::SurfaceStatus::Lost, detail.into());
4360 }
4361 };
4362
4363 let web_surface_texture = WebTexture {
4364 inner: surface_texture,
4365 drop_guard: None,
4366 ident: crate::cmp::Identifier::create(),
4367 };
4368
4369 (
4370 Some(web_surface_texture.into()),
4371 crate::SurfaceStatus::Good,
4372 detail.into(),
4373 )
4374 }
4375}
4376impl Drop for WebSurface {
4377 fn drop(&mut self) {
4378 }
4380}
4381
4382impl dispatch::SurfaceOutputDetailInterface for WebSurfaceOutputDetail {
4383 fn texture_discard(&self) {
4384 }
4386
4387 fn texture_release(&self) {
4388 }
4390}
4391impl Drop for WebSurfaceOutputDetail {
4392 fn drop(&mut self) {
4393 }
4395}
4396
4397impl WebBufferMappedRange {
4398 fn get_temporary_mapping(&self) -> &[u8] {
4399 self.temporary_mapping
4400 .get_or_init(|| self.actual_mapping.to_vec())
4401 }
4402}
4403impl dispatch::BufferMappedRangeInterface for WebBufferMappedRange {
4404 fn len(&self) -> usize {
4405 self.get_temporary_mapping().len()
4406 }
4407
4408 #[inline]
4409 unsafe fn read_slice(&self) -> &[u8] {
4410 self.get_temporary_mapping()
4411 }
4412
4413 #[inline]
4414 unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {
4415 self.temporary_mapping_modified = true;
4416 self.get_temporary_mapping();
4417 let t: &mut Vec<u8> = self.temporary_mapping.get_mut().unwrap();
4418 WriteOnly::from_mut(t)
4419 }
4420
4421 #[inline]
4422 fn as_uint8array(&self) -> &js_sys::Uint8Array {
4423 &self.actual_mapping
4424 }
4425}
4426impl Drop for WebBufferMappedRange {
4427 fn drop(&mut self) {
4428 if !self.temporary_mapping_modified {
4429 return;
4432 }
4433
4434 let temporary_mapping_slice = self.temporary_mapping.get().unwrap().as_slice();
4437 unsafe {
4438 self.actual_mapping
4441 .set(&js_sys::Uint8Array::view(temporary_mapping_slice), 0);
4442 }
4443 }
4444}
4445
4446impl dispatch::QueueWriteBufferInterface for WebQueueWriteBuffer {
4447 #[inline]
4448 fn len(&self) -> usize {
4449 self.inner.len()
4450 }
4451
4452 #[inline]
4453 unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {
4454 WriteOnly::from_mut(&mut *self.inner)
4455 }
4456}
4457impl Drop for WebQueueWriteBuffer {
4458 fn drop(&mut self) {
4459 }
4463}
4464
4465fn insert_constants_map(target: &JsValue, map: &[(&str, f64)]) {
4473 if !map.is_empty() {
4474 js_sys::Reflect::set(
4475 target,
4476 &JsValue::from_str("constants"),
4477 &hashmap_to_jsvalue(map),
4478 )
4479 .expect("Setting the values in a Javascript pipeline descriptor should never fail");
4480 }
4481}
4482
4483fn hashmap_to_jsvalue(map: &[(&str, f64)]) -> JsValue {
4485 let obj = js_sys::Object::new();
4486
4487 for &(key, v) in map.iter() {
4488 js_sys::Reflect::set(&obj, &JsValue::from_str(key), &JsValue::from_f64(v))
4489 .expect("Setting the values in a Javascript map should never fail");
4490 }
4491
4492 JsValue::from(obj)
4493}