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 pipeline_discovery: Rc::default(),
1050 }
1051 .into(),
1052 WebQueue {
1053 inner: queue,
1054 ident: crate::cmp::Identifier::create(),
1055 }
1056 .into(),
1057 )
1058 })
1059 .map_err(|error_value| crate::RequestDeviceError {
1060 inner: crate::RequestDeviceErrorKind::WebGpu(format!("{error_value:?}")),
1062 })
1063}
1064
1065fn future_pop_error_scope(
1066 result: Result<js_sys::JsNullable<webgpu_sys::GpuError>, wasm_bindgen::JsValue>,
1067) -> Option<crate::Error> {
1068 Some(crate::Error::from_js(result.ok()?.into_option()?.into()))
1069}
1070
1071fn future_compilation_info(
1072 result: Result<webgpu_sys::GpuCompilationInfo, wasm_bindgen::JsValue>,
1073 base_compilation_info: &WebShaderCompilationInfo,
1074) -> crate::CompilationInfo {
1075 let base_messages = match base_compilation_info {
1076 WebShaderCompilationInfo::Transformed { compilation_info } => {
1077 compilation_info.messages.iter().cloned()
1078 }
1079 _ => [].iter().cloned(),
1080 };
1081
1082 let messages =
1083 match result {
1084 Ok(info) => base_messages
1085 .chain(info.messages().into_iter().map(|message| {
1086 crate::CompilationMessage::from_js(message, base_compilation_info)
1087 }))
1088 .collect(),
1089 Err(_v) => base_messages
1090 .chain(core::iter::once(crate::CompilationMessage {
1091 message: "Getting compilation info failed".to_string(),
1092 message_type: crate::CompilationMessageType::Error,
1093 location: None,
1094 }))
1095 .collect(),
1096 };
1097
1098 crate::CompilationInfo { messages }
1099}
1100
1101fn register_then_closures<F, T>(
1104 promise: &Promise<js_sys::Undefined>,
1105 callback: F,
1106 success_value: T,
1107 failure_value: T,
1108) where
1109 F: FnOnce(T) + 'static,
1110 T: 'static,
1111{
1112 let rc_callback: Rc<RefCell<Option<(_, _, F)>>> = Rc::new(RefCell::new(None));
1122
1123 let rc_callback_clone1 = rc_callback.clone();
1124 let rc_callback_clone2 = rc_callback.clone();
1125
1126 let closure_success = wasm_bindgen::closure::Closure::once(move |_| {
1127 let (success_closure, rejection_closure, callback) =
1128 rc_callback_clone1.borrow_mut().take().unwrap();
1129 callback(success_value);
1130 drop((success_closure, rejection_closure));
1132 Ok(())
1133 });
1134 let closure_rejected = wasm_bindgen::closure::Closure::once(move |_| {
1135 let (success_closure, rejection_closure, callback) =
1136 rc_callback_clone2.borrow_mut().take().unwrap();
1137 callback(failure_value);
1138 drop((success_closure, rejection_closure));
1140 Ok(())
1141 });
1142
1143 let _ = promise.then_with_reject(&closure_success, &closure_rejected);
1146
1147 *rc_callback.borrow_mut() = Some((closure_success, closure_rejected, callback));
1148}
1149
1150impl ContextWebGpu {
1151 fn create_surface_from_context(
1156 &self,
1157 canvas: Canvas,
1158 context_result: Result<Option<js_sys::Object>, wasm_bindgen::JsValue>,
1159 ) -> Result<dispatch::DispatchSurface, crate::CreateSurfaceError> {
1160 let context: js_sys::Object = match context_result {
1161 Ok(Some(context)) => context,
1162 Ok(None) => {
1163 return Err(crate::CreateSurfaceError {
1170 inner: crate::CreateSurfaceErrorKind::Web(
1171 String::from(
1172 "canvas.getContext() returned null; webgpu not available or canvas already in use"
1173 )
1174 )
1175 });
1176 }
1177 Err(js_error) => {
1178 return Err(crate::CreateSurfaceError {
1181 inner: crate::CreateSurfaceErrorKind::Web(format!(
1182 "canvas.getContext() threw exception {js_error:?}",
1183 )),
1184 });
1185 }
1186 };
1187
1188 let context: webgpu_sys::GpuCanvasContext = context.unchecked_into();
1195
1196 Ok(WebSurface {
1197 gpu: self.gpu.clone(),
1198 context,
1199 canvas,
1200 configure_failed: Cell::new(false),
1201 ident: crate::cmp::Identifier::create(),
1202 }
1203 .into())
1204 }
1205}
1206
1207#[wasm_bindgen]
1212extern "C" {
1213 type Global;
1214
1215 #[wasm_bindgen(method, getter, js_name = Window)]
1216 fn window(this: &Global) -> JsValue;
1217
1218 #[wasm_bindgen(method, getter, js_name = WorkerGlobalScope)]
1219 fn worker(this: &Global) -> JsValue;
1220}
1221
1222#[derive(Debug, Clone)]
1223pub enum Canvas {
1224 Canvas(web_sys::HtmlCanvasElement),
1225 Offscreen(web_sys::OffscreenCanvas),
1226}
1227
1228#[derive(Debug, Clone, Copy)]
1229pub struct BrowserGpuPropertyInaccessible;
1230
1231pub fn get_browser_gpu_property(
1245) -> Result<Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>, BrowserGpuPropertyInaccessible> {
1246 let global: Global = js_sys::global().unchecked_into();
1247
1248 let maybe_undefined_gpu: webgpu_sys::Gpu = if !global.window().is_undefined() {
1249 let navigator = global.unchecked_into::<web_sys::Window>().navigator();
1250 ext_bindings::NavigatorGpu::gpu(&navigator)
1251 } else if !global.worker().is_undefined() {
1252 let navigator = global
1253 .unchecked_into::<web_sys::WorkerGlobalScope>()
1254 .navigator();
1255 ext_bindings::NavigatorGpu::gpu(&navigator)
1256 } else {
1257 return Err(BrowserGpuPropertyInaccessible);
1258 };
1259 Ok(DefinedNonNullJsValue::new(maybe_undefined_gpu))
1260}
1261
1262#[derive(Debug, Clone)]
1263pub struct WebAdapter {
1264 pub(crate) inner: webgpu_sys::GpuAdapter,
1265 ident: crate::cmp::Identifier,
1267}
1268
1269#[derive(Debug, Clone)]
1270pub struct WebDevice {
1271 pub(crate) inner: webgpu_sys::GpuDevice,
1272 ident: crate::cmp::Identifier,
1274 error_scope_count: Rc<Cell<u32>>,
1276 pipeline_discovery: Rc<RefCell<Option<js_sys::Array>>>,
1279}
1280
1281#[derive(Debug, Clone)]
1282pub struct WebQueue {
1283 pub(crate) inner: webgpu_sys::GpuQueue,
1284 ident: crate::cmp::Identifier,
1286}
1287
1288#[derive(Debug, Clone)]
1289pub struct WebBindGroupLayout {
1290 pub(crate) inner: webgpu_sys::GpuBindGroupLayout,
1291 ident: crate::cmp::Identifier,
1293}
1294
1295#[derive(Debug, Clone)]
1296pub struct WebBindGroup {
1297 pub(crate) inner: webgpu_sys::GpuBindGroup,
1298 ident: crate::cmp::Identifier,
1300}
1301
1302#[derive(Debug, Clone)]
1303pub struct WebTextureView {
1304 pub(crate) inner: webgpu_sys::GpuTextureView,
1305 ident: crate::cmp::Identifier,
1307}
1308
1309#[derive(Debug, Clone)]
1310pub struct WebSampler {
1311 pub(crate) inner: webgpu_sys::GpuSampler,
1312 ident: crate::cmp::Identifier,
1314}
1315
1316#[derive(Debug, Clone)]
1319struct WebBufferMapState {
1320 pub mapped_buffer: Option<js_sys::ArrayBuffer>,
1322 pub range: Range<wgt::BufferAddress>,
1324}
1325
1326#[derive(Debug, Clone)]
1331pub struct WebBuffer {
1332 pub(crate) inner: webgpu_sys::GpuBuffer,
1334 mapping: Rc<RefCell<WebBufferMapState>>,
1336 ident: crate::cmp::Identifier,
1338}
1339
1340impl WebBuffer {
1341 fn new(inner: webgpu_sys::GpuBuffer, desc: &crate::BufferDescriptor<'_>) -> Self {
1343 Self {
1344 inner,
1345 mapping: Rc::new(RefCell::new(WebBufferMapState {
1346 mapped_buffer: None,
1347 range: 0..desc.size,
1348 })),
1349 ident: crate::cmp::Identifier::create(),
1350 }
1351 }
1352
1353 fn get_mapped_range(
1355 &self,
1356 sub_range: Range<wgt::BufferAddress>,
1357 ) -> Result<js_sys::Uint8Array, crate::MapRangeError> {
1358 let mut mapping = self.mapping.borrow_mut();
1359 let range = mapping.range.clone();
1360 if mapping.mapped_buffer.is_none() {
1361 let buffer = self
1362 .inner
1363 .get_mapped_range_with_f64_and_f64(
1364 range.start as f64,
1365 (range.end - range.start) as f64,
1366 )
1367 .map_err(|e| crate::MapRangeError(format!("{e:?}")))?;
1368 mapping.mapped_buffer = Some(buffer);
1369 }
1370 let array_buffer = mapping.mapped_buffer.as_ref().unwrap();
1371 Ok(js_sys::Uint8Array::new_with_byte_offset_and_length(
1372 array_buffer,
1373 (sub_range.start - range.start) as u32,
1374 (sub_range.end - sub_range.start) as u32,
1375 ))
1376 }
1377
1378 fn set_mapped_range(&self, range: Range<wgt::BufferAddress>) {
1380 self.mapping.borrow_mut().range = range;
1381 }
1382}
1383
1384#[derive(Debug, Clone)]
1385pub struct WebTexture {
1386 pub(crate) inner: webgpu_sys::GpuTexture,
1387 drop_guard: Option<Rc<DropGuard>>,
1398 ident: crate::cmp::Identifier,
1400}
1401
1402#[derive(Debug, Clone)]
1403pub struct WebExternalTexture {
1404 ident: crate::cmp::Identifier,
1406}
1407
1408#[derive(Debug, Clone)]
1409pub struct WebBlas {
1410 ident: crate::cmp::Identifier,
1412}
1413
1414#[derive(Debug, Clone)]
1415pub struct WebTlas {
1416 ident: crate::cmp::Identifier,
1418}
1419
1420#[derive(Debug, Clone)]
1421pub struct WebQuerySet {
1422 pub(crate) inner: webgpu_sys::GpuQuerySet,
1423 ident: crate::cmp::Identifier,
1425}
1426
1427#[derive(Debug, Clone)]
1428pub struct WebPipelineLayout {
1429 pub(crate) inner: webgpu_sys::GpuPipelineLayout,
1430 ident: crate::cmp::Identifier,
1432}
1433
1434#[derive(Debug, Clone)]
1435pub struct WebRenderPipeline {
1436 pub(crate) inner: Option<webgpu_sys::GpuRenderPipeline>,
1439 ident: crate::cmp::Identifier,
1441}
1442
1443#[derive(Debug, Clone)]
1444pub struct WebComputePipeline {
1445 pub(crate) inner: webgpu_sys::GpuComputePipeline,
1446 ident: crate::cmp::Identifier,
1448}
1449
1450#[derive(Debug, Clone)]
1451pub struct WebPipelineCache {
1452 ident: crate::cmp::Identifier,
1454}
1455
1456#[derive(Debug, Clone)]
1457pub struct WebCommandEncoder {
1458 pub(crate) inner: webgpu_sys::GpuCommandEncoder,
1459 pipeline_discovery: Rc<RefCell<Option<js_sys::Array>>>,
1462 ident: crate::cmp::Identifier,
1464}
1465
1466#[derive(Debug)]
1467pub struct WebComputePassEncoder {
1468 pub(crate) inner: webgpu_sys::GpuComputePassEncoder,
1469 ident: crate::cmp::Identifier,
1471}
1472
1473#[derive(Debug)]
1474pub struct WebRenderPassEncoder {
1475 pub(crate) inner: webgpu_sys::GpuRenderPassEncoder,
1476 draws: PipelineDraws,
1479 ident: crate::cmp::Identifier,
1481}
1482
1483#[derive(Debug)]
1484pub struct WebCommandBuffer {
1485 pub(crate) inner: webgpu_sys::GpuCommandBuffer,
1486 ident: crate::cmp::Identifier,
1488}
1489
1490#[derive(Debug, Clone)]
1491pub struct WebRenderBundleEncoder {
1492 pub(crate) inner: webgpu_sys::GpuRenderBundleEncoder,
1493 draws: PipelineDraws,
1495 ident: crate::cmp::Identifier,
1497}
1498
1499#[derive(Debug, Clone)]
1500pub struct WebRenderBundle {
1501 pub(crate) inner: webgpu_sys::GpuRenderBundle,
1502 ident: crate::cmp::Identifier,
1504}
1505
1506#[derive(Debug, Clone)]
1507pub struct WebSurface {
1508 gpu: Option<DefinedNonNullJsValue<webgpu_sys::Gpu>>,
1509 canvas: Canvas,
1510 context: webgpu_sys::GpuCanvasContext,
1511 configure_failed: Cell<bool>,
1520 ident: crate::cmp::Identifier,
1522}
1523
1524#[derive(Debug, Clone)]
1525pub struct WebSurfaceOutputDetail {
1526 ident: crate::cmp::Identifier,
1528}
1529
1530#[derive(Debug)]
1531pub struct WebQueueWriteBuffer {
1532 inner: Box<[u8]>,
1533 ident: crate::cmp::Identifier,
1535}
1536
1537#[derive(Debug)]
1538pub struct WebBufferMappedRange {
1539 actual_mapping: js_sys::Uint8Array,
1540 temporary_mapping: OnceCell<Vec<u8>>,
1543 temporary_mapping_modified: bool,
1545 ident: crate::cmp::Identifier,
1547}
1548
1549impl_send_sync!(ContextWebGpu);
1550impl_send_sync!(WebAdapter);
1551impl_send_sync!(WebDevice);
1552impl_send_sync!(WebQueue);
1553impl_send_sync!(WebShaderModule);
1554impl_send_sync!(WebBindGroupLayout);
1555impl_send_sync!(WebBindGroup);
1556impl_send_sync!(WebTextureView);
1557impl_send_sync!(WebSampler);
1558impl_send_sync!(WebBuffer);
1559impl_send_sync!(WebTexture);
1560impl_send_sync!(WebExternalTexture);
1561impl_send_sync!(WebBlas);
1562impl_send_sync!(WebTlas);
1563impl_send_sync!(WebQuerySet);
1564impl_send_sync!(WebPipelineLayout);
1565impl_send_sync!(WebRenderPipeline);
1566impl_send_sync!(WebComputePipeline);
1567impl_send_sync!(WebPipelineCache);
1568impl_send_sync!(WebCommandEncoder);
1569impl_send_sync!(WebComputePassEncoder);
1570impl_send_sync!(WebRenderPassEncoder);
1571impl_send_sync!(WebCommandBuffer);
1572impl_send_sync!(WebRenderBundleEncoder);
1573impl_send_sync!(WebRenderBundle);
1574impl_send_sync!(WebSurface);
1575impl_send_sync!(WebSurfaceOutputDetail);
1576impl_send_sync!(WebQueueWriteBuffer);
1577impl_send_sync!(WebBufferMappedRange);
1578
1579crate::cmp::impl_eq_ord_hash_proxy!(ContextWebGpu => .ident);
1580crate::cmp::impl_eq_ord_hash_proxy!(WebAdapter => .ident);
1581crate::cmp::impl_eq_ord_hash_proxy!(WebDevice => .ident);
1582crate::cmp::impl_eq_ord_hash_proxy!(WebQueue => .ident);
1583crate::cmp::impl_eq_ord_hash_proxy!(WebShaderModule => .ident);
1584crate::cmp::impl_eq_ord_hash_proxy!(WebBindGroupLayout => .ident);
1585crate::cmp::impl_eq_ord_hash_proxy!(WebBindGroup => .ident);
1586crate::cmp::impl_eq_ord_hash_proxy!(WebTextureView => .ident);
1587crate::cmp::impl_eq_ord_hash_proxy!(WebSampler => .ident);
1588crate::cmp::impl_eq_ord_hash_proxy!(WebBuffer => .ident);
1589crate::cmp::impl_eq_ord_hash_proxy!(WebTexture => .ident);
1590crate::cmp::impl_eq_ord_hash_proxy!(WebExternalTexture => .ident);
1591crate::cmp::impl_eq_ord_hash_proxy!(WebBlas => .ident);
1592crate::cmp::impl_eq_ord_hash_proxy!(WebTlas => .ident);
1593crate::cmp::impl_eq_ord_hash_proxy!(WebQuerySet => .ident);
1594crate::cmp::impl_eq_ord_hash_proxy!(WebPipelineLayout => .ident);
1595crate::cmp::impl_eq_ord_hash_proxy!(WebRenderPipeline => .ident);
1596crate::cmp::impl_eq_ord_hash_proxy!(WebComputePipeline => .ident);
1597crate::cmp::impl_eq_ord_hash_proxy!(WebPipelineCache => .ident);
1598crate::cmp::impl_eq_ord_hash_proxy!(WebCommandEncoder => .ident);
1599crate::cmp::impl_eq_ord_hash_proxy!(WebComputePassEncoder => .ident);
1600crate::cmp::impl_eq_ord_hash_proxy!(WebRenderPassEncoder => .ident);
1601crate::cmp::impl_eq_ord_hash_proxy!(WebCommandBuffer => .ident);
1602crate::cmp::impl_eq_ord_hash_proxy!(WebRenderBundleEncoder => .ident);
1603crate::cmp::impl_eq_ord_hash_proxy!(WebRenderBundle => .ident);
1604crate::cmp::impl_eq_ord_hash_proxy!(WebSurface => .ident);
1605crate::cmp::impl_eq_ord_hash_proxy!(WebSurfaceOutputDetail => .ident);
1606crate::cmp::impl_eq_ord_hash_proxy!(WebQueueWriteBuffer => .ident);
1607crate::cmp::impl_eq_ord_hash_proxy!(WebBufferMappedRange => .ident);
1608
1609impl dispatch::InstanceInterface for ContextWebGpu {
1610 fn new(desc: crate::InstanceDescriptor) -> Self
1611 where
1612 Self: Sized,
1613 {
1614 let Ok(gpu) = get_browser_gpu_property() else {
1615 panic!(
1616 "Accessing the GPU is only supported on the main thread or from a dedicated worker"
1617 );
1618 };
1619
1620 ContextWebGpu {
1621 gpu,
1622 requested_backends: desc.backends,
1623 ident: crate::cmp::Identifier::create(),
1624 }
1625 }
1626
1627 unsafe fn create_surface(
1628 &self,
1629 target: crate::SurfaceTargetUnsafe,
1630 ) -> Result<dispatch::DispatchSurface, crate::CreateSurfaceError> {
1631 match target {
1632 SurfaceTargetUnsafe::RawHandle {
1633 raw_display_handle: _,
1634 raw_window_handle,
1635 } => {
1636 let canvas_element: web_sys::HtmlCanvasElement = match raw_window_handle {
1637 raw_window_handle::RawWindowHandle::Web(handle) => {
1638 let canvas_node: wasm_bindgen::JsValue = web_sys::window()
1639 .and_then(|win| win.document())
1640 .and_then(|doc| {
1641 doc.query_selector_all(&format!(
1642 "[data-raw-handle=\"{}\"]",
1643 handle.id
1644 ))
1645 .ok()
1646 })
1647 .and_then(|nodes| nodes.get(0))
1648 .expect("expected to find single canvas")
1649 .into();
1650 canvas_node.into()
1651 }
1652 raw_window_handle::RawWindowHandle::WebCanvas(handle) => {
1653 let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
1654 value.clone().unchecked_into()
1655 }
1656 raw_window_handle::RawWindowHandle::WebOffscreenCanvas(handle) => {
1657 let value: &JsValue = unsafe { handle.obj.cast().as_ref() };
1658 let canvas: web_sys::OffscreenCanvas = value.clone().unchecked_into();
1659 let context_result = canvas.get_context("webgpu");
1660
1661 return self.create_surface_from_context(
1662 Canvas::Offscreen(canvas),
1663 context_result,
1664 );
1665 }
1666 _ => panic!("expected valid handle for canvas"),
1667 };
1668
1669 let context_result = canvas_element.get_context("webgpu");
1670 self.create_surface_from_context(Canvas::Canvas(canvas_element), context_result)
1671 }
1672 }
1673 }
1674
1675 fn request_adapter(
1676 &self,
1677 options: &crate::RequestAdapterOptions<'_, '_>,
1678 ) -> Pin<Box<dyn dispatch::RequestAdapterFuture>> {
1679 let requested_backends = self.requested_backends;
1680
1681 if !(requested_backends.contains(wgt::Backends::BROWSER_WEBGPU)) {
1685 return Box::pin(core::future::ready(Err(
1686 wgt::RequestAdapterError::NotFound {
1687 active_backends: Backends::BROWSER_WEBGPU,
1688 requested_backends,
1689 supported_backends: Backends::BROWSER_WEBGPU,
1692 no_fallback_backends: Backends::default(),
1693 no_adapter_backends: Backends::default(),
1694 incompatible_surface_backends: Backends::default(),
1695 },
1696 )));
1697 }
1698 let mapped_options = webgpu_sys::GpuRequestAdapterOptions::new();
1699 let mapped_power_preference = match options.power_preference {
1700 wgt::PowerPreference::None => None,
1701 wgt::PowerPreference::LowPower => Some(webgpu_sys::GpuPowerPreference::LowPower),
1702 wgt::PowerPreference::HighPerformance => {
1703 Some(webgpu_sys::GpuPowerPreference::HighPerformance)
1704 }
1705 };
1706 if let Some(mapped_pref) = mapped_power_preference {
1707 mapped_options.set_power_preference(mapped_pref);
1708 }
1709
1710 if let Some(gpu) = &self.gpu {
1711 let adapter_promise = gpu.request_adapter_with_options(&mapped_options);
1712 Box::pin(MakeSendFuture::new(
1713 wasm_bindgen_futures::JsFuture::from(adapter_promise),
1714 move |result| future_request_adapter(result, requested_backends),
1715 ))
1716 } else {
1717 Box::pin(core::future::ready(Err(request_adapter_null_error(
1720 requested_backends,
1721 ))))
1722 }
1723 }
1724 fn enumerate_adapters(
1725 &self,
1726 _backends: crate::Backends,
1727 ) -> Pin<Box<dyn dispatch::EnumerateAdapterFuture>> {
1728 let future = self.request_adapter(&crate::RequestAdapterOptions::default());
1729 let enumerate_future = async move {
1730 let adapter = future.await;
1731 match adapter {
1732 Ok(a) => vec![a],
1733 Err(_) => vec![],
1734 }
1735 };
1736 Box::pin(enumerate_future)
1737 }
1738
1739 fn poll_all_devices(&self, _force_wait: bool) -> bool {
1740 true
1742 }
1743
1744 #[cfg(feature = "wgsl")]
1745 fn wgsl_language_features(&self) -> crate::WgslLanguageFeatures {
1746 let mut wgsl_language_features = crate::WgslLanguageFeatures::empty();
1747 if let Some(gpu) = &self.gpu {
1748 gpu.wgsl_language_features()
1749 .keys()
1750 .into_iter()
1751 .map(|wlf| wlf.expect("`WgslLanguageFeatures` elements should be valid"))
1752 .map(|wlf| {
1753 wlf.as_string()
1754 .expect("`WgslLanguageFeatures` should be string set")
1755 })
1756 .filter_map(|wlf| match wlf.as_str() {
1757 "readonly_and_readwrite_storage_textures" => {
1758 Some(crate::WgslLanguageFeatures::ReadOnlyAndReadWriteStorageTextures)
1759 }
1760 "packed_4x8_integer_dot_product" => {
1761 Some(crate::WgslLanguageFeatures::Packed4x8IntegerDotProduct)
1762 }
1763 "unrestricted_pointer_parameters" => {
1764 Some(crate::WgslLanguageFeatures::UnrestrictedPointerParameters)
1765 }
1766 "pointer_composite_access" => {
1767 Some(crate::WgslLanguageFeatures::PointerCompositeAccess)
1768 }
1769 _ => None,
1770 })
1771 .for_each(|wlf| {
1772 wgsl_language_features |= wlf;
1773 })
1774 }
1775 wgsl_language_features
1776 }
1777}
1778
1779impl Drop for ContextWebGpu {
1780 fn drop(&mut self) {
1781 }
1783}
1784
1785impl dispatch::AdapterInterface for WebAdapter {
1786 fn request_device(
1787 &self,
1788 desc: &crate::DeviceDescriptor<'_>,
1789 ) -> Pin<Box<dyn dispatch::RequestDeviceFuture>> {
1790 if !matches!(desc.trace, wgt::Trace::Off) {
1791 log::warn!("The `trace` parameter is not supported on the WebGPU backend.");
1792 }
1793
1794 let mapped_desc = webgpu_sys::GpuDeviceDescriptor::new();
1795
1796 let required_limits = map_js_sys_limits(&desc.required_limits);
1797 mapped_desc.set_required_limits(&required_limits);
1798
1799 let required_features = FEATURES_MAPPING
1800 .iter()
1801 .copied()
1802 .flat_map(|(flag, value)| {
1803 if desc.required_features.contains(flag) {
1804 Some(
1805 wasm_bindgen::JsValue::from(value)
1806 .dyn_into::<js_sys::JsString>()
1807 .unwrap(),
1808 )
1809 } else {
1810 None
1811 }
1812 })
1813 .collect::<Vec<js_sys::JsString>>();
1814 mapped_desc.set_required_features(&required_features);
1815
1816 if let Some(label) = desc.label {
1817 mapped_desc.set_label(label);
1818 }
1819
1820 let device_promise = self.inner.request_device_with_descriptor(&mapped_desc);
1821
1822 Box::pin(MakeSendFuture::new(
1823 wasm_bindgen_futures::JsFuture::from(device_promise),
1824 future_request_device,
1825 ))
1826 }
1827
1828 fn is_surface_supported(&self, _surface: &dispatch::DispatchSurface) -> bool {
1829 true
1831 }
1832
1833 fn features(&self) -> crate::Features {
1834 map_wgt_features(self.inner.features())
1835 }
1836
1837 fn limits(&self) -> crate::Limits {
1838 map_wgt_limits(self.inner.limits())
1839 }
1840
1841 fn downlevel_capabilities(&self) -> crate::DownlevelCapabilities {
1842 crate::DownlevelCapabilities::default()
1844 }
1845
1846 fn get_info(&self) -> crate::AdapterInfo {
1847 map_adapter_info(&self.inner.info())
1848 }
1849
1850 fn get_texture_format_features(
1851 &self,
1852 format: crate::TextureFormat,
1853 ) -> crate::TextureFormatFeatures {
1854 format.guaranteed_format_features(dispatch::AdapterInterface::features(self))
1855 }
1856
1857 fn get_presentation_timestamp(&self) -> crate::PresentationTimestamp {
1858 crate::PresentationTimestamp::INVALID_TIMESTAMP
1859 }
1860
1861 fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1862 Vec::new()
1863 }
1864}
1865impl Drop for WebAdapter {
1866 fn drop(&mut self) {
1867 }
1869}
1870
1871#[derive(Debug, Clone, Copy)]
1873struct PipelineDraws {
1874 discovering: bool,
1876 skipping: bool,
1878}
1879
1880impl PipelineDraws {
1881 fn new(discovering: bool) -> Self {
1882 Self {
1883 discovering,
1884 skipping: false,
1885 }
1886 }
1887
1888 fn set<'a>(
1891 &mut self,
1892 pipeline: &'a dispatch::DispatchRenderPipeline,
1893 ) -> Option<&'a webgpu_sys::GpuRenderPipeline> {
1894 let pipeline = pipeline.as_webgpu().inner.as_ref().filter(|_| !self.discovering);
1895 self.skipping = pipeline.is_none();
1896 pipeline
1897 }
1898}
1899
1900#[derive(Debug)]
1903pub struct DiscoveredPipelines(#[allow(dead_code)] JsValue);
1904
1905impl WebDevice {
1906 pub(crate) fn begin_pipeline_discovery(&self) {
1908 *self.pipeline_discovery.borrow_mut() = Some(js_sys::Array::new());
1909 }
1910
1911 pub(crate) fn end_pipeline_discovery(
1913 &self,
1914 ) -> impl Future<Output = DiscoveredPipelines> + 'static {
1915 let pending = self
1916 .pipeline_discovery
1917 .borrow_mut()
1918 .take()
1919 .unwrap_or_else(js_sys::Array::new);
1920 let settled = wasm_bindgen_futures::JsFuture::from(Promise::all_settled(&pending));
1921 async move { DiscoveredPipelines(settled.await.unwrap_or_else(|error| error)) }
1922 }
1923
1924 pub(crate) fn wrap_external_texture(
1938 &self,
1939 texture: webgpu_sys::GpuTexture,
1940 drop_callback: Option<DropCallback>,
1941 ) -> dispatch::DispatchTexture {
1942 WebTexture {
1943 inner: texture,
1944 drop_guard: Some(Rc::new(DropGuard::new(drop_callback))),
1945 ident: crate::cmp::Identifier::create(),
1946 }
1947 .into()
1948 }
1949}
1950
1951impl dispatch::DeviceInterface for WebDevice {
1952 fn features(&self) -> crate::Features {
1953 map_wgt_features(self.inner.features())
1954 }
1955
1956 fn limits(&self) -> crate::Limits {
1957 map_wgt_limits(self.inner.limits())
1958 }
1959
1960 fn adapter_info(&self) -> crate::AdapterInfo {
1961 map_adapter_info(&self.inner.adapter_info())
1962 }
1963
1964 fn create_shader_module(
1965 &self,
1966 desc: crate::ShaderModuleDescriptor<'_>,
1967 _shader_runtime_checks: crate::ShaderRuntimeChecks,
1968 ) -> dispatch::DispatchShaderModule {
1969 let shader_module_result = match desc.source {
1970 #[cfg(feature = "spirv")]
1971 crate::ShaderSource::SpirV(ref spv) => {
1972 use naga::front;
1973
1974 let options = naga::front::spv::Options {
1975 adjust_coordinate_space: false,
1976 strict_capabilities: true,
1977 block_ctx_dump_prefix: None,
1978 };
1979 let spv_parser = front::spv::Frontend::new(spv.iter().cloned(), &options);
1980 spv_parser
1981 .parse()
1982 .map_err(|inner| {
1983 crate::CompilationInfo::from(naga::error::ShaderError {
1984 source: String::new(),
1985 label: desc.label.map(|s| s.to_string()),
1986 inner: Box::new(inner),
1987 })
1988 })
1989 .and_then(|spv_module| {
1990 validate_transformed_shader_module(&spv_module, "", &desc).map(|v| {
1991 (
1992 v,
1993 WebShaderCompilationInfo::Transformed {
1994 compilation_info: crate::CompilationInfo { messages: vec![] },
1995 },
1996 )
1997 })
1998 })
1999 }
2000 #[cfg(feature = "glsl")]
2001 crate::ShaderSource::Glsl {
2002 ref shader,
2003 stage,
2004 defines,
2005 } => {
2006 use naga::front;
2007
2008 let options = front::glsl::Options {
2010 stage,
2011 defines: defines
2012 .iter()
2013 .map(|&(key, value)| (String::from(key), String::from(value)))
2014 .collect(),
2015 };
2016 let mut parser = front::glsl::Frontend::default();
2017 parser
2018 .parse(&options, shader)
2019 .map_err(|inner| {
2020 crate::CompilationInfo::from(naga::error::ShaderError {
2021 source: shader.to_string(),
2022 label: desc.label.map(|s| s.to_string()),
2023 inner: Box::new(inner),
2024 })
2025 })
2026 .and_then(|glsl_module| {
2027 validate_transformed_shader_module(&glsl_module, shader, &desc).map(|v| {
2028 (
2029 v,
2030 WebShaderCompilationInfo::Transformed {
2031 compilation_info: crate::CompilationInfo { messages: vec![] },
2032 },
2033 )
2034 })
2035 })
2036 }
2037 #[cfg(feature = "wgsl")]
2038 crate::ShaderSource::Wgsl(ref code) => {
2039 let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(code);
2040 Ok((
2041 shader_module,
2042 WebShaderCompilationInfo::Wgsl {
2043 source: code.to_string(),
2044 },
2045 ))
2046 }
2047 #[cfg(feature = "naga-ir")]
2048 crate::ShaderSource::Naga(ref module) => {
2049 validate_transformed_shader_module(module, "", &desc).map(|v| {
2050 (
2051 v,
2052 WebShaderCompilationInfo::Transformed {
2053 compilation_info: crate::CompilationInfo { messages: vec![] },
2054 },
2055 )
2056 })
2057 }
2058 crate::ShaderSource::Dummy(_) => {
2059 panic!("found `ShaderSource::Dummy`")
2060 }
2061 };
2062
2063 #[cfg(naga)]
2064 fn validate_transformed_shader_module(
2065 module: &naga::Module,
2066 source: &str,
2067 desc: &crate::ShaderModuleDescriptor<'_>,
2068 ) -> Result<webgpu_sys::GpuShaderModuleDescriptor, crate::CompilationInfo> {
2069 use naga::{back, valid};
2070 let mut validator =
2071 valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all());
2072 let module_info = validator.validate(module).map_err(|err| {
2073 crate::CompilationInfo::from(naga::error::ShaderError {
2074 source: source.to_string(),
2075 label: desc.label.map(|s| s.to_string()),
2076 inner: Box::new(err),
2077 })
2078 })?;
2079
2080 let writer_flags = naga::back::wgsl::WriterFlags::empty();
2081 let wgsl_text = back::wgsl::write_string(module, &module_info, writer_flags).unwrap();
2082 Ok(webgpu_sys::GpuShaderModuleDescriptor::new(
2083 wgsl_text.as_str(),
2084 ))
2085 }
2086 let (descriptor, compilation_info) = match shader_module_result {
2087 Ok(v) => v,
2088 Err(compilation_info) => (
2089 webgpu_sys::GpuShaderModuleDescriptor::new(""),
2090 WebShaderCompilationInfo::Transformed { compilation_info },
2091 ),
2092 };
2093 if let Some(label) = desc.label {
2094 descriptor.set_label(label);
2095 }
2096 WebShaderModule {
2097 module: self.inner.create_shader_module(&descriptor),
2098 compilation_info,
2099 ident: crate::cmp::Identifier::create(),
2100 }
2101 .into()
2102 }
2103
2104 unsafe fn create_shader_module_passthrough(
2105 &self,
2106 desc: &crate::ShaderModuleDescriptorPassthrough<'_>,
2107 ) -> dispatch::DispatchShaderModule {
2108 let shader_module_result = if let Some(ref code) = desc.wgsl {
2109 let shader_module = webgpu_sys::GpuShaderModuleDescriptor::new(code);
2110 Ok((
2111 shader_module,
2112 WebShaderCompilationInfo::Wgsl {
2113 source: code.to_string(),
2114 },
2115 ))
2116 } else {
2117 Err(crate::CompilationInfo {
2118 messages: vec![crate::CompilationMessage {
2119 message:
2120 "Passthrough shader not compiled for WGSL on WebGPU backend (wgpu error)"
2121 .to_string(),
2122 location: None,
2123 message_type: crate::CompilationMessageType::Error,
2124 }],
2125 })
2126 };
2127 let (descriptor, compilation_info) = match shader_module_result {
2128 Ok(v) => v,
2129 Err(compilation_info) => (
2130 webgpu_sys::GpuShaderModuleDescriptor::new(""),
2131 WebShaderCompilationInfo::Transformed { compilation_info },
2132 ),
2133 };
2134 if let Some(label) = desc.label {
2135 descriptor.set_label(label);
2136 }
2137 WebShaderModule {
2138 module: self.inner.create_shader_module(&descriptor),
2139 compilation_info,
2140 ident: crate::cmp::Identifier::create(),
2141 }
2142 .into()
2143 }
2144
2145 fn create_bind_group_layout(
2146 &self,
2147 desc: &crate::BindGroupLayoutDescriptor<'_>,
2148 ) -> dispatch::DispatchBindGroupLayout {
2149 let mapped_bindings = desc
2150 .entries
2151 .iter()
2152 .map(|bind| {
2153 let mapped_entry =
2154 webgpu_sys::GpuBindGroupLayoutEntry::new(bind.binding, bind.visibility.bits());
2155
2156 match bind.ty {
2157 wgt::BindingType::Buffer {
2158 ty,
2159 has_dynamic_offset,
2160 min_binding_size,
2161 } => {
2162 let buffer = webgpu_sys::GpuBufferBindingLayout::new();
2163 buffer.set_has_dynamic_offset(has_dynamic_offset);
2164 if let Some(size) = min_binding_size {
2165 buffer.set_min_binding_size_f64(size.get() as f64);
2166 }
2167 buffer.set_type(match ty {
2168 wgt::BufferBindingType::Uniform => {
2169 webgpu_sys::GpuBufferBindingType::Uniform
2170 }
2171 wgt::BufferBindingType::Storage { read_only: false } => {
2172 webgpu_sys::GpuBufferBindingType::Storage
2173 }
2174 wgt::BufferBindingType::Storage { read_only: true } => {
2175 webgpu_sys::GpuBufferBindingType::ReadOnlyStorage
2176 }
2177 });
2178 mapped_entry.set_buffer(&buffer);
2179 }
2180 wgt::BindingType::Sampler(ty) => {
2181 let sampler = webgpu_sys::GpuSamplerBindingLayout::new();
2182 sampler.set_type(match ty {
2183 wgt::SamplerBindingType::NonFiltering => {
2184 webgpu_sys::GpuSamplerBindingType::NonFiltering
2185 }
2186 wgt::SamplerBindingType::Filtering => {
2187 webgpu_sys::GpuSamplerBindingType::Filtering
2188 }
2189 wgt::SamplerBindingType::Comparison => {
2190 webgpu_sys::GpuSamplerBindingType::Comparison
2191 }
2192 });
2193 mapped_entry.set_sampler(&sampler);
2194 }
2195 wgt::BindingType::Texture {
2196 multisampled,
2197 sample_type,
2198 view_dimension,
2199 } => {
2200 let texture = webgpu_sys::GpuTextureBindingLayout::new();
2201 texture.set_multisampled(multisampled);
2202 texture.set_sample_type(map_texture_component_type(sample_type));
2203 texture.set_view_dimension(map_texture_view_dimension(view_dimension));
2204 mapped_entry.set_texture(&texture);
2205 }
2206 wgt::BindingType::StorageTexture {
2207 access,
2208 format,
2209 view_dimension,
2210 } => {
2211 let mapped_access = match access {
2212 wgt::StorageTextureAccess::WriteOnly => {
2213 webgpu_sys::GpuStorageTextureAccess::WriteOnly
2214 }
2215 wgt::StorageTextureAccess::ReadOnly => {
2216 webgpu_sys::GpuStorageTextureAccess::ReadOnly
2217 }
2218 wgt::StorageTextureAccess::ReadWrite => {
2219 webgpu_sys::GpuStorageTextureAccess::ReadWrite
2220 }
2221 wgt::StorageTextureAccess::Atomic => {
2222 unreachable!()
2224 }
2225 };
2226 let storage_texture = webgpu_sys::GpuStorageTextureBindingLayout::new(
2227 map_texture_format(format),
2228 );
2229 storage_texture.set_access(mapped_access);
2230 storage_texture
2231 .set_view_dimension(map_texture_view_dimension(view_dimension));
2232 mapped_entry.set_storage_texture(&storage_texture);
2233 }
2234 wgt::BindingType::AccelerationStructure { .. } => todo!(),
2235 wgt::BindingType::ExternalTexture => {
2236 mapped_entry.set_external_texture(
2237 &webgpu_sys::GpuExternalTextureBindingLayout::new(),
2238 );
2239 }
2240 }
2241
2242 mapped_entry
2243 })
2244 .collect::<Vec<webgpu_sys::GpuBindGroupLayoutEntry>>();
2245
2246 let mapped_desc = webgpu_sys::GpuBindGroupLayoutDescriptor::new(&mapped_bindings);
2247 if let Some(label) = desc.label {
2248 mapped_desc.set_label(label);
2249 }
2250 let bind_group_layout = self.inner.create_bind_group_layout(&mapped_desc).unwrap();
2251
2252 WebBindGroupLayout {
2253 inner: bind_group_layout,
2254 ident: crate::cmp::Identifier::create(),
2255 }
2256 .into()
2257 }
2258
2259 fn create_bind_group(
2260 &self,
2261 desc: &crate::BindGroupDescriptor<'_>,
2262 ) -> dispatch::DispatchBindGroup {
2263 let mapped_entries = desc
2264 .entries
2265 .iter()
2266 .map(|binding| match binding.resource {
2267 crate::BindingResource::Buffer(crate::BufferBinding {
2268 buffer,
2269 offset,
2270 size,
2271 }) => {
2272 let buffer = buffer.inner.as_webgpu();
2273 let mapped_buffer_binding = webgpu_sys::GpuBufferBinding::new(&buffer.inner);
2274 mapped_buffer_binding.set_offset_f64(offset as f64);
2275 if let Some(s) = size {
2276 mapped_buffer_binding.set_size_f64(s.get() as f64);
2277 }
2278
2279 webgpu_sys::GpuBindGroupEntry::new_with_gpu_buffer_binding(
2280 binding.binding,
2281 &mapped_buffer_binding,
2282 )
2283 }
2284 crate::BindingResource::BufferArray(..) => {
2285 panic!("Web backend does not support arrays of buffers")
2286 }
2287 crate::BindingResource::Sampler(sampler) => {
2288 let sampler = &sampler.inner.as_webgpu().inner;
2289 webgpu_sys::GpuBindGroupEntry::new(binding.binding, sampler)
2290 }
2291 crate::BindingResource::SamplerArray(..) => {
2292 panic!("Web backend does not support arrays of samplers")
2293 }
2294 crate::BindingResource::TextureView(texture_view) => {
2295 let texture_view = &texture_view.inner.as_webgpu().inner;
2296 webgpu_sys::GpuBindGroupEntry::new_with_gpu_texture_view(
2297 binding.binding,
2298 texture_view,
2299 )
2300 }
2301 crate::BindingResource::TextureViewArray(..) => {
2302 panic!("Web backend does not support BINDING_INDEXING extension")
2303 }
2304 crate::BindingResource::AccelerationStructure(_) => {
2305 unimplemented!("Raytracing not implemented for web")
2306 }
2307 crate::BindingResource::AccelerationStructureArray(_) => {
2308 unimplemented!("Raytracing not implemented for web")
2309 }
2310 crate::BindingResource::ExternalTexture(_) => {
2311 unimplemented!("ExternalTexture not implemented for web")
2312 }
2313 })
2314 .collect::<Vec<webgpu_sys::GpuBindGroupEntry>>();
2315
2316 let bgl = &desc.layout.inner.as_webgpu().inner;
2317 let mapped_desc = webgpu_sys::GpuBindGroupDescriptor::new(&mapped_entries, bgl);
2318 if let Some(label) = desc.label {
2319 mapped_desc.set_label(label);
2320 }
2321 let bind_group = self.inner.create_bind_group(&mapped_desc);
2322
2323 WebBindGroup {
2324 inner: bind_group,
2325 ident: crate::cmp::Identifier::create(),
2326 }
2327 .into()
2328 }
2329
2330 fn create_pipeline_layout(
2331 &self,
2332 desc: &crate::PipelineLayoutDescriptor<'_>,
2333 ) -> dispatch::DispatchPipelineLayout {
2334 let temp_layouts = desc
2335 .bind_group_layouts
2336 .iter()
2337 .map(|bgl| match bgl {
2338 Some(bgl) => js_sys::JsNullable::wrap(bgl.inner.as_webgpu().inner.clone()),
2339 None => js_sys::JsNullable::new(),
2340 })
2341 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuBindGroupLayout>>>();
2342 let mapped_desc = webgpu_sys::GpuPipelineLayoutDescriptor::new(&temp_layouts);
2343 if let Some(label) = desc.label {
2344 mapped_desc.set_label(label);
2345 }
2346
2347 let pipeline_layout = self.inner.create_pipeline_layout(&mapped_desc);
2348
2349 WebPipelineLayout {
2350 inner: pipeline_layout,
2351 ident: crate::cmp::Identifier::create(),
2352 }
2353 .into()
2354 }
2355
2356 fn create_render_pipeline(
2357 &self,
2358 desc: &crate::RenderPipelineDescriptor<'_>,
2359 ) -> dispatch::DispatchRenderPipeline {
2360 let module = desc.vertex.module.inner.as_webgpu();
2361 let mapped_vertex_state = webgpu_sys::GpuVertexState::new(&module.module);
2362 insert_constants_map(
2363 &mapped_vertex_state,
2364 desc.vertex.compilation_options.constants,
2365 );
2366 if let Some(ep) = desc.vertex.entry_point {
2367 mapped_vertex_state.set_entry_point(ep);
2368 }
2369
2370 let buffers = desc
2371 .vertex
2372 .buffers
2373 .iter()
2374 .map(|vbuf| match vbuf {
2375 Some(vbuf) => {
2376 let mapped_attributes = vbuf
2377 .attributes
2378 .iter()
2379 .map(|attr| {
2380 webgpu_sys::GpuVertexAttribute::new_with_f64(
2381 map_vertex_format(attr.format),
2382 attr.offset as f64,
2383 attr.shader_location,
2384 )
2385 })
2386 .collect::<Vec<webgpu_sys::GpuVertexAttribute>>();
2387
2388 let mapped_vbuf = webgpu_sys::GpuVertexBufferLayout::new_with_f64(
2389 vbuf.array_stride as f64,
2390 &mapped_attributes,
2391 );
2392 mapped_vbuf.set_step_mode(map_vertex_step_mode(vbuf.step_mode));
2393 js_sys::JsNullable::wrap(mapped_vbuf)
2394 }
2395 None => js_sys::JsNullable::new(),
2396 })
2397 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuVertexBufferLayout>>>();
2398
2399 mapped_vertex_state.set_buffers(&buffers);
2400
2401 let mapped_desc = match desc.layout {
2402 Some(layout) => webgpu_sys::GpuRenderPipelineDescriptor::new(
2403 &layout.inner.as_webgpu().inner,
2404 &mapped_vertex_state,
2405 ),
2406 None => webgpu_sys::GpuRenderPipelineDescriptor::new_with_gpu_auto_layout_mode(
2407 webgpu_sys::GpuAutoLayoutMode::Auto,
2408 &mapped_vertex_state,
2409 ),
2410 };
2411
2412 if let Some(label) = desc.label {
2413 mapped_desc.set_label(label);
2414 }
2415
2416 if let Some(ref depth_stencil) = desc.depth_stencil {
2417 mapped_desc.set_depth_stencil(&map_depth_stencil_state(depth_stencil));
2418 }
2419
2420 if let Some(ref frag) = desc.fragment {
2421 let targets = frag
2422 .targets
2423 .iter()
2424 .map(|target| match target {
2425 Some(target) => {
2426 let mapped_format = map_texture_format(target.format);
2427 let mapped_color_state =
2428 webgpu_sys::GpuColorTargetState::new(mapped_format);
2429 if let Some(ref bs) = target.blend {
2430 let alpha = map_blend_component(&bs.alpha);
2431 let color = map_blend_component(&bs.color);
2432 let mapped_blend_state = webgpu_sys::GpuBlendState::new(&alpha, &color);
2433 mapped_color_state.set_blend(&mapped_blend_state);
2434 }
2435 mapped_color_state.set_write_mask(target.write_mask.bits());
2436 js_sys::JsNullable::wrap(mapped_color_state)
2437 }
2438 None => js_sys::JsNullable::new(),
2439 })
2440 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuColorTargetState>>>();
2441 let module = frag.module.inner.as_webgpu();
2442 let mapped_fragment_desc = webgpu_sys::GpuFragmentState::new(&module.module, &targets);
2443 insert_constants_map(&mapped_fragment_desc, frag.compilation_options.constants);
2444 if let Some(ep) = frag.entry_point {
2445 mapped_fragment_desc.set_entry_point(ep);
2446 }
2447 mapped_desc.set_fragment(&mapped_fragment_desc);
2448 }
2449
2450 let mapped_multisample = webgpu_sys::GpuMultisampleState::new();
2451 mapped_multisample.set_count(desc.multisample.count);
2452 mapped_multisample.set_mask(desc.multisample.mask as u32);
2453 mapped_multisample
2454 .set_alpha_to_coverage_enabled(desc.multisample.alpha_to_coverage_enabled);
2455 mapped_desc.set_multisample(&mapped_multisample);
2456
2457 let mapped_primitive = map_primitive_state(&desc.primitive);
2458 mapped_desc.set_primitive(&mapped_primitive);
2459
2460 let discovered = self.pipeline_discovery.borrow().as_ref().map(|pending| {
2463 pending.push(&self.inner.create_render_pipeline_async(&mapped_desc));
2464 });
2465 let render_pipeline = match discovered {
2466 Some(()) => None,
2467 None => Some(self.inner.create_render_pipeline(&mapped_desc).unwrap()),
2468 };
2469
2470 WebRenderPipeline {
2471 inner: render_pipeline,
2472 ident: crate::cmp::Identifier::create(),
2473 }
2474 .into()
2475 }
2476
2477 fn create_mesh_pipeline(
2478 &self,
2479 _desc: &crate::MeshPipelineDescriptor<'_>,
2480 ) -> dispatch::DispatchRenderPipeline {
2481 panic!("MESH_SHADER feature must be enabled to call create_mesh_pipeline")
2482 }
2483
2484 fn create_compute_pipeline(
2485 &self,
2486 desc: &crate::ComputePipelineDescriptor<'_>,
2487 ) -> dispatch::DispatchComputePipeline {
2488 let shader_module = desc.module.inner.as_webgpu();
2489 let mapped_compute_stage = webgpu_sys::GpuProgrammableStage::new(&shader_module.module);
2490 insert_constants_map(&mapped_compute_stage, desc.compilation_options.constants);
2491 if let Some(ep) = desc.entry_point {
2492 mapped_compute_stage.set_entry_point(ep);
2493 }
2494 let mapped_desc = match desc.layout {
2495 Some(layout) => webgpu_sys::GpuComputePipelineDescriptor::new(
2496 &layout.inner.as_webgpu().inner,
2497 &mapped_compute_stage,
2498 ),
2499 None => webgpu_sys::GpuComputePipelineDescriptor::new_with_gpu_auto_layout_mode(
2500 webgpu_sys::GpuAutoLayoutMode::Auto,
2501 &mapped_compute_stage,
2502 ),
2503 };
2504
2505 if let Some(label) = desc.label {
2506 mapped_desc.set_label(label);
2507 }
2508
2509 let compute_pipeline = self.inner.create_compute_pipeline(&mapped_desc);
2510
2511 WebComputePipeline {
2512 inner: compute_pipeline,
2513 ident: crate::cmp::Identifier::create(),
2514 }
2515 .into()
2516 }
2517
2518 unsafe fn create_pipeline_cache(
2519 &self,
2520 _desc: &crate::PipelineCacheDescriptor<'_>,
2521 ) -> dispatch::DispatchPipelineCache {
2522 WebPipelineCache {
2523 ident: crate::cmp::Identifier::create(),
2524 }
2525 .into()
2526 }
2527
2528 fn create_buffer(&self, desc: &crate::BufferDescriptor<'_>) -> dispatch::DispatchBuffer {
2529 let mapped_desc =
2530 webgpu_sys::GpuBufferDescriptor::new_with_f64(desc.size as f64, desc.usage.bits());
2531 mapped_desc.set_mapped_at_creation(desc.mapped_at_creation);
2532 if let Some(label) = desc.label {
2533 mapped_desc.set_label(label);
2534 }
2535 WebBuffer::new(self.inner.create_buffer(&mapped_desc).unwrap(), desc).into()
2536 }
2537
2538 fn create_texture(&self, desc: &crate::TextureDescriptor<'_>) -> dispatch::DispatchTexture {
2539 let mapped_desc = webgpu_sys::GpuTextureDescriptor::new_with_gpu_extent_3d_dict(
2540 map_texture_format(desc.format),
2541 &map_extent_3d(desc.size),
2542 desc.usage.bits(),
2543 );
2544 if let Some(label) = desc.label {
2545 mapped_desc.set_label(label);
2546 }
2547 mapped_desc.set_dimension(map_texture_dimension(desc.dimension));
2548 mapped_desc.set_mip_level_count(desc.mip_level_count);
2549 mapped_desc.set_sample_count(desc.sample_count);
2550 let mapped_view_formats = desc
2551 .view_formats
2552 .iter()
2553 .map(|format| {
2554 wasm_bindgen::JsValue::from(map_texture_format(*format))
2555 .dyn_into::<js_sys::JsString>()
2556 .unwrap()
2557 })
2558 .collect::<Vec<js_sys::JsString>>();
2559 mapped_desc.set_view_formats(&mapped_view_formats);
2560
2561 let texture = self.inner.create_texture(&mapped_desc).unwrap();
2562 WebTexture {
2563 inner: texture,
2564 drop_guard: None,
2565 ident: crate::cmp::Identifier::create(),
2566 }
2567 .into()
2568 }
2569
2570 fn create_external_texture(
2571 &self,
2572 _desc: &crate::ExternalTextureDescriptor<'_>,
2573 _planes: &[&crate::TextureView],
2574 ) -> dispatch::DispatchExternalTexture {
2575 unimplemented!("ExternalTexture not implemented for web");
2576 }
2577
2578 fn create_blas(
2579 &self,
2580 _desc: &crate::CreateBlasDescriptor<'_>,
2581 _sizes: crate::BlasGeometrySizeDescriptors,
2582 ) -> (Option<u64>, dispatch::DispatchBlas) {
2583 unimplemented!("Raytracing not implemented for web");
2584 }
2585
2586 fn create_tlas(&self, _desc: &crate::CreateTlasDescriptor<'_>) -> dispatch::DispatchTlas {
2587 unimplemented!("Raytracing not implemented for web");
2588 }
2589
2590 fn create_sampler(&self, desc: &crate::SamplerDescriptor<'_>) -> dispatch::DispatchSampler {
2591 let mapped_desc = webgpu_sys::GpuSamplerDescriptor::new();
2592 mapped_desc.set_address_mode_u(map_address_mode(desc.address_mode_u));
2593 mapped_desc.set_address_mode_v(map_address_mode(desc.address_mode_v));
2594 mapped_desc.set_address_mode_w(map_address_mode(desc.address_mode_w));
2595 if let Some(compare) = desc.compare {
2596 mapped_desc.set_compare(map_compare_function(compare));
2597 }
2598 mapped_desc.set_lod_max_clamp(desc.lod_max_clamp);
2599 mapped_desc.set_lod_min_clamp(desc.lod_min_clamp);
2600 mapped_desc.set_mag_filter(map_filter_mode(desc.mag_filter));
2601 mapped_desc.set_min_filter(map_filter_mode(desc.min_filter));
2602 mapped_desc.set_mipmap_filter(map_mipmap_filter_mode(desc.mipmap_filter));
2603 mapped_desc.set_max_anisotropy(desc.anisotropy_clamp);
2604 if let Some(label) = desc.label {
2605 mapped_desc.set_label(label);
2606 }
2607
2608 let sampler = self.inner.create_sampler_with_descriptor(&mapped_desc);
2609
2610 WebSampler {
2611 inner: sampler,
2612 ident: crate::cmp::Identifier::create(),
2613 }
2614 .into()
2615 }
2616
2617 fn create_query_set(&self, desc: &crate::QuerySetDescriptor<'_>) -> dispatch::DispatchQuerySet {
2618 let ty = match desc.ty {
2619 wgt::QueryType::Occlusion => webgpu_sys::GpuQueryType::Occlusion,
2620 wgt::QueryType::Timestamp => webgpu_sys::GpuQueryType::Timestamp,
2621 wgt::QueryType::PipelineStatistics(_) => unreachable!(),
2622 };
2623 let mapped_desc = webgpu_sys::GpuQuerySetDescriptor::new(desc.count, ty);
2624 if let Some(label) = desc.label {
2625 mapped_desc.set_label(label);
2626 }
2627
2628 let query_set = self.inner.create_query_set(&mapped_desc).unwrap();
2629
2630 WebQuerySet {
2631 inner: query_set,
2632 ident: crate::cmp::Identifier::create(),
2633 }
2634 .into()
2635 }
2636
2637 fn create_command_encoder(
2638 &self,
2639 desc: &crate::CommandEncoderDescriptor<'_>,
2640 ) -> dispatch::DispatchCommandEncoder {
2641 let mapped_desc = webgpu_sys::GpuCommandEncoderDescriptor::new();
2642 if let Some(label) = desc.label {
2643 mapped_desc.set_label(label);
2644 }
2645
2646 let command_encoder = self
2647 .inner
2648 .create_command_encoder_with_descriptor(&mapped_desc);
2649
2650 WebCommandEncoder {
2651 inner: command_encoder,
2652 pipeline_discovery: Rc::clone(&self.pipeline_discovery),
2653 ident: crate::cmp::Identifier::create(),
2654 }
2655 .into()
2656 }
2657
2658 fn create_render_bundle_encoder(
2659 &self,
2660 desc: &crate::RenderBundleEncoderDescriptor<'_>,
2661 ) -> dispatch::DispatchRenderBundleEncoder {
2662 let mapped_color_formats = desc
2663 .color_formats
2664 .iter()
2665 .map(|cf| match cf {
2666 Some(cf) => js_sys::JsNullable::wrap(
2667 wasm_bindgen::JsValue::from(map_texture_format(*cf))
2668 .dyn_into::<js_sys::JsString>()
2669 .unwrap(),
2670 ),
2671 None => js_sys::JsNullable::new(),
2672 })
2673 .collect::<Vec<js_sys::JsNullable<js_sys::JsString>>>();
2674 let mapped_desc = webgpu_sys::GpuRenderBundleEncoderDescriptor::new(&mapped_color_formats);
2675 if let Some(label) = desc.label {
2676 mapped_desc.set_label(label);
2677 }
2678 if let Some(ds) = desc.depth_stencil {
2679 mapped_desc.set_depth_stencil_format(map_texture_format(ds.format));
2680 mapped_desc.set_depth_read_only(ds.depth_read_only);
2681 mapped_desc.set_stencil_read_only(ds.stencil_read_only);
2682 }
2683 mapped_desc.set_sample_count(desc.sample_count);
2684
2685 let render_bundle_encoder = self
2686 .inner
2687 .create_render_bundle_encoder(&mapped_desc)
2688 .unwrap();
2689
2690 WebRenderBundleEncoder {
2691 inner: render_bundle_encoder,
2692 draws: PipelineDraws::new(self.pipeline_discovery.borrow().is_some()),
2693 ident: crate::cmp::Identifier::create(),
2694 }
2695 .into()
2696 }
2697
2698 fn set_device_lost_callback(&self, device_lost_callback: dispatch::BoxDeviceLostCallback) {
2699 let closure = Closure::once(move |info: webgpu_sys::GpuDeviceLostInfo| {
2700 device_lost_callback(
2701 match info.reason() {
2702 webgpu_sys::GpuDeviceLostReason::Destroyed => {
2703 crate::DeviceLostReason::Destroyed
2704 }
2705 webgpu_sys::GpuDeviceLostReason::Unknown => crate::DeviceLostReason::Unknown,
2706 _ => crate::DeviceLostReason::Unknown,
2707 },
2708 info.message(),
2709 );
2710 });
2711 let _ = self.inner.lost().then(&closure);
2712 closure.forget();
2715 }
2716
2717 fn on_uncaptured_error(&self, handler: Arc<dyn crate::UncapturedErrorHandler>) {
2718 let f = Closure::wrap(Box::new(move |event: webgpu_sys::GpuUncapturedErrorEvent| {
2719 let error = crate::Error::from_js(event.error().value_of());
2720 handler(error);
2721 }) as Box<dyn FnMut(_)>);
2722 self.inner
2723 .set_onuncapturederror(Some(f.as_ref().unchecked_ref()));
2724 f.forget();
2727 }
2728
2729 fn push_error_scope(&self, filter: crate::ErrorFilter) -> u32 {
2730 let index = self.error_scope_count.get();
2731 self.error_scope_count.set(
2732 index
2733 .checked_add(1)
2734 .expect("Greater than 2^32 nested error scopes"),
2735 );
2736 self.inner.push_error_scope(match filter {
2737 crate::ErrorFilter::OutOfMemory => webgpu_sys::GpuErrorFilter::OutOfMemory,
2738 crate::ErrorFilter::Validation => webgpu_sys::GpuErrorFilter::Validation,
2739 crate::ErrorFilter::Internal => webgpu_sys::GpuErrorFilter::Internal,
2740 });
2741 index
2742 }
2743
2744 fn pop_error_scope(&self, index: u32) -> Pin<Box<dyn dispatch::PopErrorScopeFuture>> {
2745 let current_scope_count = self.error_scope_count.get();
2746 let is_panicking = crate::util::is_panicking();
2747 if current_scope_count == 0 && !is_panicking {
2748 panic!("Mismatched pop_error_scope call: no error scope for this thread. Error scopes are thread-local.");
2749 }
2750 if index + 1 != current_scope_count && !is_panicking {
2751 panic!(
2752 "Mismatched pop_error_scope call: error scopes must be popped in reverse order."
2753 );
2754 }
2755 self.error_scope_count.set(index);
2758
2759 let error_promise = self.inner.pop_error_scope();
2760 Box::pin(MakeSendFuture::new(
2761 wasm_bindgen_futures::JsFuture::from(error_promise),
2762 future_pop_error_scope,
2763 ))
2764 }
2765
2766 unsafe fn start_graphics_debugger_capture(&self) {
2767 }
2769
2770 unsafe fn stop_graphics_debugger_capture(&self) {
2771 }
2773
2774 fn poll(&self, _poll_type: wgt::PollType<u64>) -> Result<crate::PollStatus, crate::PollError> {
2775 Ok(crate::PollStatus::QueueEmpty)
2777 }
2778
2779 fn get_internal_counters(&self) -> crate::InternalCounters {
2780 crate::InternalCounters::default()
2781 }
2782
2783 fn generate_allocator_report(&self) -> Option<wgt::AllocatorReport> {
2784 None
2785 }
2786
2787 fn destroy(&self) {
2788 self.inner.destroy();
2789 }
2790}
2791
2792impl Drop for WebDevice {
2793 fn drop(&mut self) {
2794 }
2796}
2797
2798impl dispatch::QueueInterface for WebQueue {
2799 fn write_buffer(
2800 &self,
2801 buffer: &dispatch::DispatchBuffer,
2802 offset: crate::BufferAddress,
2803 data: &[u8],
2804 ) {
2805 let buffer = buffer.as_webgpu();
2806 self.inner
2807 .write_buffer_with_f64_and_u8_slice_and_f64_and_f64(
2808 &buffer.inner,
2809 offset as f64,
2810 data,
2811 0f64,
2812 data.len() as f64,
2813 )
2814 .unwrap();
2815 }
2816
2817 fn create_staging_buffer(
2818 &self,
2819 size: crate::BufferSize,
2820 ) -> Option<dispatch::DispatchQueueWriteBuffer> {
2821 Some(
2822 WebQueueWriteBuffer {
2823 inner: vec![0; size.get() as usize].into_boxed_slice(),
2824 ident: crate::cmp::Identifier::create(),
2825 }
2826 .into(),
2827 )
2828 }
2829
2830 fn validate_write_buffer(
2831 &self,
2832 buffer: &dispatch::DispatchBuffer,
2833 offset: wgt::BufferAddress,
2834 size: wgt::BufferSize,
2835 ) -> Option<()> {
2836 let buffer = buffer.as_webgpu();
2837
2838 let usage = wgt::BufferUsages::from_bits_truncate(buffer.inner.usage());
2839 if !usage.contains(wgt::BufferUsages::COPY_DST) {
2841 log::error!("Destination buffer is missing the `COPY_DST` usage flag");
2842 return None;
2843 }
2844 let write_size = u64::from(size);
2845 if !write_size.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
2846 log::error!("Copy size {size} does not respect `COPY_BUFFER_ALIGNMENT`");
2847 return None;
2848 }
2849 if !offset.is_multiple_of(wgt::COPY_BUFFER_ALIGNMENT) {
2850 log::error!(
2851 "Buffer offset {offset} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`"
2852 );
2853 return None;
2854 }
2855 if write_size + offset > buffer.inner.size() as u64 {
2856 log::error!("copy of {}..{} would end up overrunning the bounds of the destination buffer of size {}", offset, offset + write_size, buffer.inner.size());
2857 return None;
2858 }
2859 Some(())
2860 }
2861
2862 fn write_staging_buffer(
2863 &self,
2864 buffer: &dispatch::DispatchBuffer,
2865 offset: crate::BufferAddress,
2866 staging_buffer: &dispatch::DispatchQueueWriteBuffer,
2867 ) {
2868 let staging_buffer = staging_buffer.as_webgpu();
2869
2870 dispatch::QueueInterface::write_buffer(self, buffer, offset, &staging_buffer.inner)
2871 }
2872
2873 fn write_texture(
2874 &self,
2875 texture: crate::TexelCopyTextureInfo<'_>,
2876 data: &[u8],
2877 data_layout: crate::TexelCopyBufferLayout,
2878 size: crate::Extent3d,
2879 ) {
2880 let mapped_data_layout = webgpu_sys::GpuTexelCopyBufferLayout::new();
2881 if let Some(bytes_per_row) = data_layout.bytes_per_row {
2882 mapped_data_layout.set_bytes_per_row(bytes_per_row);
2883 }
2884 if let Some(rows_per_image) = data_layout.rows_per_image {
2885 mapped_data_layout.set_rows_per_image(rows_per_image);
2886 }
2887 mapped_data_layout.set_offset_f64(data_layout.offset as f64);
2888
2889 self.inner
2890 .write_texture_with_u8_slice_and_gpu_extent_3d_dict(
2891 &map_texture_copy_view(texture),
2892 data,
2893 &mapped_data_layout,
2894 &map_extent_3d(size),
2895 )
2896 .unwrap();
2897 }
2898
2899 fn copy_external_image_to_texture(
2900 &self,
2901 source: &crate::CopyExternalImageSourceInfo,
2902 dest: crate::CopyExternalImageDestInfo<&crate::api::Texture>,
2903 size: crate::Extent3d,
2904 ) {
2905 self.inner
2906 .copy_external_image_to_texture_with_gpu_extent_3d_dict(
2907 &map_external_texture_copy_view(source),
2908 &map_tagged_texture_copy_view(dest),
2909 &map_extent_3d(size),
2910 )
2911 .unwrap();
2912 }
2913
2914 fn submit(
2915 &self,
2916 command_buffers: &mut dyn Iterator<Item = dispatch::DispatchCommandBuffer>,
2917 ) -> u64 {
2918 let temp_command_buffers = command_buffers.collect::<Vec<_>>();
2919
2920 let array = temp_command_buffers
2921 .iter()
2922 .map(|buffer| buffer.as_webgpu().inner.clone())
2923 .collect::<Vec<webgpu_sys::GpuCommandBuffer>>();
2924
2925 self.inner.submit(&array);
2926
2927 0
2928 }
2929
2930 fn get_timestamp_period(&self) -> f32 {
2931 1.0
2933 }
2934
2935 fn on_submitted_work_done(&self, callback: dispatch::BoxSubmittedWorkDoneCallback) {
2936 let promise = self.inner.on_submitted_work_done();
2937 wasm_bindgen_futures::spawn_local(async move {
2938 match wasm_bindgen_futures::JsFuture::from(promise).await {
2939 Ok(_) => callback(),
2940 Err(error) => {
2941 log::error!("on_submitted_work_done promise failed: {error:?}");
2942 callback();
2943 }
2944 }
2945 });
2946 }
2947
2948 fn compact_blas(
2949 &self,
2950 _blas: &dispatch::DispatchBlas,
2951 ) -> (Option<u64>, dispatch::DispatchBlas) {
2952 unimplemented!("Raytracing not implemented for web")
2953 }
2954
2955 fn present(&self, _detail: &dispatch::DispatchSurfaceOutputDetail) {
2956 }
2958}
2959impl Drop for WebQueue {
2960 fn drop(&mut self) {
2961 }
2963}
2964
2965impl dispatch::ShaderModuleInterface for WebShaderModule {
2966 fn get_compilation_info(&self) -> Pin<Box<dyn dispatch::ShaderCompilationInfoFuture>> {
2967 let compilation_info_promise = self.module.get_compilation_info();
2968 let map_future = Box::new({
2969 let compilation_info = self.compilation_info.clone();
2970 move |result| future_compilation_info(result, &compilation_info)
2971 });
2972 Box::pin(MakeSendFuture::new(
2973 wasm_bindgen_futures::JsFuture::from(compilation_info_promise),
2974 map_future,
2975 ))
2976 }
2977}
2978impl Drop for WebShaderModule {
2979 fn drop(&mut self) {
2980 }
2982}
2983
2984impl dispatch::BindGroupLayoutInterface for WebBindGroupLayout {}
2985impl Drop for WebBindGroupLayout {
2986 fn drop(&mut self) {
2987 }
2989}
2990
2991impl dispatch::BindGroupInterface for WebBindGroup {}
2992impl Drop for WebBindGroup {
2993 fn drop(&mut self) {
2994 }
2996}
2997
2998impl dispatch::TextureViewInterface for WebTextureView {}
2999impl Drop for WebTextureView {
3000 fn drop(&mut self) {
3001 }
3003}
3004
3005impl dispatch::SamplerInterface for WebSampler {}
3006impl Drop for WebSampler {
3007 fn drop(&mut self) {
3008 }
3010}
3011
3012impl dispatch::BufferInterface for WebBuffer {
3013 fn map_async(
3014 &self,
3015 mode: crate::MapMode,
3016 range: Range<crate::BufferAddress>,
3017 callback: dispatch::BufferMapCallback,
3018 ) {
3019 let map_promise = self.inner.map_async_with_f64_and_f64(
3020 map_map_mode(mode),
3021 range.start as f64,
3022 (range.end - range.start) as f64,
3023 );
3024
3025 self.set_mapped_range(range);
3026
3027 register_then_closures(&map_promise, callback, Ok(()), Err(crate::BufferAsyncError));
3028 }
3029
3030 fn get_mapped_range(
3031 &self,
3032 sub_range: Range<crate::BufferAddress>,
3033 ) -> Result<dispatch::DispatchBufferMappedRange, crate::MapRangeError> {
3034 let actual_mapping = self.get_mapped_range(sub_range)?;
3035 Ok(WebBufferMappedRange {
3036 actual_mapping,
3037 temporary_mapping: OnceCell::new(),
3038 temporary_mapping_modified: false,
3039 ident: crate::cmp::Identifier::create(),
3040 }
3041 .into())
3042 }
3043
3044 fn unmap(&self) {
3045 self.inner.unmap();
3046 self.mapping.borrow_mut().mapped_buffer = None;
3047 }
3048
3049 fn destroy(&self) {
3050 self.inner.destroy();
3051 }
3052}
3053impl Drop for WebBuffer {
3054 fn drop(&mut self) {
3055 }
3057}
3058
3059impl dispatch::TextureInterface for WebTexture {
3060 fn create_view(
3061 &self,
3062 desc: &crate::TextureViewDescriptor<'_>,
3063 ) -> dispatch::DispatchTextureView {
3064 let mapped = webgpu_sys::GpuTextureViewDescriptor::new();
3065 if let Some(dim) = desc.dimension {
3066 mapped.set_dimension(map_texture_view_dimension(dim));
3067 }
3068 if let Some(format) = desc.format {
3069 mapped.set_format(map_texture_format(format));
3070 }
3071 mapped.set_aspect(map_texture_aspect(desc.aspect));
3072 mapped.set_base_array_layer(desc.base_array_layer);
3073 if let Some(count) = desc.array_layer_count {
3074 mapped.set_array_layer_count(count);
3075 }
3076 mapped.set_base_mip_level(desc.base_mip_level);
3077 if let Some(count) = desc.mip_level_count {
3078 mapped.set_mip_level_count(count);
3079 }
3080 if let Some(label) = desc.label {
3081 mapped.set_label(label);
3082 }
3083 mapped.set_usage(desc.usage.unwrap_or(wgt::TextureUsages::empty()).bits());
3084
3085 let view = self.inner.create_view_with_descriptor(&mapped).unwrap();
3086
3087 WebTextureView {
3088 inner: view,
3089 ident: crate::cmp::Identifier::create(),
3090 }
3091 .into()
3092 }
3093
3094 fn destroy(&self) {
3095 if self.drop_guard.is_none() {
3096 self.inner.destroy();
3097 }
3098 }
3099}
3100impl Drop for WebTexture {
3101 fn drop(&mut self) {
3102 }
3105}
3106
3107impl dispatch::ExternalTextureInterface for WebExternalTexture {
3108 fn destroy(&self) {
3109 unimplemented!("ExternalTexture not implemented for web");
3110 }
3111}
3112impl Drop for WebExternalTexture {
3113 fn drop(&mut self) {
3114 unimplemented!("ExternalTexture not implemented for web");
3115 }
3116}
3117
3118impl dispatch::BlasInterface for WebBlas {
3119 fn prepare_compact_async(&self, _callback: BlasCompactCallback) {
3120 unimplemented!("Raytracing not implemented for web")
3121 }
3122 fn ready_for_compaction(&self) -> bool {
3123 unimplemented!("Raytracing not implemented for web")
3124 }
3125}
3126impl Drop for WebBlas {
3127 fn drop(&mut self) {
3128 }
3130}
3131
3132impl dispatch::TlasInterface for WebTlas {}
3133impl Drop for WebTlas {
3134 fn drop(&mut self) {
3135 }
3137}
3138
3139impl dispatch::QuerySetInterface for WebQuerySet {
3140 fn destroy(&self) {
3141 self.inner.destroy();
3142 }
3143}
3144
3145impl Drop for WebQuerySet {
3146 fn drop(&mut self) {
3147 }
3149}
3150
3151impl dispatch::PipelineLayoutInterface for WebPipelineLayout {}
3152impl Drop for WebPipelineLayout {
3153 fn drop(&mut self) {
3154 }
3156}
3157
3158impl dispatch::RenderPipelineInterface for WebRenderPipeline {
3159 fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {
3160 let bind_group_layout = self
3161 .inner
3162 .as_ref()
3163 .expect("a pipeline created during pipeline discovery has no bind group layouts")
3164 .get_bind_group_layout(index);
3165
3166 WebBindGroupLayout {
3167 inner: bind_group_layout,
3168 ident: crate::cmp::Identifier::create(),
3169 }
3170 .into()
3171 }
3172}
3173impl Drop for WebRenderPipeline {
3174 fn drop(&mut self) {
3175 }
3177}
3178
3179impl dispatch::ComputePipelineInterface for WebComputePipeline {
3180 fn get_bind_group_layout(&self, index: u32) -> dispatch::DispatchBindGroupLayout {
3181 let bind_group_layout = self.inner.get_bind_group_layout(index);
3182
3183 WebBindGroupLayout {
3184 inner: bind_group_layout,
3185 ident: crate::cmp::Identifier::create(),
3186 }
3187 .into()
3188 }
3189}
3190impl Drop for WebComputePipeline {
3191 fn drop(&mut self) {
3192 }
3194}
3195
3196impl dispatch::CommandEncoderInterface for WebCommandEncoder {
3197 fn copy_buffer_to_buffer(
3198 &self,
3199 source: &dispatch::DispatchBuffer,
3200 source_offset: crate::BufferAddress,
3201 destination: &dispatch::DispatchBuffer,
3202 destination_offset: crate::BufferAddress,
3203 copy_size: Option<crate::BufferAddress>,
3204 ) {
3205 let source = source.as_webgpu();
3206 let destination = destination.as_webgpu();
3207
3208 if let Some(size) = copy_size {
3209 self.inner
3210 .copy_buffer_to_buffer_with_f64_and_f64_and_f64(
3211 &source.inner,
3212 source_offset as f64,
3213 &destination.inner,
3214 destination_offset as f64,
3215 size as f64,
3216 )
3217 .unwrap();
3218 } else {
3219 self.inner
3220 .copy_buffer_to_buffer_with_f64_and_f64(
3221 &source.inner,
3222 source_offset as f64,
3223 &destination.inner,
3224 destination_offset as f64,
3225 )
3226 .unwrap();
3227 }
3228 }
3229
3230 fn copy_buffer_to_texture(
3231 &self,
3232 source: crate::TexelCopyBufferInfo<'_>,
3233 destination: crate::TexelCopyTextureInfo<'_>,
3234 copy_size: crate::Extent3d,
3235 ) {
3236 self.inner
3237 .copy_buffer_to_texture_with_gpu_extent_3d_dict(
3238 &map_buffer_copy_view(source),
3239 &map_texture_copy_view(destination),
3240 &map_extent_3d(copy_size),
3241 )
3242 .unwrap();
3243 }
3244
3245 fn copy_texture_to_buffer(
3246 &self,
3247 source: crate::TexelCopyTextureInfo<'_>,
3248 destination: crate::TexelCopyBufferInfo<'_>,
3249 copy_size: crate::Extent3d,
3250 ) {
3251 self.inner
3252 .copy_texture_to_buffer_with_gpu_extent_3d_dict(
3253 &map_texture_copy_view(source),
3254 &map_buffer_copy_view(destination),
3255 &map_extent_3d(copy_size),
3256 )
3257 .unwrap();
3258 }
3259
3260 fn copy_texture_to_texture(
3261 &self,
3262 source: crate::TexelCopyTextureInfo<'_>,
3263 destination: crate::TexelCopyTextureInfo<'_>,
3264 copy_size: crate::Extent3d,
3265 ) {
3266 self.inner
3267 .copy_texture_to_texture_with_gpu_extent_3d_dict(
3268 &map_texture_copy_view(source),
3269 &map_texture_copy_view(destination),
3270 &map_extent_3d(copy_size),
3271 )
3272 .unwrap();
3273 }
3274
3275 fn begin_compute_pass(
3276 &self,
3277 desc: &crate::ComputePassDescriptor<'_>,
3278 ) -> dispatch::DispatchComputePass {
3279 let mapped_desc = webgpu_sys::GpuComputePassDescriptor::new();
3280 if let Some(label) = desc.label {
3281 mapped_desc.set_label(label);
3282 }
3283
3284 if let Some(ref timestamp_writes) = desc.timestamp_writes {
3285 let query_set = timestamp_writes.query_set.inner.as_webgpu();
3286 let writes = webgpu_sys::GpuComputePassTimestampWrites::new(&query_set.inner);
3287 if let Some(index) = timestamp_writes.beginning_of_pass_write_index {
3288 writes.set_beginning_of_pass_write_index(index);
3289 }
3290 if let Some(index) = timestamp_writes.end_of_pass_write_index {
3291 writes.set_end_of_pass_write_index(index);
3292 }
3293 mapped_desc.set_timestamp_writes(&writes);
3294 }
3295
3296 let compute_pass = self.inner.begin_compute_pass_with_descriptor(&mapped_desc);
3297
3298 WebComputePassEncoder {
3299 inner: compute_pass,
3300 ident: crate::cmp::Identifier::create(),
3301 }
3302 .into()
3303 }
3304
3305 fn begin_render_pass(
3306 &self,
3307 desc: &crate::RenderPassDescriptor<'_>,
3308 ) -> dispatch::DispatchRenderPass {
3309 let mapped_color_attachments = desc
3310 .color_attachments
3311 .iter()
3312 .map(|attachment| match attachment {
3313 Some(ca) => {
3314 let (load_op, clear_value) = match ca.ops.load {
3315 crate::LoadOp::Clear(color) => {
3316 (webgpu_sys::GpuLoadOp::Clear, Some(map_color(color)))
3317 }
3318 crate::LoadOp::DontCare(_token) => {
3319 (
3322 webgpu_sys::GpuLoadOp::Clear,
3323 Some(map_color(crate::Color::BLACK)),
3324 )
3325 }
3326 crate::LoadOp::Load => (webgpu_sys::GpuLoadOp::Load, None),
3327 };
3328
3329 let view = &ca.view.inner.as_webgpu().inner;
3330
3331 let mapped_color_attachment =
3332 webgpu_sys::GpuRenderPassColorAttachment::new_with_gpu_texture_view(
3333 load_op,
3334 map_store_op(ca.ops.store),
3335 view,
3336 );
3337 if let Some(cv) = clear_value {
3338 mapped_color_attachment.set_clear_value_gpu_color_dict(&cv);
3339 }
3340 if let Some(rt) = ca.resolve_target {
3341 let resolve_target_view = &rt.inner.as_webgpu().inner;
3342 mapped_color_attachment
3343 .set_resolve_target_gpu_texture_view(resolve_target_view);
3344 }
3345 mapped_color_attachment.set_store_op(map_store_op(ca.ops.store));
3346
3347 js_sys::JsNullable::wrap(mapped_color_attachment)
3348 }
3349 None => js_sys::JsNullable::new(),
3350 })
3351 .collect::<Vec<js_sys::JsNullable<webgpu_sys::GpuRenderPassColorAttachment>>>();
3352
3353 let mapped_desc = webgpu_sys::GpuRenderPassDescriptor::new(&mapped_color_attachments);
3354
3355 if let Some(label) = desc.label {
3356 mapped_desc.set_label(label);
3357 }
3358
3359 if let Some(dsa) = &desc.depth_stencil_attachment {
3360 let depth_stencil_attachment = &dsa.view.inner.as_webgpu().inner;
3361 let mapped_depth_stencil_attachment =
3362 webgpu_sys::GpuRenderPassDepthStencilAttachment::new_with_gpu_texture_view(
3363 depth_stencil_attachment,
3364 );
3365 if let Some(ref ops) = dsa.depth_ops {
3366 let load_op = match ops.load {
3367 crate::LoadOp::Clear(v) => {
3368 mapped_depth_stencil_attachment.set_depth_clear_value(v);
3369 webgpu_sys::GpuLoadOp::Clear
3370 }
3371 crate::LoadOp::DontCare(_token) => {
3372 mapped_depth_stencil_attachment.set_depth_clear_value(1.0);
3374 webgpu_sys::GpuLoadOp::Clear
3375 }
3376 crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load,
3377 };
3378 mapped_depth_stencil_attachment.set_depth_load_op(load_op);
3379 mapped_depth_stencil_attachment.set_depth_store_op(map_store_op(ops.store));
3380 }
3381 mapped_depth_stencil_attachment.set_depth_read_only(dsa.depth_ops.is_none());
3382 if let Some(ref ops) = dsa.stencil_ops {
3383 let load_op = match ops.load {
3384 crate::LoadOp::Clear(v) => {
3385 mapped_depth_stencil_attachment.set_stencil_clear_value(v);
3386 webgpu_sys::GpuLoadOp::Clear
3387 }
3388 crate::LoadOp::DontCare(_token) => {
3389 mapped_depth_stencil_attachment.set_stencil_clear_value(0);
3391 webgpu_sys::GpuLoadOp::Clear
3392 }
3393 crate::LoadOp::Load => webgpu_sys::GpuLoadOp::Load,
3394 };
3395 mapped_depth_stencil_attachment.set_stencil_load_op(load_op);
3396 mapped_depth_stencil_attachment.set_stencil_store_op(map_store_op(ops.store));
3397 }
3398 mapped_depth_stencil_attachment.set_stencil_read_only(dsa.stencil_ops.is_none());
3399 mapped_desc.set_depth_stencil_attachment(&mapped_depth_stencil_attachment);
3400 }
3401
3402 if let Some(ref timestamp_writes) = desc.timestamp_writes {
3403 let query_set = ×tamp_writes.query_set.inner.as_webgpu().inner;
3404 let writes = webgpu_sys::GpuRenderPassTimestampWrites::new(query_set);
3405 if let Some(index) = timestamp_writes.beginning_of_pass_write_index {
3406 writes.set_beginning_of_pass_write_index(index);
3407 }
3408 if let Some(index) = timestamp_writes.end_of_pass_write_index {
3409 writes.set_end_of_pass_write_index(index);
3410 }
3411 mapped_desc.set_timestamp_writes(&writes);
3412 }
3413
3414 let render_pass = self.inner.begin_render_pass(&mapped_desc).unwrap();
3415
3416 WebRenderPassEncoder {
3417 inner: render_pass,
3418 draws: PipelineDraws::new(self.pipeline_discovery.borrow().is_some()),
3419 ident: crate::cmp::Identifier::create(),
3420 }
3421 .into()
3422 }
3423
3424 fn finish(&mut self) -> dispatch::DispatchCommandBuffer {
3425 let label = self.inner.label();
3426 let buffer = if label.is_empty() {
3427 self.inner.finish()
3428 } else {
3429 let mapped_desc = webgpu_sys::GpuCommandBufferDescriptor::new();
3430 mapped_desc.set_label(&label);
3431
3432 self.inner.finish_with_descriptor(&mapped_desc)
3433 };
3434
3435 WebCommandBuffer {
3436 inner: buffer,
3437 ident: crate::cmp::Identifier::create(),
3438 }
3439 .into()
3440 }
3441
3442 fn clear_texture(
3443 &self,
3444 _texture: &dispatch::DispatchTexture,
3445 _subresource_range: &crate::ImageSubresourceRange,
3446 ) {
3447 unimplemented!("clear_texture is not yet implemented");
3448 }
3449
3450 fn clear_buffer(
3451 &self,
3452 buffer: &dispatch::DispatchBuffer,
3453 offset: crate::BufferAddress,
3454 size: Option<crate::BufferAddress>,
3455 ) {
3456 let buffer = buffer.as_webgpu();
3457
3458 match size {
3459 Some(size) => {
3460 self.inner
3461 .clear_buffer_with_f64_and_f64(&buffer.inner, offset as f64, size as f64)
3462 }
3463 None => self
3464 .inner
3465 .clear_buffer_with_f64(&buffer.inner, offset as f64),
3466 }
3467 }
3468
3469 fn insert_debug_marker(&self, label: &str) {
3470 self.inner.insert_debug_marker(label)
3471 }
3472
3473 fn push_debug_group(&self, group_label: &str) {
3474 self.inner.push_debug_group(group_label)
3475 }
3476
3477 fn pop_debug_group(&self) {
3478 self.inner.pop_debug_group()
3479 }
3480
3481 fn write_timestamp(&self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {
3482 panic!("TIMESTAMP_QUERY_INSIDE_ENCODERS feature must be enabled to call write_timestamp on a command encoder.")
3485 }
3486
3487 fn resolve_query_set(
3488 &self,
3489 query_set: &dispatch::DispatchQuerySet,
3490 first_query: u32,
3491 query_count: u32,
3492 destination: &dispatch::DispatchBuffer,
3493 destination_offset: crate::BufferAddress,
3494 ) {
3495 let query_set = &query_set.as_webgpu().inner;
3496 let destination = &destination.as_webgpu().inner;
3497
3498 self.inner.resolve_query_set_with_u32(
3499 query_set,
3500 first_query,
3501 query_count,
3502 destination,
3503 destination_offset as u32,
3504 );
3505 }
3506
3507 fn mark_acceleration_structures_built<'a>(
3508 &self,
3509 _blas: &mut dyn Iterator<Item = &'a Blas>,
3510 _tlas: &mut dyn Iterator<Item = &'a Tlas>,
3511 ) {
3512 unimplemented!("Raytracing not implemented for web");
3513 }
3514
3515 fn build_acceleration_structures<'a>(
3516 &self,
3517 _blas: &mut dyn Iterator<Item = &'a crate::BlasBuildEntry<'a>>,
3518 _tlas: &mut dyn Iterator<Item = &'a crate::Tlas>,
3519 ) {
3520 unimplemented!("Raytracing not implemented for web");
3521 }
3522
3523 fn transition_resources<'a>(
3524 &mut self,
3525 _buffer_transitions: &mut dyn Iterator<
3526 Item = wgt::BufferTransition<&'a dispatch::DispatchBuffer>,
3527 >,
3528 _texture_transitions: &mut dyn Iterator<
3529 Item = wgt::TextureTransition<&'a dispatch::DispatchTexture>,
3530 >,
3531 ) {
3532 }
3534}
3535impl Drop for WebCommandEncoder {
3536 fn drop(&mut self) {
3537 }
3539}
3540
3541impl dispatch::PipelineCacheInterface for WebPipelineCache {
3542 fn get_data(&self) -> Option<Vec<u8>> {
3543 todo!()
3544 }
3545}
3546impl Drop for WebPipelineCache {
3547 fn drop(&mut self) {
3548 }
3550}
3551
3552impl dispatch::ComputePassInterface for WebComputePassEncoder {
3553 fn set_pipeline(&mut self, pipeline: &dispatch::DispatchComputePipeline) {
3554 let pipeline = &pipeline.as_webgpu().inner;
3555 self.inner.set_pipeline(pipeline);
3556 }
3557
3558 fn set_bind_group(
3559 &mut self,
3560 index: u32,
3561 bind_group: Option<&dispatch::DispatchBindGroup>,
3562 offsets: &[crate::DynamicOffset],
3563 ) {
3564 let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);
3565
3566 if offsets.is_empty() {
3567 self.inner.set_bind_group(index, bind_group);
3568 } else {
3569 self.inner
3570 .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(
3571 index,
3572 bind_group,
3573 offsets,
3574 0f64,
3575 offsets.len() as u32,
3576 )
3577 .unwrap();
3578 }
3579 }
3580
3581 fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {
3582 panic!("IMMEDIATES feature must be enabled to call set_immediates")
3583 }
3584
3585 fn insert_debug_marker(&mut self, label: &str) {
3586 self.inner.insert_debug_marker(label);
3587 }
3588
3589 fn push_debug_group(&mut self, group_label: &str) {
3590 self.inner.push_debug_group(group_label);
3591 }
3592
3593 fn pop_debug_group(&mut self) {
3594 self.inner.pop_debug_group();
3595 }
3596
3597 fn write_timestamp(&mut self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {
3598 panic!("TIMESTAMP_QUERY_INSIDE_PASSES feature must be enabled to call write_timestamp in a compute pass.")
3599 }
3600
3601 fn begin_pipeline_statistics_query(
3602 &mut self,
3603 _query_set: &dispatch::DispatchQuerySet,
3604 _query_index: u32,
3605 ) {
3606 }
3608
3609 fn end_pipeline_statistics_query(&mut self) {
3610 }
3612
3613 fn dispatch_workgroups(&mut self, x: u32, y: u32, z: u32) {
3614 self.inner
3615 .dispatch_workgroups_with_workgroup_count_y_and_workgroup_count_z(x, y, z);
3616 }
3617
3618 fn dispatch_workgroups_indirect(
3619 &mut self,
3620 indirect_buffer: &dispatch::DispatchBuffer,
3621 indirect_offset: crate::BufferAddress,
3622 ) {
3623 let indirect_buffer = indirect_buffer.as_webgpu();
3624
3625 self.inner
3626 .dispatch_workgroups_indirect_with_f64(&indirect_buffer.inner, indirect_offset as f64);
3627 }
3628
3629 fn transition_resources<'a>(
3630 &mut self,
3631 _buffer_transitions: &mut dyn Iterator<
3632 Item = wgt::BufferTransition<&'a dispatch::DispatchBuffer>,
3633 >,
3634 _texture_transitions: &mut dyn Iterator<
3635 Item = wgt::TextureTransition<&'a dispatch::DispatchTextureView>,
3636 >,
3637 ) {
3638 }
3640}
3641impl Drop for WebComputePassEncoder {
3642 fn drop(&mut self) {
3643 self.inner.end();
3644 }
3645}
3646
3647impl dispatch::RenderPassInterface for WebRenderPassEncoder {
3648 fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {
3649 if let Some(pipeline) = self.draws.set(pipeline) {
3650 self.inner.set_pipeline(pipeline);
3651 }
3652 }
3653
3654 fn set_bind_group(
3655 &mut self,
3656 index: u32,
3657 bind_group: Option<&dispatch::DispatchBindGroup>,
3658 offsets: &[crate::DynamicOffset],
3659 ) {
3660 let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);
3661
3662 if offsets.is_empty() {
3663 self.inner.set_bind_group(index, bind_group);
3664 } else {
3665 self.inner
3666 .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(
3667 index,
3668 bind_group,
3669 offsets,
3670 0f64,
3671 offsets.len() as u32,
3672 )
3673 .unwrap();
3674 }
3675 }
3676
3677 fn set_index_buffer(
3678 &mut self,
3679 buffer: &dispatch::DispatchBuffer,
3680 index_format: crate::IndexFormat,
3681 offset: crate::BufferAddress,
3682 size: Option<crate::BufferSize>,
3683 ) {
3684 let buffer = buffer.as_webgpu();
3685 let index_format = map_index_format(index_format);
3686
3687 if let Some(size) = size {
3688 self.inner.set_index_buffer_with_f64_and_f64(
3689 &buffer.inner,
3690 index_format,
3691 offset as f64,
3692 size.get() as f64,
3693 );
3694 } else {
3695 self.inner
3696 .set_index_buffer_with_f64(&buffer.inner, index_format, offset as f64);
3697 }
3698 }
3699
3700 fn set_vertex_buffer(
3701 &mut self,
3702 slot: u32,
3703 buffer: Option<&dispatch::DispatchBuffer>,
3704 offset: crate::BufferAddress,
3705 size: Option<crate::BufferSize>,
3706 ) {
3707 let buffer = buffer.map(|buffer| &buffer.as_webgpu().inner);
3708
3709 if let Some(size) = size {
3710 self.inner.set_vertex_buffer_with_f64_and_f64(
3711 slot,
3712 buffer,
3713 offset as f64,
3714 size.get() as f64,
3715 );
3716 } else {
3717 self.inner
3718 .set_vertex_buffer_with_f64(slot, buffer, offset as f64);
3719 }
3720 }
3721
3722 fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {
3723 panic!("IMMEDIATES feature must be enabled to call set_immediates")
3724 }
3725
3726 fn set_blend_constant(&mut self, color: crate::Color) {
3727 self.inner
3728 .set_blend_constant_with_gpu_color_dict(&map_color(color))
3729 .unwrap();
3730 }
3731
3732 fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {
3733 self.inner.set_scissor_rect(x, y, width, height);
3734 }
3735
3736 fn set_viewport(
3737 &mut self,
3738 x: f32,
3739 y: f32,
3740 width: f32,
3741 height: f32,
3742 min_depth: f32,
3743 max_depth: f32,
3744 ) {
3745 self.inner
3746 .set_viewport(x, y, width, height, min_depth, max_depth);
3747 }
3748
3749 fn set_stencil_reference(&mut self, reference: u32) {
3750 self.inner.set_stencil_reference(reference);
3751 }
3752
3753 fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
3754 if self.draws.skipping {
3755 return;
3756 }
3757 self.inner
3758 .draw_with_instance_count_and_first_vertex_and_first_instance(
3759 vertices.end - vertices.start,
3760 instances.end - instances.start,
3761 vertices.start,
3762 instances.start,
3763 );
3764 }
3765
3766 fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
3767 if self.draws.skipping {
3768 return;
3769 }
3770 self.inner
3771 .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(
3772 indices.end - indices.start,
3773 instances.end - instances.start,
3774 indices.start,
3775 base_vertex,
3776 instances.start,
3777 )
3778 }
3779
3780 fn draw_mesh_tasks(&mut self, _group_count_x: u32, _group_count_y: u32, _group_count_z: u32) {
3781 panic!("MESH_SHADER feature must be enabled to call draw_mesh_tasks")
3782 }
3783
3784 fn draw_indirect(
3785 &mut self,
3786 indirect_buffer: &dispatch::DispatchBuffer,
3787 indirect_offset: crate::BufferAddress,
3788 ) {
3789 if self.draws.skipping {
3790 return;
3791 }
3792 let buffer = indirect_buffer.as_webgpu();
3793 self.inner
3794 .draw_indirect_with_f64(&buffer.inner, indirect_offset as f64);
3795 }
3796
3797 fn draw_indexed_indirect(
3798 &mut self,
3799 indirect_buffer: &dispatch::DispatchBuffer,
3800 indirect_offset: crate::BufferAddress,
3801 ) {
3802 if self.draws.skipping {
3803 return;
3804 }
3805 let buffer = indirect_buffer.as_webgpu();
3806 self.inner
3807 .draw_indexed_indirect_with_f64(&buffer.inner, indirect_offset as f64);
3808 }
3809
3810 fn draw_mesh_tasks_indirect(
3811 &mut self,
3812 _indirect_buffer: &dispatch::DispatchBuffer,
3813 _indirect_offset: crate::BufferAddress,
3814 ) {
3815 panic!("MESH_SHADER feature must be enabled to call draw_mesh_tasks_indirect")
3816 }
3817
3818 fn multi_draw_indirect(
3819 &mut self,
3820 indirect_buffer: &dispatch::DispatchBuffer,
3821 indirect_offset: crate::BufferAddress,
3822 count: u32,
3823 ) {
3824 if self.draws.skipping {
3825 return;
3826 }
3827 let buffer = indirect_buffer.as_webgpu();
3828
3829 for i in 0..count {
3830 let offset = indirect_offset + i as crate::BufferAddress * 16;
3831 self.inner
3832 .draw_indirect_with_f64(&buffer.inner, offset as f64);
3833 }
3834 }
3835
3836 fn multi_draw_indexed_indirect(
3837 &mut self,
3838 indirect_buffer: &dispatch::DispatchBuffer,
3839 indirect_offset: crate::BufferAddress,
3840 count: u32,
3841 ) {
3842 if self.draws.skipping {
3843 return;
3844 }
3845 let buffer = indirect_buffer.as_webgpu();
3846
3847 for i in 0..count {
3848 let offset = indirect_offset + i as crate::BufferAddress * 20;
3849 self.inner
3850 .draw_indexed_indirect_with_f64(&buffer.inner, offset as f64);
3851 }
3852 }
3853
3854 fn multi_draw_mesh_tasks_indirect(
3855 &mut self,
3856 _indirect_buffer: &dispatch::DispatchBuffer,
3857 _indirect_offset: crate::BufferAddress,
3858 _count: u32,
3859 ) {
3860 panic!("MESH_SHADER feature must be enabled to call multi_draw_mesh_tasks_indirect")
3861 }
3862
3863 fn multi_draw_indirect_count(
3864 &mut self,
3865 _indirect_buffer: &dispatch::DispatchBuffer,
3866 _indirect_offset: crate::BufferAddress,
3867 _count_buffer: &dispatch::DispatchBuffer,
3868 _count_buffer_offset: crate::BufferAddress,
3869 _max_count: u32,
3870 ) {
3871 if self.draws.skipping {
3872 return;
3873 }
3874 panic!(
3875 "MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indirect_count"
3876 )
3877 }
3878
3879 fn multi_draw_indexed_indirect_count(
3880 &mut self,
3881 _indirect_buffer: &dispatch::DispatchBuffer,
3882 _indirect_offset: crate::BufferAddress,
3883 _count_buffer: &dispatch::DispatchBuffer,
3884 _count_buffer_offset: crate::BufferAddress,
3885 _max_count: u32,
3886 ) {
3887 if self.draws.skipping {
3888 return;
3889 }
3890 panic!("MULTI_DRAW_INDIRECT_COUNT feature must be enabled to call multi_draw_indexed_indirect_count")
3891 }
3892
3893 fn multi_draw_mesh_tasks_indirect_count(
3894 &mut self,
3895 _indirect_buffer: &dispatch::DispatchBuffer,
3896 _indirect_offset: crate::BufferAddress,
3897 _count_buffer: &dispatch::DispatchBuffer,
3898 _count_buffer_offset: crate::BufferAddress,
3899 _max_count: u32,
3900 ) {
3901 panic!("MESH_SHADER feature must be enabled to call multi_draw_mesh_tasks_indirect_count")
3902 }
3903
3904 fn insert_debug_marker(&mut self, label: &str) {
3905 self.inner.insert_debug_marker(label);
3906 }
3907
3908 fn push_debug_group(&mut self, group_label: &str) {
3909 self.inner.push_debug_group(group_label);
3910 }
3911
3912 fn pop_debug_group(&mut self) {
3913 self.inner.pop_debug_group();
3914 }
3915
3916 fn write_timestamp(&mut self, _query_set: &dispatch::DispatchQuerySet, _query_index: u32) {
3917 panic!("TIMESTAMP_QUERY_INSIDE_PASSES feature must be enabled to call write_timestamp in a render pass.")
3918 }
3919
3920 fn begin_occlusion_query(&mut self, query_index: u32) {
3921 self.inner.begin_occlusion_query(query_index);
3922 }
3923
3924 fn end_occlusion_query(&mut self) {
3925 self.inner.end_occlusion_query();
3926 }
3927
3928 fn begin_pipeline_statistics_query(
3929 &mut self,
3930 _query_set: &dispatch::DispatchQuerySet,
3931 _query_index: u32,
3932 ) {
3933 }
3935
3936 fn end_pipeline_statistics_query(&mut self) {
3937 }
3939
3940 fn execute_bundles(
3941 &mut self,
3942 render_bundles: &mut dyn Iterator<Item = &dispatch::DispatchRenderBundle>,
3943 ) {
3944 let mapped = render_bundles
3945 .map(|bundle| bundle.as_webgpu().inner.clone())
3946 .collect::<Vec<webgpu_sys::GpuRenderBundle>>();
3947 self.inner.execute_bundles(&mapped);
3948 }
3949}
3950impl Drop for WebRenderPassEncoder {
3951 fn drop(&mut self) {
3952 self.inner.end();
3953 }
3954}
3955
3956impl dispatch::CommandBufferInterface for WebCommandBuffer {}
3957impl Drop for WebCommandBuffer {
3958 fn drop(&mut self) {
3959 }
3961}
3962
3963impl dispatch::RenderBundleEncoderInterface for WebRenderBundleEncoder {
3964 fn set_pipeline(&mut self, pipeline: &dispatch::DispatchRenderPipeline) {
3965 if let Some(pipeline) = self.draws.set(pipeline) {
3966 self.inner.set_pipeline(pipeline);
3967 }
3968 }
3969
3970 fn set_bind_group(
3971 &mut self,
3972 index: u32,
3973 bind_group: Option<&dispatch::DispatchBindGroup>,
3974 offsets: &[crate::DynamicOffset],
3975 ) {
3976 let bind_group = bind_group.map(|bind_group| &bind_group.as_webgpu().inner);
3977
3978 if offsets.is_empty() {
3979 self.inner.set_bind_group(index, bind_group);
3980 } else {
3981 self.inner
3982 .set_bind_group_with_u32_slice_and_f64_and_dynamic_offsets_data_length(
3983 index,
3984 bind_group,
3985 offsets,
3986 0f64,
3987 offsets.len() as u32,
3988 )
3989 .unwrap();
3990 }
3991 }
3992
3993 fn set_index_buffer(
3994 &mut self,
3995 buffer: &dispatch::DispatchBuffer,
3996 index_format: crate::IndexFormat,
3997 offset: crate::BufferAddress,
3998 size: Option<crate::BufferSize>,
3999 ) {
4000 let buffer = buffer.as_webgpu();
4001 let index_format = map_index_format(index_format);
4002
4003 if let Some(size) = size {
4004 self.inner.set_index_buffer_with_f64_and_f64(
4005 &buffer.inner,
4006 index_format,
4007 offset as f64,
4008 size.get() as f64,
4009 );
4010 } else {
4011 self.inner
4012 .set_index_buffer_with_f64(&buffer.inner, index_format, offset as f64);
4013 }
4014 }
4015
4016 fn set_vertex_buffer(
4017 &mut self,
4018 slot: u32,
4019 buffer: Option<&dispatch::DispatchBuffer>,
4020 offset: crate::BufferAddress,
4021 size: Option<crate::BufferSize>,
4022 ) {
4023 let buffer = buffer.map(|buffer| &buffer.as_webgpu().inner);
4024
4025 if let Some(size) = size {
4026 self.inner.set_vertex_buffer_with_f64_and_f64(
4027 slot,
4028 buffer,
4029 offset as f64,
4030 size.get() as f64,
4031 );
4032 } else {
4033 self.inner
4034 .set_vertex_buffer_with_f64(slot, buffer, offset as f64);
4035 }
4036 }
4037
4038 fn set_immediates(&mut self, _offset: u32, _data: &[u8]) {
4039 panic!("IMMEDIATES feature must be enabled to call set_immediates")
4040 }
4041
4042 fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
4043 if self.draws.skipping {
4044 return;
4045 }
4046 self.inner
4047 .draw_with_instance_count_and_first_vertex_and_first_instance(
4048 vertices.end - vertices.start,
4049 instances.end - instances.start,
4050 vertices.start,
4051 instances.start,
4052 );
4053 }
4054
4055 fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
4056 if self.draws.skipping {
4057 return;
4058 }
4059 self.inner
4060 .draw_indexed_with_instance_count_and_first_index_and_base_vertex_and_first_instance(
4061 indices.end - indices.start,
4062 instances.end - instances.start,
4063 indices.start,
4064 base_vertex,
4065 instances.start,
4066 )
4067 }
4068
4069 fn draw_indirect(
4070 &mut self,
4071 indirect_buffer: &dispatch::DispatchBuffer,
4072 indirect_offset: crate::BufferAddress,
4073 ) {
4074 if self.draws.skipping {
4075 return;
4076 }
4077 let buffer = indirect_buffer.as_webgpu();
4078 self.inner
4079 .draw_indirect_with_f64(&buffer.inner, indirect_offset as f64);
4080 }
4081
4082 fn draw_indexed_indirect(
4083 &mut self,
4084 indirect_buffer: &dispatch::DispatchBuffer,
4085 indirect_offset: crate::BufferAddress,
4086 ) {
4087 if self.draws.skipping {
4088 return;
4089 }
4090 let buffer = indirect_buffer.as_webgpu();
4091 self.inner
4092 .draw_indexed_indirect_with_f64(&buffer.inner, indirect_offset as f64);
4093 }
4094
4095 fn finish(self, desc: &crate::RenderBundleDescriptor<'_>) -> dispatch::DispatchRenderBundle
4096 where
4097 Self: Sized,
4098 {
4099 let bundle = match desc.label {
4100 Some(label) => {
4101 let mapped_desc = webgpu_sys::GpuRenderBundleDescriptor::new();
4102 mapped_desc.set_label(label);
4103 self.inner.finish_with_descriptor(&mapped_desc)
4104 }
4105 None => self.inner.finish(),
4106 };
4107
4108 WebRenderBundle {
4109 inner: bundle,
4110 ident: crate::cmp::Identifier::create(),
4111 }
4112 .into()
4113 }
4114
4115 #[cfg(custom)]
4116 fn finish_boxed(
4117 self: Box<Self>,
4118 desc: &crate::RenderBundleDescriptor<'_>,
4119 ) -> dispatch::DispatchRenderBundle {
4120 (*self).finish(desc)
4121 }
4122}
4123impl Drop for WebRenderBundleEncoder {
4124 fn drop(&mut self) {
4125 }
4127}
4128
4129impl dispatch::RenderBundleInterface for WebRenderBundle {}
4130impl Drop for WebRenderBundle {
4131 fn drop(&mut self) {
4132 }
4134}
4135
4136fn match_media_query(query: &str) -> Option<bool> {
4139 let list = web_sys::window()?.match_media(query).ok().flatten()?;
4140 Some(list.matches())
4141}
4142
4143fn environment_color_gamut() -> Option<wgt::DisplayGamut> {
4146 if match_media_query("(color-gamut: rec2020)") == Some(true) {
4147 Some(wgt::DisplayGamut::Rec2020)
4148 } else if match_media_query("(color-gamut: p3)") == Some(true) {
4149 Some(wgt::DisplayGamut::DisplayP3)
4150 } else if match_media_query("(color-gamut: srgb)") == Some(true) {
4151 Some(wgt::DisplayGamut::Srgb)
4152 } else {
4153 None
4154 }
4155}
4156
4157mod rgba16float_probe {
4161 use super::*;
4162
4163 static RGBA16FLOAT_CANVAS_SUPPORT: AtomicU8 = AtomicU8::new(PROBE_UNKNOWN);
4174 const PROBE_UNKNOWN: u8 = 0;
4175 const PROBE_UNSUPPORTED: u8 = 1;
4176 const PROBE_SUPPORTED: u8 = 2;
4177
4178 pub(super) fn rgba16float_canvas_supported() -> bool {
4185 RGBA16FLOAT_CANVAS_SUPPORT.load(Ordering::Relaxed) != PROBE_UNSUPPORTED
4186 }
4187
4188 pub(super) fn probe_rgba16float_canvas_support(device: &webgpu_sys::GpuDevice) {
4196 if RGBA16FLOAT_CANVAS_SUPPORT.load(Ordering::Relaxed) != PROBE_UNKNOWN {
4197 return;
4198 }
4199 let Some(supported) = try_configure_rgba16float_canvas(device) else {
4202 return;
4203 };
4204 RGBA16FLOAT_CANVAS_SUPPORT.store(
4205 if supported {
4206 PROBE_SUPPORTED
4207 } else {
4208 PROBE_UNSUPPORTED
4209 },
4210 Ordering::Relaxed,
4211 );
4212 }
4213
4214 fn try_configure_rgba16float_canvas(device: &webgpu_sys::GpuDevice) -> Option<bool> {
4217 let canvas = web_sys::OffscreenCanvas::new(1, 1).ok()?;
4218 let context = canvas.get_context("webgpu").ok()??;
4219 let context: webgpu_sys::GpuCanvasContext = context.unchecked_into();
4220 let config = webgpu_sys::GpuCanvasConfiguration::new(
4221 device,
4222 map_texture_format(wgt::TextureFormat::Rgba16Float),
4223 );
4224 let supported = context.configure(&config).is_ok();
4225 if supported {
4226 context.unconfigure();
4228 }
4229 Some(supported)
4230 }
4231}
4232
4233impl dispatch::SurfaceInterface for WebSurface {
4234 fn get_capabilities(&self, _adapter: &dispatch::DispatchAdapter) -> wgt::SurfaceCapabilities {
4235 let mut formats = vec![
4236 wgt::TextureFormat::Rgba8Unorm,
4237 wgt::TextureFormat::Bgra8Unorm,
4238 ];
4239 if rgba16float_probe::rgba16float_canvas_supported() {
4242 formats.push(wgt::TextureFormat::Rgba16Float);
4243 }
4244 let mut mapped_formats = formats.iter().map(|format| map_texture_format(*format));
4245 let preferred_format = self
4248 .gpu
4249 .as_ref()
4250 .expect("Caller could not have created an adapter if gpu is undefined.")
4251 .get_preferred_canvas_format();
4252 if let Some(index) = mapped_formats.position(|format| format == preferred_format) {
4253 formats.swap(0, index);
4254 }
4255
4256 wgt::SurfaceCapabilities {
4257 format_capabilities: formats
4258 .iter()
4259 .map(|&format| {
4260 let mut color_spaces =
4264 wgt::SurfaceColorSpaces::SRGB | wgt::SurfaceColorSpaces::DISPLAY_P3;
4265 if format == wgt::TextureFormat::Rgba16Float {
4281 color_spaces |= wgt::SurfaceColorSpaces::EXTENDED_SRGB
4282 | wgt::SurfaceColorSpaces::EXTENDED_DISPLAY_P3;
4283 }
4284 wgt::SurfaceFormatCapabilities {
4285 format,
4286 color_spaces,
4287 }
4288 })
4289 .collect(),
4290 formats,
4292 present_modes: vec![wgt::PresentMode::Fifo],
4294 alpha_modes: vec![wgt::CompositeAlphaMode::Opaque],
4295 usages: wgt::TextureUsages::RENDER_ATTACHMENT
4298 | wgt::TextureUsages::COPY_SRC
4299 | wgt::TextureUsages::COPY_DST
4300 | wgt::TextureUsages::TEXTURE_BINDING,
4301 }
4302 }
4303
4304 fn display_hdr_info(&self, _adapter: &dispatch::DispatchAdapter) -> wgt::DisplayHdrInfo {
4305 let high_dynamic_range = match_media_query("(dynamic-range: high)");
4311 let gamut = environment_color_gamut();
4312
4313 let coarse = if high_dynamic_range.is_some() || gamut.is_some() {
4314 Some(wgt::DisplayCoarseRange {
4315 high_dynamic_range,
4316 gamut,
4317 })
4318 } else {
4319 None
4320 };
4321 wgt::DisplayHdrInfo {
4322 luminance: None,
4323 headroom: None,
4324 chromaticity: None,
4325 coarse,
4326 bits_per_color: None,
4327 }
4328 }
4329
4330 fn configure(&self, device: &dispatch::DispatchDevice, config: &crate::SurfaceConfiguration) {
4331 let device = device.as_webgpu();
4332
4333 match self.canvas {
4334 Canvas::Canvas(ref canvas) => {
4335 canvas.set_width(config.width);
4336 canvas.set_height(config.height);
4337 }
4338 Canvas::Offscreen(ref canvas) => {
4339 canvas.set_width(config.width);
4340 canvas.set_height(config.height);
4341 }
4342 }
4343
4344 if let wgt::PresentMode::Mailbox | wgt::PresentMode::Immediate = config.present_mode {
4345 panic!("Only FIFO/Auto* is supported on web");
4346 }
4347 if let wgt::CompositeAlphaMode::PostMultiplied | wgt::CompositeAlphaMode::Inherit =
4348 config.alpha_mode
4349 {
4350 panic!("Only Opaque/Auto or PreMultiplied alpha mode are supported on web");
4351 }
4352 let alpha_mode = match config.alpha_mode {
4353 wgt::CompositeAlphaMode::PreMultiplied => webgpu_sys::GpuCanvasAlphaMode::Premultiplied,
4354 _ => webgpu_sys::GpuCanvasAlphaMode::Opaque,
4355 };
4356 let mapped = webgpu_sys::GpuCanvasConfiguration::new(
4357 &device.inner,
4358 map_texture_format(config.format),
4359 );
4360 mapped.set_usage(config.usage.bits());
4361 mapped.set_alpha_mode(alpha_mode);
4362 match config.color_space {
4363 wgt::SurfaceColorSpace::Auto | wgt::SurfaceColorSpace::Srgb => {}
4367 wgt::SurfaceColorSpace::DisplayP3 => {
4368 js_sys::Reflect::set(
4371 &mapped,
4372 &JsValue::from_str("colorSpace"),
4373 &JsValue::from_str("display-p3"),
4374 )
4375 .expect("Setting the canvas configuration color space should never fail");
4376 }
4377 wgt::SurfaceColorSpace::ExtendedSrgb => {
4378 let tone_mapping = webgpu_sys::GpuCanvasToneMapping::new();
4384 tone_mapping.set_mode(webgpu_sys::GpuCanvasToneMappingMode::Extended);
4385 mapped.set_tone_mapping(&tone_mapping);
4386 }
4387 wgt::SurfaceColorSpace::ExtendedDisplayP3 => {
4388 js_sys::Reflect::set(
4393 &mapped,
4394 &JsValue::from_str("colorSpace"),
4395 &JsValue::from_str("display-p3"),
4396 )
4397 .expect("Setting the canvas configuration color space should never fail");
4398 let tone_mapping = webgpu_sys::GpuCanvasToneMapping::new();
4399 tone_mapping.set_mode(webgpu_sys::GpuCanvasToneMappingMode::Extended);
4400 mapped.set_tone_mapping(&tone_mapping);
4401 }
4402 cs @ (wgt::SurfaceColorSpace::ExtendedSrgbLinear
4403 | wgt::SurfaceColorSpace::Bt2100Pq
4404 | wgt::SurfaceColorSpace::Bt2100Hlg) => {
4405 self.configure_failed.set(true);
4414 log::error!(
4415 "Surface color space {cs:?} is not supported on the WebGPU backend; \
4416 the surface will report as lost. Check `get_capabilities` before \
4417 configuring."
4418 );
4419 return;
4420 }
4421 }
4422 let mapped_view_formats = config
4423 .view_formats
4424 .iter()
4425 .map(|format| {
4426 wasm_bindgen::JsValue::from(map_texture_format(*format))
4427 .dyn_into::<js_sys::JsString>()
4428 .unwrap()
4429 })
4430 .collect::<Vec<js_sys::JsString>>();
4431 mapped.set_view_formats(&mapped_view_formats);
4432 match self.context.configure(&mapped) {
4443 Ok(()) => self.configure_failed.set(false),
4444 Err(err) => {
4445 self.configure_failed.set(true);
4446 log::error!(
4447 "Surface configuration failed: {err:?}. The browser may not support \
4448 this canvas format (for example, Firefox does not yet support \
4449 `rgba16float` canvases). The surface will report as lost until it \
4450 is successfully reconfigured."
4451 );
4452 }
4453 }
4454 }
4455
4456 fn get_current_texture(
4457 &self,
4458 ) -> (
4459 Option<dispatch::DispatchTexture>,
4460 crate::SurfaceStatus,
4461 dispatch::DispatchSurfaceOutputDetail,
4462 ) {
4463 let detail = WebSurfaceOutputDetail {
4464 ident: crate::cmp::Identifier::create(),
4465 };
4466
4467 if self.configure_failed.get() {
4470 return (None, crate::SurfaceStatus::Lost, detail.into());
4471 }
4472
4473 let surface_texture = match self.context.get_current_texture() {
4474 Ok(surface_texture) => surface_texture,
4475 Err(err) => {
4476 log::error!("`getCurrentTexture` failed: {err:?}");
4477 return (None, crate::SurfaceStatus::Lost, detail.into());
4478 }
4479 };
4480
4481 let web_surface_texture = WebTexture {
4482 inner: surface_texture,
4483 drop_guard: None,
4484 ident: crate::cmp::Identifier::create(),
4485 };
4486
4487 (
4488 Some(web_surface_texture.into()),
4489 crate::SurfaceStatus::Good,
4490 detail.into(),
4491 )
4492 }
4493}
4494impl Drop for WebSurface {
4495 fn drop(&mut self) {
4496 }
4498}
4499
4500impl dispatch::SurfaceOutputDetailInterface for WebSurfaceOutputDetail {
4501 fn texture_discard(&self) {
4502 }
4504
4505 fn texture_release(&self) {
4506 }
4508}
4509impl Drop for WebSurfaceOutputDetail {
4510 fn drop(&mut self) {
4511 }
4513}
4514
4515impl WebBufferMappedRange {
4516 fn get_temporary_mapping(&self) -> &[u8] {
4517 self.temporary_mapping
4518 .get_or_init(|| self.actual_mapping.to_vec())
4519 }
4520}
4521impl dispatch::BufferMappedRangeInterface for WebBufferMappedRange {
4522 fn len(&self) -> usize {
4523 self.get_temporary_mapping().len()
4524 }
4525
4526 #[inline]
4527 unsafe fn read_slice(&self) -> &[u8] {
4528 self.get_temporary_mapping()
4529 }
4530
4531 #[inline]
4532 unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {
4533 self.temporary_mapping_modified = true;
4534 self.get_temporary_mapping();
4535 let t: &mut Vec<u8> = self.temporary_mapping.get_mut().unwrap();
4536 WriteOnly::from_mut(t)
4537 }
4538
4539 #[inline]
4540 fn as_uint8array(&self) -> &js_sys::Uint8Array {
4541 &self.actual_mapping
4542 }
4543}
4544impl Drop for WebBufferMappedRange {
4545 fn drop(&mut self) {
4546 if !self.temporary_mapping_modified {
4547 return;
4550 }
4551
4552 let temporary_mapping_slice = self.temporary_mapping.get().unwrap().as_slice();
4555 unsafe {
4556 self.actual_mapping
4559 .set(&js_sys::Uint8Array::view(temporary_mapping_slice), 0);
4560 }
4561 }
4562}
4563
4564impl dispatch::QueueWriteBufferInterface for WebQueueWriteBuffer {
4565 #[inline]
4566 fn len(&self) -> usize {
4567 self.inner.len()
4568 }
4569
4570 #[inline]
4571 unsafe fn write_slice(&mut self) -> WriteOnly<'_, [u8]> {
4572 WriteOnly::from_mut(&mut *self.inner)
4573 }
4574}
4575impl Drop for WebQueueWriteBuffer {
4576 fn drop(&mut self) {
4577 }
4581}
4582
4583fn insert_constants_map(target: &JsValue, map: &[(&str, f64)]) {
4591 if !map.is_empty() {
4592 js_sys::Reflect::set(
4593 target,
4594 &JsValue::from_str("constants"),
4595 &hashmap_to_jsvalue(map),
4596 )
4597 .expect("Setting the values in a Javascript pipeline descriptor should never fail");
4598 }
4599}
4600
4601fn hashmap_to_jsvalue(map: &[(&str, f64)]) -> JsValue {
4603 let obj = js_sys::Object::new();
4604
4605 for &(key, v) in map.iter() {
4606 js_sys::Reflect::set(&obj, &JsValue::from_str(key), &JsValue::from_f64(v))
4607 .expect("Setting the values in a Javascript map should never fail");
4608 }
4609
4610 JsValue::from(obj)
4611}