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