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