1use std::borrow::Cow;
8use std::slice;
9use std::sync::{Arc, Mutex};
10
11use base::id::PipelineId;
12use compositing_traits::{
13 CrossProcessCompositorApi, WebrenderExternalImageRegistry, WebrenderImageHandlerType,
14};
15use ipc_channel::ipc::{IpcReceiver, IpcSender, IpcSharedMemory};
16use log::{info, warn};
17use rustc_hash::FxHashMap;
18use servo_config::pref;
19use webgpu_traits::{
20 Adapter, ComputePassId, DeviceLostReason, Error, ErrorScope, Mapping, Pipeline, PopError,
21 RenderPassId, ShaderCompilationInfo, WebGPU, WebGPUAdapter, WebGPUContextId, WebGPUDevice,
22 WebGPUMsg, WebGPUQueue, WebGPURequest, apply_render_command,
23};
24use webrender_api::ExternalImageId;
25use wgc::command::{ComputePass, ComputePassDescriptor, RenderPass};
26use wgc::device::{DeviceDescriptor, ImplicitPipelineIds};
27use wgc::id;
28use wgc::id::DeviceId;
29use wgc::pipeline::ShaderModuleDescriptor;
30use wgc::resource::BufferMapOperation;
31use wgpu_core::command::RenderPassDescriptor;
32use wgpu_core::device::DeviceError;
33use wgpu_core::pipeline::{CreateComputePipelineError, CreateRenderPipelineError};
34use wgpu_core::resource::BufferAccessResult;
35use wgpu_types::MemoryHints;
36use wgt::InstanceDescriptor;
37pub use {wgpu_core as wgc, wgpu_types as wgt};
38
39use crate::canvas_context::WGPUImageMap;
40use crate::poll_thread::Poller;
41
42#[derive(Eq, Hash, PartialEq)]
43pub(crate) struct DeviceScope {
44 pub device_id: DeviceId,
45 pub pipeline_id: PipelineId,
46 pub error_scope_stack: Option<Vec<ErrorScope>>,
50 }
56
57impl DeviceScope {
58 pub fn new(device_id: DeviceId, pipeline_id: PipelineId) -> Self {
59 Self {
60 device_id,
61 pipeline_id,
62 error_scope_stack: Some(Vec::new()),
63 }
64 }
65}
66
67#[derive(Debug, Default, Eq, PartialEq)]
69enum Pass<P> {
70 Open {
72 pass: P,
74 valid: bool,
77 },
78 #[default]
80 Ended,
81}
82
83impl<P> Pass<P> {
84 fn new(pass: P, valid: bool) -> Self {
86 Self::Open { pass, valid }
87 }
88
89 fn take(&mut self) -> Self {
91 std::mem::take(self)
92 }
93}
94
95#[allow(clippy::upper_case_acronyms)] pub(crate) struct WGPU {
97 receiver: IpcReceiver<WebGPURequest>,
98 sender: IpcSender<WebGPURequest>,
99 pub(crate) script_sender: IpcSender<WebGPUMsg>,
100 pub(crate) global: Arc<wgc::global::Global>,
101 devices: Arc<Mutex<FxHashMap<DeviceId, DeviceScope>>>,
102 error_command_encoders: FxHashMap<id::CommandEncoderId, String>,
107 pub(crate) compositor_api: CrossProcessCompositorApi,
108 pub(crate) external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
109 pub(crate) wgpu_image_map: WGPUImageMap,
110 pub(crate) poller: Poller,
112 compute_passes: FxHashMap<ComputePassId, Pass<ComputePass>>,
114 render_passes: FxHashMap<RenderPassId, Pass<RenderPass>>,
116}
117
118impl WGPU {
119 pub(crate) fn new(
120 receiver: IpcReceiver<WebGPURequest>,
121 sender: IpcSender<WebGPURequest>,
122 script_sender: IpcSender<WebGPUMsg>,
123 compositor_api: CrossProcessCompositorApi,
124 external_images: Arc<Mutex<WebrenderExternalImageRegistry>>,
125 wgpu_image_map: WGPUImageMap,
126 ) -> Self {
127 let backend_pref = pref!(dom_webgpu_wgpu_backend);
128 let backends = if backend_pref.is_empty() {
129 wgt::Backends::PRIMARY
130 } else {
131 info!(
132 "Selecting backends based on dom.webgpu.wgpu_backend pref: {:?}",
133 backend_pref
134 );
135 wgt::Backends::from_comma_list(&backend_pref)
136 };
137 let global = Arc::new(wgc::global::Global::new(
138 "wgpu-core",
139 &InstanceDescriptor {
140 backends,
141 ..Default::default()
142 },
143 ));
144 WGPU {
145 poller: Poller::new(Arc::clone(&global)),
146 receiver,
147 sender,
148 script_sender,
149 global,
150 devices: Arc::new(Mutex::new(FxHashMap::default())),
151 error_command_encoders: FxHashMap::default(),
152 compositor_api,
153 external_images,
154 wgpu_image_map,
155 compute_passes: FxHashMap::default(),
156 render_passes: FxHashMap::default(),
157 }
158 }
159
160 pub(crate) fn run(&mut self) {
161 loop {
162 if let Ok(msg) = self.receiver.recv() {
163 log::trace!("recv: {msg:?}");
164 match msg {
165 WebGPURequest::BufferMapAsync {
166 sender,
167 buffer_id,
168 device_id,
169 host_map,
170 offset,
171 size,
172 } => {
173 let glob = Arc::clone(&self.global);
174 let resp_sender = sender.clone();
175 let token = self.poller.token();
176 let callback = Box::from(move |result: BufferAccessResult| {
177 drop(token);
178 let response = result.and_then(|_| {
179 let global = &glob;
180 let (slice_pointer, range_size) =
181 global.buffer_get_mapped_range(buffer_id, offset, size)?;
182 let data = unsafe {
184 slice::from_raw_parts(
185 slice_pointer.as_ptr(),
186 range_size as usize,
187 )
188 };
189
190 Ok(Mapping {
191 data: IpcSharedMemory::from_bytes(data),
192 range: offset..offset + range_size,
193 mode: host_map,
194 })
195 });
196 if let Err(e) = resp_sender.send(response) {
197 warn!("Could not send BufferMapAsync Response ({})", e);
198 }
199 });
200
201 let operation = BufferMapOperation {
202 host: host_map,
203 callback: Some(callback),
204 };
205 let global = &self.global;
206 let result = global.buffer_map_async(buffer_id, offset, size, operation);
207 self.poller.wake();
208 self.maybe_dispatch_wgpu_error(device_id, result.err());
210 },
211 WebGPURequest::CommandEncoderFinish {
212 command_encoder_id,
213 device_id,
214 desc,
215 } => {
216 let global = &self.global;
217 let result = if let Some(err) =
218 self.error_command_encoders.get(&command_encoder_id)
219 {
220 Err(Error::Validation(err.clone()))
221 } else if let Some(error) =
222 global.command_encoder_finish(command_encoder_id, &desc).1
223 {
224 Err(Error::from_error(error))
225 } else {
226 Ok(())
227 };
228
229 self.encoder_record_error(command_encoder_id, &result);
231 self.maybe_dispatch_error(device_id, result.err());
233 },
234 WebGPURequest::CopyBufferToBuffer {
235 command_encoder_id,
236 source_id,
237 source_offset,
238 destination_id,
239 destination_offset,
240 size,
241 } => {
242 let global = &self.global;
243 let result = global.command_encoder_copy_buffer_to_buffer(
244 command_encoder_id,
245 source_id,
246 source_offset,
247 destination_id,
248 destination_offset,
249 Some(size),
250 );
251 self.encoder_record_error(command_encoder_id, &result);
252 },
253 WebGPURequest::CopyBufferToTexture {
254 command_encoder_id,
255 source,
256 destination,
257 copy_size,
258 } => {
259 let global = &self.global;
260 let result = global.command_encoder_copy_buffer_to_texture(
261 command_encoder_id,
262 &source,
263 &destination,
264 ©_size,
265 );
266 self.encoder_record_error(command_encoder_id, &result);
267 },
268 WebGPURequest::CopyTextureToBuffer {
269 command_encoder_id,
270 source,
271 destination,
272 copy_size,
273 } => {
274 let global = &self.global;
275 let result = global.command_encoder_copy_texture_to_buffer(
276 command_encoder_id,
277 &source,
278 &destination,
279 ©_size,
280 );
281 self.encoder_record_error(command_encoder_id, &result);
282 },
283 WebGPURequest::CopyTextureToTexture {
284 command_encoder_id,
285 source,
286 destination,
287 copy_size,
288 } => {
289 let global = &self.global;
290 let result = global.command_encoder_copy_texture_to_texture(
291 command_encoder_id,
292 &source,
293 &destination,
294 ©_size,
295 );
296 self.encoder_record_error(command_encoder_id, &result);
297 },
298 WebGPURequest::CreateBindGroup {
299 device_id,
300 bind_group_id,
301 descriptor,
302 } => {
303 let global = &self.global;
304 let (_, error) = global.device_create_bind_group(
305 device_id,
306 &descriptor,
307 Some(bind_group_id),
308 );
309 self.maybe_dispatch_wgpu_error(device_id, error);
310 },
311 WebGPURequest::CreateBindGroupLayout {
312 device_id,
313 bind_group_layout_id,
314 descriptor,
315 } => {
316 let global = &self.global;
317 if let Some(desc) = descriptor {
318 let (_, error) = global.device_create_bind_group_layout(
319 device_id,
320 &desc,
321 Some(bind_group_layout_id),
322 );
323
324 self.maybe_dispatch_wgpu_error(device_id, error);
325 }
326 },
327 WebGPURequest::CreateBuffer {
328 device_id,
329 buffer_id,
330 descriptor,
331 } => {
332 let global = &self.global;
333 let (_, error) =
334 global.device_create_buffer(device_id, &descriptor, Some(buffer_id));
335
336 self.maybe_dispatch_wgpu_error(device_id, error);
337 },
338 WebGPURequest::CreateCommandEncoder {
339 device_id,
340 command_encoder_id,
341 desc,
342 } => {
343 let global = &self.global;
344 let (_, error) = global.device_create_command_encoder(
345 device_id,
346 &desc,
347 Some(command_encoder_id),
348 );
349
350 self.maybe_dispatch_wgpu_error(device_id, error);
351 },
352 WebGPURequest::CreateComputePipeline {
353 device_id,
354 compute_pipeline_id,
355 descriptor,
356 implicit_ids,
357 async_sender: sender,
358 } => {
359 let global = &self.global;
360 let bgls = implicit_ids
361 .as_ref()
362 .map_or(Vec::with_capacity(0), |(_, bgls)| {
363 bgls.iter().map(|x| x.to_owned()).collect()
364 });
365 let implicit =
366 implicit_ids
367 .as_ref()
368 .map(|(layout, _)| ImplicitPipelineIds {
369 root_id: *layout,
370 group_ids: bgls.as_slice(),
371 });
372 let (_, error) = global.device_create_compute_pipeline(
373 device_id,
374 &descriptor,
375 Some(compute_pipeline_id),
376 implicit,
377 );
378 if let Some(sender) = sender {
379 let res = match error {
380 Some(CreateComputePipelineError::Device(DeviceError::Lost)) |
382 None => Ok(Pipeline {
383 id: compute_pipeline_id,
384 label: descriptor.label.unwrap_or_default().to_string(),
385 }),
386 Some(e) => Err(Error::from_error(e)),
387 };
388 if let Err(e) = sender.send(res) {
389 warn!("Failed sending WebGPUComputePipelineResponse {e:?}");
390 }
391 } else {
392 self.maybe_dispatch_wgpu_error(device_id, error);
393 }
394 },
395 WebGPURequest::CreatePipelineLayout {
396 device_id,
397 pipeline_layout_id,
398 descriptor,
399 } => {
400 let global = &self.global;
401 let (_, error) = global.device_create_pipeline_layout(
402 device_id,
403 &descriptor,
404 Some(pipeline_layout_id),
405 );
406 self.maybe_dispatch_wgpu_error(device_id, error);
407 },
408 WebGPURequest::CreateRenderPipeline {
409 device_id,
410 render_pipeline_id,
411 descriptor,
412 implicit_ids,
413 async_sender: sender,
414 } => {
415 let global = &self.global;
416 let bgls = implicit_ids
417 .as_ref()
418 .map_or(Vec::with_capacity(0), |(_, bgls)| {
419 bgls.iter().map(|x| x.to_owned()).collect()
420 });
421 let implicit =
422 implicit_ids
423 .as_ref()
424 .map(|(layout, _)| ImplicitPipelineIds {
425 root_id: *layout,
426 group_ids: bgls.as_slice(),
427 });
428 let (_, error) = global.device_create_render_pipeline(
429 device_id,
430 &descriptor,
431 Some(render_pipeline_id),
432 implicit,
433 );
434
435 if let Some(sender) = sender {
436 let res = match error {
437 Some(CreateRenderPipelineError::Device(DeviceError::Lost)) |
439 None => Ok(Pipeline {
440 id: render_pipeline_id,
441 label: descriptor.label.unwrap_or_default().to_string(),
442 }),
443 Some(e) => Err(Error::from_error(e)),
444 };
445 if let Err(e) = sender.send(res) {
446 warn!("Failed sending WebGPURenderPipelineResponse {e:?}");
447 }
448 } else {
449 self.maybe_dispatch_wgpu_error(device_id, error);
450 }
451 },
452 WebGPURequest::CreateSampler {
453 device_id,
454 sampler_id,
455 descriptor,
456 } => {
457 let global = &self.global;
458 let (_, error) =
459 global.device_create_sampler(device_id, &descriptor, Some(sampler_id));
460 self.maybe_dispatch_wgpu_error(device_id, error);
461 },
462 WebGPURequest::CreateShaderModule {
463 device_id,
464 program_id,
465 program,
466 label,
467 sender,
468 } => {
469 let global = &self.global;
470 let source =
471 wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&program));
472 let desc = ShaderModuleDescriptor {
473 label: label.map(|s| s.into()),
474 runtime_checks: wgt::ShaderRuntimeChecks::checked(),
475 };
476 let (_, error) = global.device_create_shader_module(
477 device_id,
478 &desc,
479 source,
480 Some(program_id),
481 );
482 if let Err(e) = sender.send(
483 error
484 .as_ref()
485 .map(|e| ShaderCompilationInfo::from(e, &program)),
486 ) {
487 warn!("Failed to send CompilationInfo {e:?}");
488 }
489 self.maybe_dispatch_wgpu_error(device_id, error);
490 },
491 WebGPURequest::CreateContext {
492 buffer_ids,
493 size,
494 sender,
495 } => {
496 let id = self
497 .external_images
498 .lock()
499 .expect("Lock poisoned?")
500 .next_id(WebrenderImageHandlerType::WebGPU);
501 let image_key = self.compositor_api.generate_image_key_blocking().unwrap();
502 let context_id = WebGPUContextId(id.0);
503 if let Err(e) = sender.send((context_id, image_key)) {
504 warn!("Failed to send ExternalImageId to new context ({})", e);
505 };
506 self.create_context(context_id, image_key, size, buffer_ids);
507 },
508 WebGPURequest::Present {
509 context_id,
510 pending_texture,
511 size,
512 canvas_epoch,
513 } => {
514 self.present(context_id, pending_texture, size, canvas_epoch);
515 },
516 WebGPURequest::GetImage {
517 context_id,
518 pending_texture,
519 sender,
520 } => self.get_image(context_id, pending_texture, sender),
521 WebGPURequest::ValidateTextureDescriptor {
522 device_id,
523 texture_id,
524 descriptor,
525 } => {
526 let global = &self.global;
529 let (_, error) =
530 global.device_create_texture(device_id, &descriptor, Some(texture_id));
531 global.texture_drop(texture_id);
532 self.poller.wake();
533 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(texture_id))
534 {
535 warn!("Unable to send FreeTexture({:?}) ({:?})", texture_id, e);
536 };
537 self.maybe_dispatch_wgpu_error(device_id, error);
538 },
539 WebGPURequest::DestroyContext { context_id } => {
540 self.destroy_context(context_id);
541 self.external_images
542 .lock()
543 .expect("Lock poisoned?")
544 .remove(&ExternalImageId(context_id.0));
545 },
546 WebGPURequest::CreateTexture {
547 device_id,
548 texture_id,
549 descriptor,
550 } => {
551 let global = &self.global;
552 let (_, error) =
553 global.device_create_texture(device_id, &descriptor, Some(texture_id));
554 self.maybe_dispatch_wgpu_error(device_id, error);
555 },
556 WebGPURequest::CreateTextureView {
557 texture_id,
558 texture_view_id,
559 device_id,
560 descriptor,
561 } => {
562 let global = &self.global;
563 if let Some(desc) = descriptor {
564 let (_, error) = global.texture_create_view(
565 texture_id,
566 &desc,
567 Some(texture_view_id),
568 );
569
570 self.maybe_dispatch_wgpu_error(device_id, error);
571 }
572 },
573 WebGPURequest::DestroyBuffer(buffer) => {
574 let global = &self.global;
575 global.buffer_destroy(buffer);
576 },
577 WebGPURequest::DestroyDevice(device) => {
578 let global = &self.global;
579 global.device_destroy(device);
580 self.poller.wake();
582 },
583 WebGPURequest::DestroyTexture(texture_id) => {
584 let global = &self.global;
585 global.texture_destroy(texture_id);
586 },
587 WebGPURequest::Exit(sender) => {
588 if let Err(e) = sender.send(()) {
589 warn!("Failed to send response to WebGPURequest::Exit ({})", e)
590 }
591 break;
592 },
593 WebGPURequest::DropCommandBuffer(id) => {
594 self.error_command_encoders
595 .remove(&id.into_command_encoder_id());
596 let global = &self.global;
597 global.command_buffer_drop(id);
598 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandBuffer(id)) {
599 warn!("Unable to send FreeCommandBuffer({:?}) ({:?})", id, e);
600 };
601 },
602 WebGPURequest::DropDevice(device_id) => {
603 let global = &self.global;
604 global.device_drop(device_id);
605 let device_scope = self
606 .devices
607 .lock()
608 .unwrap()
609 .remove(&device_id)
610 .expect("Device should not be dropped by this point");
611 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeDevice {
612 device_id,
613 pipeline_id: device_scope.pipeline_id,
614 }) {
615 warn!("Unable to send FreeDevice({:?}) ({:?})", device_id, e);
616 };
617 },
618 WebGPURequest::RenderBundleEncoderFinish {
619 render_bundle_encoder,
620 descriptor,
621 render_bundle_id,
622 device_id,
623 } => {
624 let global = &self.global;
625 let (_, error) = global.render_bundle_encoder_finish(
626 render_bundle_encoder,
627 &descriptor,
628 Some(render_bundle_id),
629 );
630
631 self.maybe_dispatch_wgpu_error(device_id, error);
632 },
633 WebGPURequest::RequestAdapter {
634 sender,
635 options,
636 adapter_id,
637 } => {
638 let global = &self.global;
639 let response = self
640 .global
641 .request_adapter(&options, wgt::Backends::all(), Some(adapter_id))
642 .map(|adapter_id| {
643 let adapter_info = global.adapter_get_info(adapter_id);
645 let limits = global.adapter_limits(adapter_id);
646 let features = global.adapter_features(adapter_id);
647 Adapter {
648 adapter_info,
649 adapter_id: WebGPUAdapter(adapter_id),
650 features,
651 limits,
652 channel: WebGPU(self.sender.clone()),
653 }
654 })
655 .map_err(|err| err.to_string());
656
657 if let Err(e) = sender.send(Some(response)) {
658 warn!(
659 "Failed to send response to WebGPURequest::RequestAdapter ({})",
660 e
661 )
662 }
663 },
664 WebGPURequest::RequestDevice {
665 sender,
666 adapter_id,
667 descriptor,
668 device_id,
669 queue_id,
670 pipeline_id,
671 } => {
672 let desc = DeviceDescriptor {
673 label: descriptor.label.as_ref().map(crate::Cow::from),
674 required_features: descriptor.required_features,
675 required_limits: descriptor.required_limits.clone(),
676 memory_hints: MemoryHints::MemoryUsage,
677 trace: wgpu_types::Trace::Off,
678 };
679 let global = &self.global;
680 let device = WebGPUDevice(device_id);
681 let queue = WebGPUQueue(queue_id);
682 let result = global
683 .adapter_request_device(
684 adapter_id.0,
685 &desc,
686 Some(device_id),
687 Some(queue_id),
688 )
689 .map(|_| {
690 {
691 self.devices.lock().unwrap().insert(
692 device_id,
693 DeviceScope::new(device_id, pipeline_id),
694 );
695 }
696 let script_sender = self.script_sender.clone();
697 let devices = Arc::clone(&self.devices);
698 let callback = Box::from(move |reason, msg| {
699 let reason = match reason {
700 wgt::DeviceLostReason::Unknown => DeviceLostReason::Unknown,
701 wgt::DeviceLostReason::Destroyed => {
702 DeviceLostReason::Destroyed
703 },
704 };
705 let _ = devices
707 .lock()
708 .unwrap()
709 .get_mut(&device_id)
710 .expect("Device should not be dropped by this point")
711 .error_scope_stack
712 .take();
713 if let Err(e) = script_sender.send(WebGPUMsg::DeviceLost {
714 device,
715 pipeline_id,
716 reason,
717 msg,
718 }) {
719 warn!("Failed to send WebGPUMsg::DeviceLost: {e}");
720 }
721 });
722 global.device_set_device_lost_closure(device_id, callback);
723 descriptor
724 })
725 .map_err(Into::into);
726 if let Err(e) = sender.send((device, queue, result)) {
727 warn!(
728 "Failed to send response to WebGPURequest::RequestDevice ({})",
729 e
730 )
731 }
732 },
733 WebGPURequest::BeginComputePass {
734 command_encoder_id,
735 compute_pass_id,
736 label,
737 device_id: _device_id,
738 } => {
739 let global = &self.global;
740 let (pass, error) = global.command_encoder_begin_compute_pass(
741 command_encoder_id,
742 &ComputePassDescriptor {
743 label,
744 timestamp_writes: None,
745 },
746 );
747 assert!(
748 self.compute_passes
749 .insert(compute_pass_id, Pass::new(pass, error.is_none()))
750 .is_none(),
751 "ComputePass should not exist yet."
752 );
753 },
756 WebGPURequest::ComputePassSetPipeline {
757 compute_pass_id,
758 pipeline_id,
759 device_id,
760 } => {
761 let pass = self
762 .compute_passes
763 .get_mut(&compute_pass_id)
764 .expect("ComputePass should exists");
765 if let Pass::Open { pass, valid } = pass {
766 *valid &= self
767 .global
768 .compute_pass_set_pipeline(pass, pipeline_id)
769 .is_ok();
770 } else {
771 self.maybe_dispatch_error(
772 device_id,
773 Some(Error::Validation("pass already ended".to_string())),
774 );
775 };
776 },
777 WebGPURequest::ComputePassSetBindGroup {
778 compute_pass_id,
779 index,
780 bind_group_id,
781 offsets,
782 device_id,
783 } => {
784 let pass = self
785 .compute_passes
786 .get_mut(&compute_pass_id)
787 .expect("ComputePass should exists");
788 if let Pass::Open { pass, valid } = pass {
789 *valid &= self
790 .global
791 .compute_pass_set_bind_group(
792 pass,
793 index,
794 Some(bind_group_id),
795 &offsets,
796 )
797 .is_ok();
798 } else {
799 self.maybe_dispatch_error(
800 device_id,
801 Some(Error::Validation("pass already ended".to_string())),
802 );
803 };
804 },
805 WebGPURequest::ComputePassDispatchWorkgroups {
806 compute_pass_id,
807 x,
808 y,
809 z,
810 device_id,
811 } => {
812 let pass = self
813 .compute_passes
814 .get_mut(&compute_pass_id)
815 .expect("ComputePass should exists");
816 if let Pass::Open { pass, valid } = pass {
817 *valid &= self
818 .global
819 .compute_pass_dispatch_workgroups(pass, x, y, z)
820 .is_ok();
821 } else {
822 self.maybe_dispatch_error(
823 device_id,
824 Some(Error::Validation("pass already ended".to_string())),
825 );
826 };
827 },
828 WebGPURequest::ComputePassDispatchWorkgroupsIndirect {
829 compute_pass_id,
830 buffer_id,
831 offset,
832 device_id,
833 } => {
834 let pass = self
835 .compute_passes
836 .get_mut(&compute_pass_id)
837 .expect("ComputePass should exists");
838 if let Pass::Open { pass, valid } = pass {
839 *valid &= self
840 .global
841 .compute_pass_dispatch_workgroups_indirect(pass, buffer_id, offset)
842 .is_ok();
843 } else {
844 self.maybe_dispatch_error(
845 device_id,
846 Some(Error::Validation("pass already ended".to_string())),
847 );
848 };
849 },
850 WebGPURequest::EndComputePass {
851 compute_pass_id,
852 device_id,
853 command_encoder_id,
854 } => {
855 let pass = self
857 .compute_passes
858 .get_mut(&compute_pass_id)
859 .expect("ComputePass should exists");
860 if let Pass::Open { mut pass, valid } = pass.take() {
862 if self.global.compute_pass_end(&mut pass).is_ok() && !valid {
865 self.encoder_record_error(
866 command_encoder_id,
867 &Err::<(), _>("Pass is invalid".to_string()),
868 );
869 }
870 } else {
871 self.dispatch_error(
872 device_id,
873 Error::Validation("pass already ended".to_string()),
874 );
875 };
876 },
877 WebGPURequest::BeginRenderPass {
878 command_encoder_id,
879 render_pass_id,
880 label,
881 color_attachments,
882 depth_stencil_attachment,
883 device_id: _device_id,
884 } => {
885 let global = &self.global;
886 let desc = &RenderPassDescriptor {
887 label,
888 color_attachments: color_attachments.into(),
889 depth_stencil_attachment: depth_stencil_attachment.as_ref(),
890 timestamp_writes: None,
891 occlusion_query_set: None,
892 };
893 let (pass, error) =
894 global.command_encoder_begin_render_pass(command_encoder_id, desc);
895 assert!(
896 self.render_passes
897 .insert(render_pass_id, Pass::new(pass, error.is_none()))
898 .is_none(),
899 "RenderPass should not exist yet."
900 );
901 },
904 WebGPURequest::RenderPassCommand {
905 render_pass_id,
906 render_command,
907 device_id,
908 } => {
909 let pass = self
910 .render_passes
911 .get_mut(&render_pass_id)
912 .expect("RenderPass should exists");
913 if let Pass::Open { pass, valid } = pass {
914 *valid &=
915 apply_render_command(&self.global, pass, render_command).is_ok();
916 } else {
917 self.maybe_dispatch_error(
918 device_id,
919 Some(Error::Validation("pass already ended".to_string())),
920 );
921 };
922 },
923 WebGPURequest::EndRenderPass {
924 render_pass_id,
925 device_id,
926 command_encoder_id,
927 } => {
928 let pass = self
930 .render_passes
931 .get_mut(&render_pass_id)
932 .expect("RenderPass should exists");
933 if let Pass::Open { mut pass, valid } = pass.take() {
935 if self.global.render_pass_end(&mut pass).is_ok() && !valid {
938 self.encoder_record_error(
939 command_encoder_id,
940 &Err::<(), _>("Pass is invalid".to_string()),
941 );
942 }
943 } else {
944 self.dispatch_error(
945 device_id,
946 Error::Validation("Pass already ended".to_string()),
947 );
948 };
949 },
950 WebGPURequest::Submit {
951 device_id,
952 queue_id,
953 command_buffers,
954 } => {
955 let global = &self.global;
956 let cmd_id = command_buffers.iter().find(|id| {
957 self.error_command_encoders
958 .contains_key(&id.into_command_encoder_id())
959 });
960 let result = if cmd_id.is_some() {
961 Err(Error::Validation(String::from(
962 "Invalid command buffer submitted",
963 )))
964 } else {
965 let _guard = self.poller.lock();
966 global
967 .queue_submit(queue_id, &command_buffers)
968 .map_err(|(_, error)| Error::from_error(error))
969 };
970 self.maybe_dispatch_error(device_id, result.err());
971 },
972 WebGPURequest::UnmapBuffer { buffer_id, mapping } => {
973 let global = &self.global;
974 if let Some(mapping) = mapping {
975 if let Ok((slice_pointer, range_size)) = global.buffer_get_mapped_range(
976 buffer_id,
977 mapping.range.start,
978 Some(mapping.range.end - mapping.range.start),
979 ) {
980 unsafe {
981 slice::from_raw_parts_mut(
982 slice_pointer.as_ptr(),
983 range_size as usize,
984 )
985 }
986 .copy_from_slice(&mapping.data);
987 }
988 }
989 let _result = global.buffer_unmap(buffer_id);
991 },
992 WebGPURequest::WriteBuffer {
993 device_id,
994 queue_id,
995 buffer_id,
996 buffer_offset,
997 data,
998 } => {
999 let global = &self.global;
1000 let result = global.queue_write_buffer(
1001 queue_id,
1002 buffer_id,
1003 buffer_offset as wgt::BufferAddress,
1004 &data,
1005 );
1006 self.maybe_dispatch_wgpu_error(device_id, result.err());
1007 },
1008 WebGPURequest::WriteTexture {
1009 device_id,
1010 queue_id,
1011 texture_cv,
1012 data_layout,
1013 size,
1014 data,
1015 } => {
1016 let global = &self.global;
1017 let _guard = self.poller.lock();
1018 let result = global.queue_write_texture(
1020 queue_id,
1021 &texture_cv,
1022 &data,
1023 &data_layout,
1024 &size,
1025 );
1026 drop(_guard);
1027 self.maybe_dispatch_wgpu_error(device_id, result.err());
1028 },
1029 WebGPURequest::QueueOnSubmittedWorkDone { sender, queue_id } => {
1030 let global = &self.global;
1031 let token = self.poller.token();
1032 let callback = Box::from(move || {
1033 drop(token);
1034 if let Err(e) = sender.send(()) {
1035 warn!("Could not send SubmittedWorkDone Response ({})", e);
1036 }
1037 });
1038 global.queue_on_submitted_work_done(queue_id, callback);
1039 self.poller.wake();
1040 },
1041 WebGPURequest::DropTexture(id) => {
1042 let global = &self.global;
1043 global.texture_drop(id);
1044 self.poller.wake();
1045 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) {
1046 warn!("Unable to send FreeTexture({:?}) ({:?})", id, e);
1047 };
1048 },
1049 WebGPURequest::DropAdapter(id) => {
1050 let global = &self.global;
1051 global.adapter_drop(id);
1052 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeAdapter(id)) {
1053 warn!("Unable to send FreeAdapter({:?}) ({:?})", id, e);
1054 };
1055 },
1056 WebGPURequest::DropBuffer(id) => {
1057 let global = &self.global;
1058 global.buffer_drop(id);
1059 self.poller.wake();
1060 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(id)) {
1061 warn!("Unable to send FreeBuffer({:?}) ({:?})", id, e);
1062 };
1063 },
1064 WebGPURequest::DropPipelineLayout(id) => {
1065 let global = &self.global;
1066 global.pipeline_layout_drop(id);
1067 if let Err(e) = self.script_sender.send(WebGPUMsg::FreePipelineLayout(id)) {
1068 warn!("Unable to send FreePipelineLayout({:?}) ({:?})", id, e);
1069 };
1070 },
1071 WebGPURequest::DropComputePipeline(id) => {
1072 let global = &self.global;
1073 global.compute_pipeline_drop(id);
1074 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePipeline(id))
1075 {
1076 warn!("Unable to send FreeComputePipeline({:?}) ({:?})", id, e);
1077 };
1078 },
1079 WebGPURequest::DropComputePass(id) => {
1080 self.compute_passes.remove(&id);
1082 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePass(id)) {
1083 warn!("Unable to send FreeComputePass({:?}) ({:?})", id, e);
1084 };
1085 },
1086 WebGPURequest::DropRenderPass(id) => {
1087 self.render_passes
1088 .remove(&id)
1089 .expect("RenderPass should exists");
1090 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPass(id)) {
1091 warn!("Unable to send FreeRenderPass({:?}) ({:?})", id, e);
1092 };
1093 },
1094 WebGPURequest::DropRenderPipeline(id) => {
1095 let global = &self.global;
1096 global.render_pipeline_drop(id);
1097 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPipeline(id)) {
1098 warn!("Unable to send FreeRenderPipeline({:?}) ({:?})", id, e);
1099 };
1100 },
1101 WebGPURequest::DropBindGroup(id) => {
1102 let global = &self.global;
1103 global.bind_group_drop(id);
1104 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroup(id)) {
1105 warn!("Unable to send FreeBindGroup({:?}) ({:?})", id, e);
1106 };
1107 },
1108 WebGPURequest::DropBindGroupLayout(id) => {
1109 let global = &self.global;
1110 global.bind_group_layout_drop(id);
1111 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroupLayout(id))
1112 {
1113 warn!("Unable to send FreeBindGroupLayout({:?}) ({:?})", id, e);
1114 };
1115 },
1116 WebGPURequest::DropTextureView(id) => {
1117 let global = &self.global;
1118 let _result = global.texture_view_drop(id);
1119 self.poller.wake();
1120 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(id)) {
1121 warn!("Unable to send FreeTextureView({:?}) ({:?})", id, e);
1122 };
1123 },
1124 WebGPURequest::DropSampler(id) => {
1125 let global = &self.global;
1126 global.sampler_drop(id);
1127 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeSampler(id)) {
1128 warn!("Unable to send FreeSampler({:?}) ({:?})", id, e);
1129 };
1130 },
1131 WebGPURequest::DropShaderModule(id) => {
1132 let global = &self.global;
1133 global.shader_module_drop(id);
1134 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeShaderModule(id)) {
1135 warn!("Unable to send FreeShaderModule({:?}) ({:?})", id, e);
1136 };
1137 },
1138 WebGPURequest::DropRenderBundle(id) => {
1139 let global = &self.global;
1140 global.render_bundle_drop(id);
1141 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderBundle(id)) {
1142 warn!("Unable to send FreeRenderBundle({:?}) ({:?})", id, e);
1143 };
1144 },
1145 WebGPURequest::DropQuerySet(id) => {
1146 let global = &self.global;
1147 global.query_set_drop(id);
1148 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeQuerySet(id)) {
1149 warn!("Unable to send FreeQuerySet({:?}) ({:?})", id, e);
1150 };
1151 },
1152 WebGPURequest::PushErrorScope { device_id, filter } => {
1153 let mut devices = self.devices.lock().unwrap();
1155 let device_scope = devices
1156 .get_mut(&device_id)
1157 .expect("Device should not be dropped by this point");
1158 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1159 error_scope_stack.push(ErrorScope::new(filter));
1160 } },
1162 WebGPURequest::DispatchError { device_id, error } => {
1163 self.dispatch_error(device_id, error);
1164 },
1165 WebGPURequest::PopErrorScope { device_id, sender } => {
1166 let mut devices = self.devices.lock().unwrap();
1168 let device_scope = devices
1169 .get_mut(&device_id)
1170 .expect("Device should not be dropped by this point");
1171 let result =
1172 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1173 if let Some(error_scope) = error_scope_stack.pop() {
1174 Ok(
1175 error_scope.errors.first().cloned(),
1177 )
1178 } else {
1179 Err(PopError::Empty)
1180 }
1181 } else {
1182 Err(PopError::Lost)
1184 };
1185 if let Err(error) = sender.send(result) {
1186 warn!("Error while sending PopErrorScope result: {error}");
1187 }
1188 },
1189 WebGPURequest::ComputeGetBindGroupLayout {
1190 device_id,
1191 pipeline_id,
1192 index,
1193 id,
1194 } => {
1195 let global = &self.global;
1196 let (_, error) = global.compute_pipeline_get_bind_group_layout(
1197 pipeline_id,
1198 index,
1199 Some(id),
1200 );
1201 self.maybe_dispatch_wgpu_error(device_id, error);
1202 },
1203 WebGPURequest::RenderGetBindGroupLayout {
1204 device_id,
1205 pipeline_id,
1206 index,
1207 id,
1208 } => {
1209 let global = &self.global;
1210 let (_, error) = global.render_pipeline_get_bind_group_layout(
1211 pipeline_id,
1212 index,
1213 Some(id),
1214 );
1215 self.maybe_dispatch_wgpu_error(device_id, error);
1216 },
1217 }
1218 }
1219 }
1220 if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) {
1221 warn!("Failed to send WebGPUMsg::Exit to script ({})", e);
1222 }
1223 }
1224
1225 fn maybe_dispatch_wgpu_error<E: std::error::Error + 'static>(
1226 &mut self,
1227 device_id: id::DeviceId,
1228 error: Option<E>,
1229 ) {
1230 self.maybe_dispatch_error(device_id, error.map(|e| Error::from_error(e)))
1231 }
1232
1233 fn maybe_dispatch_error(&mut self, device_id: id::DeviceId, error: Option<Error>) {
1235 if let Some(error) = error {
1236 self.dispatch_error(device_id, error);
1237 }
1238 }
1239
1240 fn dispatch_error(&mut self, device_id: id::DeviceId, error: Error) {
1242 let mut devices = self.devices.lock().unwrap();
1243 let device_scope = devices
1244 .get_mut(&device_id)
1245 .expect("Device should not be dropped by this point");
1246 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1247 if let Some(error_scope) = error_scope_stack
1248 .iter_mut()
1249 .rev()
1250 .find(|error_scope| error_scope.filter == error.filter())
1251 {
1252 error_scope.errors.push(error);
1253 } else if self
1254 .script_sender
1255 .send(WebGPUMsg::UncapturedError {
1256 device: WebGPUDevice(device_id),
1257 pipeline_id: device_scope.pipeline_id,
1258 error: error.clone(),
1259 })
1260 .is_err()
1261 {
1262 warn!("Failed to send WebGPUMsg::UncapturedError: {error:?}");
1263 }
1264 } }
1266
1267 fn encoder_record_error<U, T: std::fmt::Debug>(
1268 &mut self,
1269 encoder_id: id::CommandEncoderId,
1270 result: &Result<U, T>,
1271 ) {
1272 if let Err(e) = result {
1273 self.error_command_encoders
1274 .entry(encoder_id)
1275 .or_insert_with(|| format!("{:?}", e));
1276 }
1277 }
1278}