1use std::borrow::Cow;
8use std::slice;
9use std::sync::{Arc, Mutex};
10
11use log::{info, warn};
12use paint_api::{CrossProcessPaintApi, WebRenderExternalImageIdManager, WebRenderImageHandlerType};
13use rustc_hash::FxHashMap;
14use servo_base::generic_channel::{GenericReceiver, GenericSender, GenericSharedMemory};
15use servo_base::id::PipelineId;
16use servo_config::pref;
17use webgpu_traits::{
18 Adapter, DeviceLostReason, Error, ErrorScope, Mapping, Pipeline, PopError,
19 ShaderCompilationInfo, WebGPU, WebGPUAdapter, WebGPUContextId, WebGPUDevice, WebGPUMsg,
20 WebGPUQueue, WebGPURequest, apply_render_bundle_command, apply_render_command,
21};
22use webrender_api::ExternalImageId;
23use wgc::command::ComputePassDescriptor;
24use wgc::device::DeviceDescriptor;
25use wgc::id;
26use wgc::id::DeviceId;
27use wgc::pipeline::ShaderModuleDescriptor;
28use wgc::resource::BufferMapOperation;
29pub use wgpu_core as wgc;
30use wgpu_core::command::{RenderPassDescriptor, TexelCopyTextureInfo};
31use wgpu_core::resource::{BufferAccessResult, TextureViewDescriptor};
32pub use wgpu_types as wgt;
33use wgpu_types::error::WebGpuError;
34use wgpu_types::{
35 ExperimentalFeatures, Extent3d, ExternalTextureDescriptor, ExternalTextureFormat,
36 ExternalTextureTransferFunction, MemoryHints, Origin3d, TexelCopyBufferLayout, TextureAspect,
37 TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
38};
39use wgt::InstanceDescriptor;
40
41use crate::canvas_context::WebGpuExternalImageMap;
42use crate::poll_thread::Poller;
43
44#[derive(Eq, Hash, PartialEq)]
45pub(crate) struct DeviceScope {
46 pub device_id: DeviceId,
47 pub pipeline_id: PipelineId,
48 pub error_scope_stack: Option<Vec<ErrorScope>>,
52 }
58
59impl DeviceScope {
60 pub fn new(device_id: DeviceId, pipeline_id: PipelineId) -> Self {
61 Self {
62 device_id,
63 pipeline_id,
64 error_scope_stack: Some(Vec::new()),
65 }
66 }
67}
68
69#[expect(clippy::upper_case_acronyms)] pub(crate) struct WGPU {
71 receiver: GenericReceiver<WebGPURequest>,
72 sender: GenericSender<WebGPURequest>,
73 pub(crate) script_sender: GenericSender<WebGPUMsg>,
74 pub(crate) global: Arc<wgc::global::Global>,
75 devices: Arc<Mutex<FxHashMap<DeviceId, DeviceScope>>>,
76 pub(crate) paint_api: CrossProcessPaintApi,
77 pub(crate) webrender_external_image_id_manager: WebRenderExternalImageIdManager,
78 pub(crate) wgpu_image_map: WebGpuExternalImageMap,
79 pub(crate) poller: Poller,
81}
82
83impl WGPU {
84 pub(crate) fn new(
85 receiver: GenericReceiver<WebGPURequest>,
86 sender: GenericSender<WebGPURequest>,
87 script_sender: GenericSender<WebGPUMsg>,
88 paint_api: CrossProcessPaintApi,
89 webrender_external_image_id_manager: WebRenderExternalImageIdManager,
90 wgpu_image_map: WebGpuExternalImageMap,
91 ) -> Self {
92 let backend_pref = pref!(dom_webgpu_wgpu_backend);
93 let backends = if backend_pref.is_empty() {
94 wgt::Backends::PRIMARY
95 } else {
96 info!(
97 "Selecting backends based on dom.webgpu.wgpu_backend pref: {:?}",
98 backend_pref
99 );
100 wgt::Backends::from_comma_list(&backend_pref)
101 };
102 let global = Arc::new(wgc::global::Global::new(
103 "wgpu-core",
104 InstanceDescriptor {
105 backends,
106 backend_options: wgt::BackendOptions {
107 gl: wgt::GlBackendOptions {
108 gles_minor_version: wgt::Gles3MinorVersion::Automatic,
109 fence_behavior: wgt::GlFenceBehavior::Normal,
110 debug_fns: wgt::GlDebugFns::Auto,
111 },
112 dx12: wgt::Dx12BackendOptions {
113 ..Default::default()
114 },
115 noop: wgt::NoopBackendOptions::default(),
116 },
117
118 flags: wgt::InstanceFlags::from_build_config() |
119 wgt::InstanceFlags::AUTOMATIC_TIMESTAMP_NORMALIZATION |
120 wgt::InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
121 memory_budget_thresholds: wgt::MemoryBudgetThresholds {
124 for_resource_creation: Some(95),
125 for_device_loss: Some(99),
126 },
127 display: None,
128 },
129 None,
130 ));
131 WGPU {
132 poller: Poller::new(Arc::clone(&global)),
133 receiver,
134 sender,
135 script_sender,
136 global,
137 devices: Arc::new(Mutex::new(FxHashMap::default())),
138 paint_api,
139 webrender_external_image_id_manager,
140 wgpu_image_map,
141 }
142 }
143
144 pub(crate) fn run(&mut self) {
145 loop {
146 if let Ok(msg) = self.receiver.recv() {
147 log::trace!("recv: {msg:?}");
148 match msg {
149 WebGPURequest::SetImageKey {
150 context_id,
151 image_key,
152 } => self.set_image_key(context_id, image_key),
153 WebGPURequest::BufferMapAsync {
154 callback: sender,
155 buffer_id,
156 device_id,
157 host_map,
158 offset,
159 size,
160 } => {
161 let glob = Arc::clone(&self.global);
162 let resp_sender = sender.clone();
163 let token = self.poller.token();
164 let callback = Box::from(move |result: BufferAccessResult| {
165 drop(token);
166 let response = result.and_then(|_| {
167 let global = &glob;
168 let (slice_pointer, range_size) =
169 global.buffer_get_mapped_range(buffer_id, offset, size)?;
170 let data = unsafe {
172 slice::from_raw_parts(
173 slice_pointer.as_ptr(),
174 range_size as usize,
175 )
176 };
177
178 Ok(Mapping {
179 data: GenericSharedMemory::from_bytes(data),
180 range: offset..offset + range_size,
181 mode: host_map,
182 })
183 });
184 if let Err(e) = resp_sender.send(response) {
185 warn!("Could not send BufferMapAsync Response ({})", e);
186 }
187 });
188
189 let operation = BufferMapOperation {
190 host: host_map,
191 callback: Some(callback),
192 };
193 let global = &self.global;
194 let result = global.buffer_map_async(buffer_id, offset, size, operation);
195 self.poller.wake();
196 self.maybe_dispatch_wgpu_error(device_id, result.err());
198 },
199 WebGPURequest::CommandEncoderFinish {
200 command_encoder_id,
201 device_id,
202 desc,
203 command_buffer_id,
204 } => {
205 let global = &self.global;
206 let (_, error) = global.command_encoder_finish(
207 command_encoder_id,
208 &desc,
209 Some(command_buffer_id),
210 );
211 self.maybe_dispatch_wgpu_error(device_id, error.map(|(_, e)| e));
212 },
213 WebGPURequest::CopyBufferToBuffer {
214 device_id,
215 command_encoder_id,
216 source_id,
217 source_offset,
218 destination_id,
219 destination_offset,
220 size,
221 } => {
222 let global = &self.global;
223 let result = global.command_encoder_copy_buffer_to_buffer(
224 command_encoder_id,
225 source_id,
226 source_offset,
227 destination_id,
228 destination_offset,
229 Some(size),
230 );
231 self.maybe_dispatch_wgpu_error(device_id, result.err());
232 },
233 WebGPURequest::CopyBufferToTexture {
234 device_id,
235 command_encoder_id,
236 source,
237 destination,
238 copy_size,
239 } => {
240 let global = &self.global;
241 let result = global.command_encoder_copy_buffer_to_texture(
242 command_encoder_id,
243 &source,
244 &destination,
245 ©_size,
246 );
247 self.maybe_dispatch_wgpu_error(device_id, result.err());
248 },
249 WebGPURequest::CopyTextureToBuffer {
250 device_id,
251 command_encoder_id,
252 source,
253 destination,
254 copy_size,
255 } => {
256 let global = &self.global;
257 let result = global.command_encoder_copy_texture_to_buffer(
258 command_encoder_id,
259 &source,
260 &destination,
261 ©_size,
262 );
263 self.maybe_dispatch_wgpu_error(device_id, result.err());
264 },
265 WebGPURequest::CopyTextureToTexture {
266 device_id,
267 command_encoder_id,
268 source,
269 destination,
270 copy_size,
271 } => {
272 let global = &self.global;
273 let result = global.command_encoder_copy_texture_to_texture(
274 command_encoder_id,
275 &source,
276 &destination,
277 ©_size,
278 );
279 self.maybe_dispatch_wgpu_error(device_id, result.err());
280 },
281 WebGPURequest::CommandEncoderPushDebugGroup {
282 device_id,
283 command_encoder_id,
284 label,
285 } => {
286 let result = self
287 .global
288 .command_encoder_push_debug_group(command_encoder_id, &label);
289 self.maybe_dispatch_wgpu_error(device_id, result.err());
290 },
291 WebGPURequest::CommandEncoderPopDebugGroup {
292 device_id,
293 command_encoder_id,
294 } => {
295 let result = self
296 .global
297 .command_encoder_pop_debug_group(command_encoder_id);
298 self.maybe_dispatch_wgpu_error(device_id, result.err());
299 },
300 WebGPURequest::CommandEncoderInsertDebugMarker {
301 device_id,
302 command_encoder_id,
303 label,
304 } => {
305 let result = self
306 .global
307 .command_encoder_insert_debug_marker(command_encoder_id, &label);
308 self.maybe_dispatch_wgpu_error(device_id, result.err());
309 },
310 WebGPURequest::CreateBindGroup {
311 device_id,
312 bind_group_id,
313 descriptor,
314 } => {
315 let global = &self.global;
316 let (_, error) = global.device_create_bind_group(
317 device_id,
318 &descriptor,
319 Some(bind_group_id),
320 );
321 self.maybe_dispatch_wgpu_error(device_id, error);
322 },
323 WebGPURequest::CreateBindGroupLayout {
324 device_id,
325 bind_group_layout_id,
326 descriptor,
327 } => {
328 let global = &self.global;
329 if let Some(desc) = descriptor {
330 let (_, error) = global.device_create_bind_group_layout(
331 device_id,
332 &desc,
333 Some(bind_group_layout_id),
334 );
335
336 self.maybe_dispatch_wgpu_error(device_id, error);
337 }
338 },
339 WebGPURequest::CreateBuffer {
340 device_id,
341 buffer_id,
342 descriptor,
343 } => {
344 let global = &self.global;
345 let (_, error) =
346 global.device_create_buffer(device_id, &descriptor, Some(buffer_id));
347
348 self.maybe_dispatch_wgpu_error(device_id, error);
349 },
350 WebGPURequest::CreateCommandEncoder {
351 device_id,
352 command_encoder_id,
353 desc,
354 } => {
355 let global = &self.global;
356 let (_, error) = global.device_create_command_encoder(
357 device_id,
358 &desc,
359 Some(command_encoder_id),
360 );
361
362 self.maybe_dispatch_wgpu_error(device_id, error);
363 },
364 WebGPURequest::CreateComputePipeline {
365 device_id,
366 compute_pipeline_id,
367 descriptor,
368 async_sender: sender,
369 } => {
370 let global = &self.global;
371 let (_, error) = global.device_create_compute_pipeline(
372 device_id,
373 &descriptor,
374 Some(compute_pipeline_id),
375 );
376 if let Some(sender) = sender {
377 let res = match error.and_then(Error::from_wgpu_error) {
378 None => Ok(Pipeline {
380 id: compute_pipeline_id,
381 label: descriptor.label.unwrap_or_default().to_string(),
382 }),
383 Some(e) => Err(e),
384 };
385 if let Err(e) = sender.send(res) {
386 warn!("Failed sending WebGPUComputePipelineResponse {e:?}");
387 }
388 } else {
389 self.maybe_dispatch_wgpu_error(device_id, error);
390 }
391 },
392 WebGPURequest::CreatePipelineLayout {
393 device_id,
394 pipeline_layout_id,
395 descriptor,
396 } => {
397 let global = &self.global;
398 let (_, error) = global.device_create_pipeline_layout(
399 device_id,
400 &descriptor,
401 Some(pipeline_layout_id),
402 );
403 self.maybe_dispatch_wgpu_error(device_id, error);
404 },
405 WebGPURequest::CreateRenderPipeline {
406 device_id,
407 render_pipeline_id,
408 descriptor,
409 async_sender: sender,
410 } => {
411 let global = &self.global;
412 let (_, error) = global.device_create_render_pipeline(
413 device_id,
414 &descriptor,
415 Some(render_pipeline_id),
416 );
417
418 if let Some(sender) = sender {
419 let res = match error.and_then(Error::from_wgpu_error) {
420 None => Ok(Pipeline {
422 id: render_pipeline_id,
423 label: descriptor.label.unwrap_or_default().to_string(),
424 }),
425 Some(e) => Err(e),
426 };
427 if let Err(e) = sender.send(res) {
428 warn!("Failed sending WebGPURenderPipelineResponse {e:?}");
429 }
430 } else {
431 self.maybe_dispatch_wgpu_error(device_id, error);
432 }
433 },
434 WebGPURequest::CreateSampler {
435 device_id,
436 sampler_id,
437 descriptor,
438 } => {
439 let global = &self.global;
440 let (_, error) =
441 global.device_create_sampler(device_id, &descriptor, Some(sampler_id));
442 self.maybe_dispatch_wgpu_error(device_id, error);
443 },
444 WebGPURequest::CreateShaderModule {
445 device_id,
446 program_id,
447 program,
448 label,
449 callback: sender,
450 } => {
451 let global = &self.global;
452 let source =
453 wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&program));
454 let desc = ShaderModuleDescriptor {
455 label: label.map(|s| s.into()),
456 runtime_checks: wgt::ShaderRuntimeChecks::checked(),
457 };
458 let (_, error) = global.device_create_shader_module(
459 device_id,
460 &desc,
461 source,
462 Some(program_id),
463 );
464 if let Err(e) = sender.send(
465 error
466 .as_ref()
467 .map(|e| ShaderCompilationInfo::from(e, &program)),
468 ) {
469 warn!("Failed to send CompilationInfo {e:?}");
470 }
471 self.maybe_dispatch_wgpu_error(device_id, error);
472 },
473 WebGPURequest::CreateContext {
474 buffer_ids,
475 size,
476 sender,
477 } => {
478 let id = self
479 .webrender_external_image_id_manager
480 .next_id(WebRenderImageHandlerType::WebGpu);
481 let context_id = WebGPUContextId(id.0);
482
483 if let Err(error) = sender.send(context_id) {
484 warn!("Failed to send ContextId to new context ({error})");
485 };
486
487 self.create_context(context_id, size, buffer_ids);
488 },
489 WebGPURequest::Present {
490 context_id,
491 pending_texture,
492 size,
493 canvas_epoch,
494 } => {
495 self.present(context_id, pending_texture, size, canvas_epoch);
496 },
497 WebGPURequest::GetImage {
498 context_id,
499 pending_texture,
500 sender,
501 } => self.get_image(context_id, pending_texture, sender),
502 WebGPURequest::ValidateTextureDescriptor {
503 device_id,
504 texture_id,
505 descriptor,
506 } => {
507 let global = &self.global;
510 let (_, error) =
511 global.device_create_texture(device_id, &descriptor, Some(texture_id));
512 global.texture_drop(texture_id);
513 self.poller.wake();
514 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(texture_id))
515 {
516 warn!("Unable to send FreeTexture({:?}) ({:?})", texture_id, e);
517 };
518 self.maybe_dispatch_wgpu_error(device_id, error);
519 },
520 WebGPURequest::DestroyContext { context_id } => {
521 self.destroy_context(context_id);
522 self.webrender_external_image_id_manager
523 .remove(&ExternalImageId(context_id.0));
524 },
525 WebGPURequest::CreateTexture {
526 device_id,
527 texture_id,
528 descriptor,
529 } => {
530 let global = &self.global;
531 let (_, error) =
532 global.device_create_texture(device_id, &descriptor, Some(texture_id));
533 self.maybe_dispatch_wgpu_error(device_id, error);
534 },
535 WebGPURequest::CreateTextureView {
536 texture_id,
537 texture_view_id,
538 device_id,
539 descriptor,
540 } => {
541 let global = &self.global;
542 if let Some(desc) = descriptor {
543 let (_, error) = global.texture_create_view(
544 texture_id,
545 &desc,
546 Some(texture_view_id),
547 );
548
549 self.maybe_dispatch_wgpu_error(device_id, error);
550 }
551 },
552 WebGPURequest::DestroyBuffer(buffer) => {
553 let global = &self.global;
554 global.buffer_destroy(buffer);
555 },
556 WebGPURequest::DestroyDevice(device) => {
557 let global = &self.global;
558 global.device_destroy(device);
559 self.poller.wake();
561 },
562 WebGPURequest::DestroyTexture(texture_id) => {
563 let global = &self.global;
564 global.texture_destroy(texture_id);
565 },
566 WebGPURequest::Exit(sender) => {
567 if let Err(e) = sender.send(()) {
568 warn!("Failed to send response to WebGPURequest::Exit ({})", e)
569 }
570 break;
571 },
572 WebGPURequest::DropCommandEncoder(id) => {
573 let global = &self.global;
574 global.command_encoder_drop(id);
575 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandEncoder(id)) {
576 warn!("Unable to send FreeCommandEncoder({:?}) ({:?})", id, e);
577 };
578 },
579 WebGPURequest::DropCommandBuffer(id) => {
580 let global = &self.global;
581 global.command_buffer_drop(id);
582 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandBuffer(id)) {
583 warn!("Unable to send FreeCommandBuffer({:?}) ({:?})", id, e);
584 };
585 },
586 WebGPURequest::DropDevice(device_id) => {
587 let global = &self.global;
588 global.device_drop(device_id);
589 let device_scope = self
590 .devices
591 .lock()
592 .unwrap()
593 .remove(&device_id)
594 .expect("Device should not be dropped by this point");
595 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeDevice {
596 device_id,
597 pipeline_id: device_scope.pipeline_id,
598 }) {
599 warn!("Unable to send FreeDevice({:?}) ({:?})", device_id, e);
600 };
601 },
602 WebGPURequest::RequestAdapter {
603 sender,
604 options,
605 adapter_id,
606 } => {
607 let global = &self.global;
608 let response = self
609 .global
610 .request_adapter(&options, wgt::Backends::all(), Some(adapter_id))
611 .map(|adapter_id| {
612 let adapter_info = global.adapter_get_info(adapter_id);
614 let limits = global.adapter_limits(adapter_id);
615 let features = global.adapter_features(adapter_id);
616 Adapter {
617 adapter_info,
618 adapter_id: WebGPUAdapter(adapter_id),
619 features,
620 limits,
621 channel: WebGPU(self.sender.clone()),
622 }
623 })
624 .map_err(|err| err.to_string());
625
626 if let Err(e) = sender.send(Some(response)) {
627 warn!(
628 "Failed to send response to WebGPURequest::RequestAdapter ({})",
629 e
630 )
631 }
632 },
633 WebGPURequest::RequestDevice {
634 sender,
635 adapter_id,
636 descriptor,
637 device_id,
638 queue_id,
639 pipeline_id,
640 } => {
641 let mut desc = DeviceDescriptor {
642 label: descriptor.label.as_ref().map(crate::Cow::from),
643 required_features: descriptor.required_features,
644 required_limits: descriptor.required_limits.clone(),
645 memory_hints: MemoryHints::MemoryUsage,
646 trace: wgpu_types::Trace::Off,
647 experimental_features: ExperimentalFeatures::disabled(),
648 };
649 let global = &self.global;
650 let features = global.adapter_features(adapter_id.0);
652 if features.contains(wgpu_types::Features::EXTERNAL_TEXTURE) {
653 desc.required_features |= wgpu_types::Features::EXTERNAL_TEXTURE;
654 }
655 let device = WebGPUDevice(device_id);
656 let queue = WebGPUQueue(queue_id);
657 let result = global
658 .adapter_request_device(
659 adapter_id.0,
660 &desc,
661 Some(device_id),
662 Some(queue_id),
663 )
664 .map(|_| {
665 {
666 self.devices.lock().unwrap().insert(
667 device_id,
668 DeviceScope::new(device_id, pipeline_id),
669 );
670 }
671 let script_sender = self.script_sender.clone();
672 let devices = Arc::clone(&self.devices);
673 let callback = Box::from(move |reason, msg| {
674 let reason = match reason {
675 wgt::DeviceLostReason::Unknown => DeviceLostReason::Unknown,
676 wgt::DeviceLostReason::Destroyed => {
677 DeviceLostReason::Destroyed
678 },
679 };
680 let _ = devices
682 .lock()
683 .unwrap()
684 .get_mut(&device_id)
685 .expect("Device should not be dropped by this point")
686 .error_scope_stack
687 .take();
688 if let Err(e) = script_sender.send(WebGPUMsg::DeviceLost {
689 device,
690 pipeline_id,
691 reason,
692 msg,
693 }) {
694 warn!("Failed to send WebGPUMsg::DeviceLost: {e}");
695 }
696 });
697 global.device_set_device_lost_closure(device_id, callback);
698 let mut descriptor = descriptor;
699 descriptor.required_limits = global.device_limits(device_id);
700 descriptor.required_features = global.device_features(device_id);
701 descriptor
702 })
703 .map_err(Into::into);
704
705 if let Err(e) = sender.send((device, queue, result)) {
706 warn!(
707 "Failed to send response to WebGPURequest::RequestDevice ({})",
708 e
709 )
710 }
711 },
712 WebGPURequest::BeginComputePass {
713 command_encoder_id,
714 compute_pass_id,
715 label,
716 timestamp_writes,
717 device_id,
718 } => {
719 let global = &self.global;
720 let (_, error) = global.command_encoder_begin_compute_pass_with_id(
721 command_encoder_id,
722 &ComputePassDescriptor {
723 label,
724 timestamp_writes,
725 },
726 Some(compute_pass_id),
727 );
728 self.maybe_dispatch_wgpu_error(device_id, error);
729 },
730 WebGPURequest::ComputePassSetPipeline {
731 compute_pass_id,
732 pipeline_id,
733 device_id,
734 } => {
735 let result = self
736 .global
737 .compute_pass_set_pipeline_with_id(compute_pass_id, pipeline_id);
738 self.maybe_dispatch_wgpu_error(device_id, result.err());
739 },
740 WebGPURequest::ComputePassSetBindGroup {
741 compute_pass_id,
742 index,
743 bind_group_id,
744 offsets,
745 device_id,
746 } => {
747 let result = self.global.compute_pass_set_bind_group_with_id(
748 compute_pass_id,
749 index,
750 Some(bind_group_id),
751 &offsets,
752 );
753 self.maybe_dispatch_wgpu_error(device_id, result.err());
754 },
755 WebGPURequest::ComputePassDispatchWorkgroups {
756 compute_pass_id,
757 x,
758 y,
759 z,
760 device_id,
761 } => {
762 let result = self.global.compute_pass_dispatch_workgroups_with_id(
763 compute_pass_id,
764 x,
765 y,
766 z,
767 );
768 self.maybe_dispatch_wgpu_error(device_id, result.err());
769 },
770 WebGPURequest::ComputePassDispatchWorkgroupsIndirect {
771 compute_pass_id,
772 buffer_id,
773 offset,
774 device_id,
775 } => {
776 let result = self
777 .global
778 .compute_pass_dispatch_workgroups_indirect_with_id(
779 compute_pass_id,
780 buffer_id,
781 offset,
782 );
783 self.maybe_dispatch_wgpu_error(device_id, result.err());
784 },
785 WebGPURequest::ComputePassPushDebugGroup {
786 compute_pass_id,
787 label,
788 device_id,
789 } => {
790 let result = self.global.compute_pass_push_debug_group_with_id(
791 compute_pass_id,
792 &label,
793 0,
794 );
795 self.maybe_dispatch_wgpu_error(device_id, result.err());
796 },
797 WebGPURequest::ComputePassPopDebugGroup {
798 compute_pass_id,
799 device_id,
800 } => {
801 let result = self
802 .global
803 .compute_pass_pop_debug_group_with_id(compute_pass_id);
804 self.maybe_dispatch_wgpu_error(device_id, result.err());
805 },
806 WebGPURequest::ComputePassInsertDebugMarker {
807 compute_pass_id,
808 label,
809 device_id,
810 } => {
811 let result = self.global.compute_pass_insert_debug_marker_with_id(
812 compute_pass_id,
813 &label,
814 0,
815 );
816 self.maybe_dispatch_wgpu_error(device_id, result.err());
817 },
818 WebGPURequest::EndComputePass {
819 compute_pass_id,
820 device_id,
821 } => {
822 let result = self.global.compute_pass_end_with_id(compute_pass_id);
824 self.maybe_dispatch_wgpu_error(device_id, result.err());
825 },
826 WebGPURequest::BeginRenderPass {
827 command_encoder_id,
828 render_pass_id,
829 label,
830 color_attachments,
831 depth_stencil_attachment,
832 timestamp_writes,
833 device_id,
834 } => {
835 let global = &self.global;
836 let desc = &RenderPassDescriptor {
837 label,
838 color_attachments: color_attachments.into(),
839 depth_stencil_attachment,
840 timestamp_writes,
841 occlusion_query_set: None,
842 multiview_mask: None,
843 };
844 let (_, error) = global.command_encoder_begin_render_pass_with_id(
845 command_encoder_id,
846 desc,
847 Some(render_pass_id),
848 );
849 self.maybe_dispatch_wgpu_error(device_id, error);
850 },
851 WebGPURequest::RenderPassCommand {
852 render_pass_id,
853 render_command,
854 device_id,
855 } => {
856 let result =
857 apply_render_command(&self.global, render_pass_id, render_command);
858 self.maybe_dispatch_wgpu_error(device_id, result.err());
859 },
860 WebGPURequest::EndRenderPass {
861 render_pass_id,
862 device_id,
863 } => {
864 let result = self.global.render_pass_end_with_id(render_pass_id);
866 self.maybe_dispatch_wgpu_error(device_id, result.err());
867 },
868 WebGPURequest::Submit {
869 device_id,
870 queue_id,
871 command_buffers,
872 } => {
873 let global = &self.global;
874 let result = {
875 let _guard = self.poller.lock();
876 global.queue_submit(queue_id, &command_buffers)
877 };
878 self.maybe_dispatch_wgpu_error(device_id, result.err().map(|(_, x)| x));
879 },
880 WebGPURequest::UnmapBuffer { buffer_id, mapping } => {
881 let global = &self.global;
882 if let Some(mapping) = mapping &&
883 let Ok((slice_pointer, range_size)) = global.buffer_get_mapped_range(
884 buffer_id,
885 mapping.range.start,
886 Some(mapping.range.end - mapping.range.start),
887 )
888 {
889 unsafe {
890 slice::from_raw_parts_mut(
891 slice_pointer.as_ptr(),
892 range_size as usize,
893 )
894 }
895 .copy_from_slice(&mapping.data);
896 }
897 let _result = global.buffer_unmap(buffer_id);
899 },
900 WebGPURequest::WriteBuffer {
901 device_id,
902 queue_id,
903 buffer_id,
904 buffer_offset,
905 data,
906 } => {
907 let global = &self.global;
908 let result = global.queue_write_buffer(
909 queue_id,
910 buffer_id,
911 buffer_offset as wgt::BufferAddress,
912 &data,
913 );
914 self.maybe_dispatch_wgpu_error(device_id, result.err());
915 },
916 WebGPURequest::WriteTexture {
917 device_id,
918 queue_id,
919 texture_cv,
920 data_layout,
921 size,
922 data,
923 } => {
924 let global = &self.global;
925 let _guard = self.poller.lock();
926 let result = global.queue_write_texture(
928 queue_id,
929 &texture_cv,
930 &data,
931 &data_layout,
932 &size,
933 );
934 drop(_guard);
935 self.maybe_dispatch_wgpu_error(device_id, result.err());
936 },
937 WebGPURequest::CopyExternalImageToTexture {
938 device_id,
939 queue_id,
940 usable_source,
941 destination,
942 dest_tex_descriptor,
943 copy_size,
944 } => {
945 let global = &self.global;
947 let Some(source) = usable_source else {
950 self.maybe_dispatch_error(
951 device_id,
952 Some(Error::Validation("Source is not usable".to_string())),
953 );
954 continue;
955 };
956 if !dest_tex_descriptor
958 .usage
959 .contains(TextureUsages::RENDER_ATTACHMENT)
960 {
961 self.maybe_dispatch_error(
962 device_id,
963 Some(Error::Validation(
964 "Texture usage must include RENDER_ATTACHMENT".to_string(),
965 )),
966 );
967 continue;
968 }
969 if dest_tex_descriptor.dimension != TextureDimension::D2 {
971 self.maybe_dispatch_error(
972 device_id,
973 Some(Error::Validation(
974 "Texture dimension must be 2d".to_string(),
975 )),
976 );
977 continue;
978 }
979 let _guard = self.poller.lock();
983 let result = global.queue_write_texture(
984 queue_id,
985 &destination,
986 source.data(),
987 &TexelCopyBufferLayout {
988 offset: 0,
989 bytes_per_row: Some(source.size().width * 4),
990 rows_per_image: None,
991 },
992 ©_size,
993 );
994 drop(_guard);
995 self.maybe_dispatch_wgpu_error(device_id, result.err());
996 },
997 WebGPURequest::QueueOnSubmittedWorkDone { sender, queue_id } => {
998 let global = &self.global;
999 let token = self.poller.token();
1000 let callback = Box::from(move || {
1001 drop(token);
1002 if let Err(e) = sender.send(()) {
1003 warn!("Could not send SubmittedWorkDone Response ({})", e);
1004 }
1005 });
1006 global.queue_on_submitted_work_done(queue_id, callback);
1007 self.poller.wake();
1008 },
1009 WebGPURequest::DropTexture(id) => {
1010 let global = &self.global;
1011 global.texture_drop(id);
1012 self.poller.wake();
1013 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) {
1014 warn!("Unable to send FreeTexture({:?}) ({:?})", id, e);
1015 };
1016 },
1017 WebGPURequest::DropAdapter(id) => {
1018 let global = &self.global;
1019 global.adapter_drop(id);
1020 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeAdapter(id)) {
1021 warn!("Unable to send FreeAdapter({:?}) ({:?})", id, e);
1022 };
1023 },
1024 WebGPURequest::DropBuffer(id) => {
1025 let global = &self.global;
1026 global.buffer_drop(id);
1027 self.poller.wake();
1028 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(id)) {
1029 warn!("Unable to send FreeBuffer({:?}) ({:?})", id, e);
1030 };
1031 },
1032 WebGPURequest::DropPipelineLayout(id) => {
1033 let global = &self.global;
1034 global.pipeline_layout_drop(id);
1035 if let Err(e) = self.script_sender.send(WebGPUMsg::FreePipelineLayout(id)) {
1036 warn!("Unable to send FreePipelineLayout({:?}) ({:?})", id, e);
1037 };
1038 },
1039 WebGPURequest::DropComputePipeline(id) => {
1040 let global = &self.global;
1041 global.compute_pipeline_drop(id);
1042 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePipeline(id))
1043 {
1044 warn!("Unable to send FreeComputePipeline({:?}) ({:?})", id, e);
1045 };
1046 },
1047 WebGPURequest::DropComputePass(id) => {
1048 self.global.compute_pass_drop(id);
1049 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePass(id)) {
1050 warn!("Unable to send FreeComputePass({:?}) ({:?})", id, e);
1051 };
1052 },
1053 WebGPURequest::DropRenderPass(id) => {
1054 self.global.render_pass_drop(id);
1055 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPass(id)) {
1056 warn!("Unable to send FreeRenderPass({:?}) ({:?})", id, e);
1057 };
1058 },
1059 WebGPURequest::DropRenderPipeline(id) => {
1060 let global = &self.global;
1061 global.render_pipeline_drop(id);
1062 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPipeline(id)) {
1063 warn!("Unable to send FreeRenderPipeline({:?}) ({:?})", id, e);
1064 };
1065 },
1066 WebGPURequest::DropBindGroup(id) => {
1067 let global = &self.global;
1068 global.bind_group_drop(id);
1069 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroup(id)) {
1070 warn!("Unable to send FreeBindGroup({:?}) ({:?})", id, e);
1071 };
1072 },
1073 WebGPURequest::DropBindGroupLayout(id) => {
1074 let global = &self.global;
1075 global.bind_group_layout_drop(id);
1076 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroupLayout(id))
1077 {
1078 warn!("Unable to send FreeBindGroupLayout({:?}) ({:?})", id, e);
1079 };
1080 },
1081 WebGPURequest::DropTextureView(id) => {
1082 let global = &self.global;
1083 global.texture_view_drop(id);
1084 self.poller.wake();
1085 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(id)) {
1086 warn!("Unable to send FreeTextureView({:?}) ({:?})", id, e);
1087 };
1088 },
1089 WebGPURequest::DropSampler(id) => {
1090 let global = &self.global;
1091 global.sampler_drop(id);
1092 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeSampler(id)) {
1093 warn!("Unable to send FreeSampler({:?}) ({:?})", id, e);
1094 };
1095 },
1096 WebGPURequest::DropShaderModule(id) => {
1097 let global = &self.global;
1098 global.shader_module_drop(id);
1099 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeShaderModule(id)) {
1100 warn!("Unable to send FreeShaderModule({:?}) ({:?})", id, e);
1101 };
1102 },
1103 WebGPURequest::DropRenderBundleEncoder(id) => {
1104 let global = &self.global;
1105 global.render_bundle_encoder_drop(id);
1106 if let Err(e) = self
1107 .script_sender
1108 .send(WebGPUMsg::FreeRenderBundleEncoder(id))
1109 {
1110 warn!("Unable to send FreeRenderBundleEncoder({:?}) ({:?})", id, e);
1111 };
1112 },
1113 WebGPURequest::DropRenderBundle(id) => {
1114 let global = &self.global;
1115 global.render_bundle_drop(id);
1116 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderBundle(id)) {
1117 warn!("Unable to send FreeRenderBundle({:?}) ({:?})", id, e);
1118 };
1119 },
1120 WebGPURequest::DropQuerySet(id) => {
1121 let global = &self.global;
1122 global.query_set_drop(id);
1123 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeQuerySet(id)) {
1124 warn!("Unable to send FreeQuerySet({:?}) ({:?})", id, e);
1125 };
1126 },
1127 WebGPURequest::PushErrorScope { device_id, filter } => {
1128 let mut devices = self.devices.lock().unwrap();
1130 let device_scope = devices
1131 .get_mut(&device_id)
1132 .expect("Device should not be dropped by this point");
1133 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1134 error_scope_stack.push(ErrorScope::new(filter));
1135 } },
1137 WebGPURequest::DispatchError { device_id, error } => {
1138 self.dispatch_error(device_id, error);
1139 },
1140 WebGPURequest::PopErrorScope {
1141 device_id,
1142 callback: sender,
1143 } => {
1144 let mut devices = self.devices.lock().unwrap();
1146 let device_scope = devices
1147 .get_mut(&device_id)
1148 .expect("Device should not be dropped by this point");
1149 let result =
1150 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1151 if let Some(error_scope) = error_scope_stack.pop() {
1152 Ok(
1153 error_scope.errors.first().cloned(),
1155 )
1156 } else {
1157 Err(PopError::Empty)
1158 }
1159 } else {
1160 Err(PopError::Lost)
1162 };
1163 if let Err(error) = sender.send(result) {
1164 warn!("Error while sending PopErrorScope result: {error}");
1165 }
1166 },
1167 WebGPURequest::ComputeGetBindGroupLayout {
1168 device_id,
1169 pipeline_id,
1170 index,
1171 id,
1172 } => {
1173 let global = &self.global;
1174 let (_, error) = global.compute_pipeline_get_bind_group_layout(
1175 pipeline_id,
1176 index,
1177 Some(id),
1178 );
1179 self.maybe_dispatch_wgpu_error(device_id, error);
1180 },
1181 WebGPURequest::RenderGetBindGroupLayout {
1182 device_id,
1183 pipeline_id,
1184 index,
1185 id,
1186 } => {
1187 let global = &self.global;
1188 let (_, error) = global.render_pipeline_get_bind_group_layout(
1189 pipeline_id,
1190 index,
1191 Some(id),
1192 );
1193 self.maybe_dispatch_wgpu_error(device_id, error);
1194 },
1195 WebGPURequest::CreateQuerySet {
1196 device_id,
1197 query_set_id,
1198 descriptor,
1199 } => {
1200 let global = &self.global;
1201 let (_, error) = global.device_create_query_set(
1202 device_id,
1203 &descriptor,
1204 Some(query_set_id),
1205 );
1206 self.maybe_dispatch_wgpu_error(device_id, error);
1207 },
1208 WebGPURequest::ResolveQuerySet {
1209 device_id,
1210 command_encoder_id,
1211 query_set_id,
1212 start_query,
1213 query_count,
1214 destination,
1215 destination_offset,
1216 } => {
1217 let global = &self.global;
1218 let result = global.command_encoder_resolve_query_set(
1219 command_encoder_id,
1220 query_set_id,
1221 start_query,
1222 query_count,
1223 destination,
1224 destination_offset,
1225 );
1226 self.maybe_dispatch_wgpu_error(device_id, result.err());
1227 },
1228 WebGPURequest::CreatePlanarTexture {
1229 device_id,
1230 size,
1231 format,
1232 texture_id,
1233 texture_view_id,
1234 } => {
1235 let (_, maybe_error) = self.global.device_create_texture(
1236 device_id,
1237 &TextureDescriptor {
1238 label: None,
1239 size: Extent3d {
1240 width: size.width,
1241 height: size.height,
1242 depth_or_array_layers: 1,
1243 },
1244 mip_level_count: 1,
1245 sample_count: 1,
1246 dimension: TextureDimension::D2,
1247 format: match format {
1248 pixels::SnapshotPixelFormat::RGBA => TextureFormat::Rgba8Unorm,
1249 pixels::SnapshotPixelFormat::BGRA => TextureFormat::Bgra8Unorm,
1250 },
1251 usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
1252 view_formats: Vec::new(),
1253 },
1254 Some(texture_id),
1255 );
1256 self.maybe_dispatch_error(
1257 device_id,
1258 maybe_error.map(|error| {
1259 Error::Internal(format!(
1260 "Failed to create planar texture: {error:?}"
1261 ))
1262 }),
1263 );
1264 let (_, maybe_error) = self.global.texture_create_view(
1265 texture_id,
1266 &TextureViewDescriptor {
1267 ..Default::default()
1268 },
1269 Some(texture_view_id),
1270 );
1271 self.maybe_dispatch_error(
1272 device_id,
1273 maybe_error.map(|error| {
1274 Error::Internal(format!(
1275 "Failed to create planar texture view: {error:?}"
1276 ))
1277 }),
1278 );
1279 },
1280 WebGPURequest::UpdatePlanarTexture {
1281 device_id,
1282 queue_id,
1283 texture_id,
1284 snapshot,
1285 } => {
1286 let result = self.global.queue_write_texture(
1287 queue_id,
1288 &TexelCopyTextureInfo {
1289 texture: texture_id,
1290 mip_level: 0,
1291 origin: Origin3d::ZERO,
1292 aspect: TextureAspect::All,
1293 },
1294 snapshot.data(),
1295 &TexelCopyBufferLayout {
1296 offset: 0,
1297 bytes_per_row: Some(snapshot.size().width * 4),
1298 rows_per_image: None,
1299 },
1300 &Extent3d {
1301 width: snapshot.size().width,
1302 height: snapshot.size().height,
1303 depth_or_array_layers: 1,
1304 },
1305 );
1306 self.maybe_dispatch_error(
1307 device_id,
1308 result.err().map(|error| {
1309 Error::Internal(format!(
1310 "Failed to write planar texture: {error:?}"
1311 ))
1312 }),
1313 );
1314 },
1315 WebGPURequest::DropPlanarTexture(id, view_id) => {
1316 self.global.texture_view_drop(view_id);
1317 self.global.texture_drop(id);
1318 self.poller.wake();
1319 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(view_id))
1320 {
1321 warn!("Unable to send FreeTextureView({:?}) ({:?})", view_id, e);
1322 };
1323 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) {
1324 warn!("Unable to send FreeTexture({:?}) ({:?})", id, e);
1325 };
1326 },
1327 WebGPURequest::ImportExternalTexture {
1328 device_id,
1329 external_texture_id,
1330 size,
1331 label,
1332 plane0,
1333 } => {
1334 let desc = ExternalTextureDescriptor {
1335 label: Some(label.into()),
1336 width: size.width,
1337 height: size.height,
1338 format: ExternalTextureFormat::Rgba,
1339 yuv_conversion_matrix: [0.; 16],
1340 gamut_conversion_matrix: [
1341 1., 0., 0., 0., 1., 0., 0., 0., 1., ],
1345 src_transfer_function: ExternalTextureTransferFunction::default(),
1346 dst_transfer_function: ExternalTextureTransferFunction::default(),
1347 sample_transform: [
1348 1., 0., 0., 1., 0., 0., ],
1352 load_transform: [
1353 1., 0., 0., 1., 0., 0., ],
1357 };
1358 if let Some(plane0) = plane0 {
1359 let (_, maybe_error) = self.global.device_create_external_texture(
1360 device_id,
1361 &desc,
1362 &[plane0],
1363 Some(external_texture_id),
1364 );
1365 self.maybe_dispatch_error(
1366 device_id,
1367 maybe_error.map(|error| {
1368 Error::Internal(format!(
1369 "Failed to import external texture: {error:?}"
1370 ))
1371 }),
1372 );
1373 } else {
1374 self.global
1375 .create_external_texture_error(Some(external_texture_id), &desc);
1376 self.maybe_dispatch_error(
1377 device_id,
1378 Some(Error::Validation("Usability is not good".to_string())),
1379 );
1380 }
1381 },
1382 WebGPURequest::DestroyExternalTexture(id) => {
1383 self.global.external_texture_destroy(id);
1384 },
1385 WebGPURequest::DropExternalTexture(id) => {
1386 self.global.external_texture_drop(id);
1387 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeExternalTexture(id))
1388 {
1389 warn!("Unable to send FreeExternalTexture({:?}) ({:?})", id, e);
1390 };
1391 },
1392 WebGPURequest::DestroyQuerySet(query_set_id) => {
1393 self.global.query_set_destroy(query_set_id);
1394 },
1395 WebGPURequest::RenderBundleEncoderFinish {
1396 render_bundle_encoder_id,
1397 descriptor,
1398 render_bundle_id,
1399 device_id,
1400 } => {
1401 let global = &self.global;
1402 let (_, error) = global.render_bundle_encoder_finish_with_id(
1403 render_bundle_encoder_id,
1404 &descriptor,
1405 Some(render_bundle_id),
1406 );
1407
1408 self.maybe_dispatch_wgpu_error(device_id, error);
1409 },
1410 WebGPURequest::CreateRenderBundleEncoder {
1411 device_id,
1412 render_bundle_encoder_id,
1413 desc,
1414 } => {
1415 let (_, error) = self.global.device_create_render_bundle_encoder_with_id(
1416 device_id,
1417 &desc,
1418 Some(render_bundle_encoder_id),
1419 );
1420 self.maybe_dispatch_wgpu_error(device_id, error);
1421 },
1422 WebGPURequest::RenderBundleEncoderCommand {
1423 render_bundle_encoder_id,
1424 render_command,
1425 device_id,
1426 } => {
1427 let result = apply_render_bundle_command(
1428 &self.global,
1429 render_bundle_encoder_id,
1430 render_command,
1431 );
1432 self.maybe_dispatch_wgpu_error(device_id, result.err());
1433 },
1434 }
1435 }
1436 }
1437 if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) {
1438 warn!("Failed to send WebGPUMsg::Exit to script ({})", e);
1439 }
1440 }
1441
1442 #[inline]
1443 fn maybe_dispatch_wgpu_error<E: WebGpuError>(
1444 &mut self,
1445 device_id: id::DeviceId,
1446 error: Option<E>,
1447 ) {
1448 self.maybe_dispatch_error(device_id, error.and_then(Error::from_wgpu_error))
1449 }
1450
1451 fn maybe_dispatch_error(&mut self, device_id: id::DeviceId, error: Option<Error>) {
1453 if let Some(error) = error {
1454 self.dispatch_error(device_id, error);
1455 }
1456 }
1457
1458 fn dispatch_error(&mut self, device_id: id::DeviceId, error: Error) {
1460 log::trace!("Dispatching error for device {:?}: {:?}", device_id, error);
1461 let mut devices = self.devices.lock().unwrap();
1462 let device_scope = devices
1463 .get_mut(&device_id)
1464 .expect("Device should not be dropped by this point");
1465 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1466 if let Some(error_scope) = error_scope_stack
1467 .iter_mut()
1468 .rev()
1469 .find(|error_scope| error_scope.filter == error.filter())
1470 {
1471 error_scope.errors.push(error);
1472 } else if self
1473 .script_sender
1474 .send(WebGPUMsg::UncapturedError {
1475 device: WebGPUDevice(device_id),
1476 pipeline_id: device_scope.pipeline_id,
1477 error: error.clone(),
1478 })
1479 .is_err()
1480 {
1481 warn!("Failed to send WebGPUMsg::UncapturedError: {error:?}");
1482 }
1483 } }
1485}