1use std::borrow::Cow;
8use std::slice;
9use std::sync::{Arc, Mutex};
10
11use base::id::PipelineId;
12use compositing_traits::{
13 CrossProcessCompositorApi, WebRenderExternalImageIdManager, 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::WebGpuExternalImageMap;
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) webrender_external_image_id_manager: WebRenderExternalImageIdManager,
109 pub(crate) wgpu_image_map: WebGpuExternalImageMap,
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 webrender_external_image_id_manager: WebRenderExternalImageIdManager,
125 wgpu_image_map: WebGpuExternalImageMap,
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 webrender_external_image_id_manager,
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 .webrender_external_image_id_manager
502 .next_id(WebRenderImageHandlerType::WebGpu);
503 let context_id = WebGPUContextId(id.0);
504
505 if let Err(error) = sender.send(context_id) {
506 warn!("Failed to send ContextId to new context ({error})");
507 };
508
509 self.create_context(context_id, size, buffer_ids);
510 },
511 WebGPURequest::Present {
512 context_id,
513 pending_texture,
514 size,
515 canvas_epoch,
516 } => {
517 self.present(context_id, pending_texture, size, canvas_epoch);
518 },
519 WebGPURequest::GetImage {
520 context_id,
521 pending_texture,
522 sender,
523 } => self.get_image(context_id, pending_texture, sender),
524 WebGPURequest::ValidateTextureDescriptor {
525 device_id,
526 texture_id,
527 descriptor,
528 } => {
529 let global = &self.global;
532 let (_, error) =
533 global.device_create_texture(device_id, &descriptor, Some(texture_id));
534 global.texture_drop(texture_id);
535 self.poller.wake();
536 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(texture_id))
537 {
538 warn!("Unable to send FreeTexture({:?}) ({:?})", texture_id, e);
539 };
540 self.maybe_dispatch_wgpu_error(device_id, error);
541 },
542 WebGPURequest::DestroyContext { context_id } => {
543 self.destroy_context(context_id);
544 self.webrender_external_image_id_manager
545 .remove(&ExternalImageId(context_id.0));
546 },
547 WebGPURequest::CreateTexture {
548 device_id,
549 texture_id,
550 descriptor,
551 } => {
552 let global = &self.global;
553 let (_, error) =
554 global.device_create_texture(device_id, &descriptor, Some(texture_id));
555 self.maybe_dispatch_wgpu_error(device_id, error);
556 },
557 WebGPURequest::CreateTextureView {
558 texture_id,
559 texture_view_id,
560 device_id,
561 descriptor,
562 } => {
563 let global = &self.global;
564 if let Some(desc) = descriptor {
565 let (_, error) = global.texture_create_view(
566 texture_id,
567 &desc,
568 Some(texture_view_id),
569 );
570
571 self.maybe_dispatch_wgpu_error(device_id, error);
572 }
573 },
574 WebGPURequest::DestroyBuffer(buffer) => {
575 let global = &self.global;
576 global.buffer_destroy(buffer);
577 },
578 WebGPURequest::DestroyDevice(device) => {
579 let global = &self.global;
580 global.device_destroy(device);
581 self.poller.wake();
583 },
584 WebGPURequest::DestroyTexture(texture_id) => {
585 let global = &self.global;
586 global.texture_destroy(texture_id);
587 },
588 WebGPURequest::Exit(sender) => {
589 if let Err(e) = sender.send(()) {
590 warn!("Failed to send response to WebGPURequest::Exit ({})", e)
591 }
592 break;
593 },
594 WebGPURequest::DropCommandBuffer(id) => {
595 self.error_command_encoders
596 .remove(&id.into_command_encoder_id());
597 let global = &self.global;
598 global.command_buffer_drop(id);
599 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeCommandBuffer(id)) {
600 warn!("Unable to send FreeCommandBuffer({:?}) ({:?})", id, e);
601 };
602 },
603 WebGPURequest::DropDevice(device_id) => {
604 let global = &self.global;
605 global.device_drop(device_id);
606 let device_scope = self
607 .devices
608 .lock()
609 .unwrap()
610 .remove(&device_id)
611 .expect("Device should not be dropped by this point");
612 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeDevice {
613 device_id,
614 pipeline_id: device_scope.pipeline_id,
615 }) {
616 warn!("Unable to send FreeDevice({:?}) ({:?})", device_id, e);
617 };
618 },
619 WebGPURequest::RenderBundleEncoderFinish {
620 render_bundle_encoder,
621 descriptor,
622 render_bundle_id,
623 device_id,
624 } => {
625 let global = &self.global;
626 let (_, error) = global.render_bundle_encoder_finish(
627 render_bundle_encoder,
628 &descriptor,
629 Some(render_bundle_id),
630 );
631
632 self.maybe_dispatch_wgpu_error(device_id, error);
633 },
634 WebGPURequest::RequestAdapter {
635 sender,
636 options,
637 adapter_id,
638 } => {
639 let global = &self.global;
640 let response = self
641 .global
642 .request_adapter(&options, wgt::Backends::all(), Some(adapter_id))
643 .map(|adapter_id| {
644 let adapter_info = global.adapter_get_info(adapter_id);
646 let limits = global.adapter_limits(adapter_id);
647 let features = global.adapter_features(adapter_id);
648 Adapter {
649 adapter_info,
650 adapter_id: WebGPUAdapter(adapter_id),
651 features,
652 limits,
653 channel: WebGPU(self.sender.clone()),
654 }
655 })
656 .map_err(|err| err.to_string());
657
658 if let Err(e) = sender.send(Some(response)) {
659 warn!(
660 "Failed to send response to WebGPURequest::RequestAdapter ({})",
661 e
662 )
663 }
664 },
665 WebGPURequest::RequestDevice {
666 sender,
667 adapter_id,
668 descriptor,
669 device_id,
670 queue_id,
671 pipeline_id,
672 } => {
673 let desc = DeviceDescriptor {
674 label: descriptor.label.as_ref().map(crate::Cow::from),
675 required_features: descriptor.required_features,
676 required_limits: descriptor.required_limits.clone(),
677 memory_hints: MemoryHints::MemoryUsage,
678 trace: wgpu_types::Trace::Off,
679 };
680 let global = &self.global;
681 let device = WebGPUDevice(device_id);
682 let queue = WebGPUQueue(queue_id);
683 let result = global
684 .adapter_request_device(
685 adapter_id.0,
686 &desc,
687 Some(device_id),
688 Some(queue_id),
689 )
690 .map(|_| {
691 {
692 self.devices.lock().unwrap().insert(
693 device_id,
694 DeviceScope::new(device_id, pipeline_id),
695 );
696 }
697 let script_sender = self.script_sender.clone();
698 let devices = Arc::clone(&self.devices);
699 let callback = Box::from(move |reason, msg| {
700 let reason = match reason {
701 wgt::DeviceLostReason::Unknown => DeviceLostReason::Unknown,
702 wgt::DeviceLostReason::Destroyed => {
703 DeviceLostReason::Destroyed
704 },
705 };
706 let _ = devices
708 .lock()
709 .unwrap()
710 .get_mut(&device_id)
711 .expect("Device should not be dropped by this point")
712 .error_scope_stack
713 .take();
714 if let Err(e) = script_sender.send(WebGPUMsg::DeviceLost {
715 device,
716 pipeline_id,
717 reason,
718 msg,
719 }) {
720 warn!("Failed to send WebGPUMsg::DeviceLost: {e}");
721 }
722 });
723 global.device_set_device_lost_closure(device_id, callback);
724 descriptor
725 })
726 .map_err(Into::into);
727 if let Err(e) = sender.send((device, queue, result)) {
728 warn!(
729 "Failed to send response to WebGPURequest::RequestDevice ({})",
730 e
731 )
732 }
733 },
734 WebGPURequest::BeginComputePass {
735 command_encoder_id,
736 compute_pass_id,
737 label,
738 device_id: _device_id,
739 } => {
740 let global = &self.global;
741 let (pass, error) = global.command_encoder_begin_compute_pass(
742 command_encoder_id,
743 &ComputePassDescriptor {
744 label,
745 timestamp_writes: None,
746 },
747 );
748 assert!(
749 self.compute_passes
750 .insert(compute_pass_id, Pass::new(pass, error.is_none()))
751 .is_none(),
752 "ComputePass should not exist yet."
753 );
754 },
757 WebGPURequest::ComputePassSetPipeline {
758 compute_pass_id,
759 pipeline_id,
760 device_id,
761 } => {
762 let pass = self
763 .compute_passes
764 .get_mut(&compute_pass_id)
765 .expect("ComputePass should exists");
766 if let Pass::Open { pass, valid } = pass {
767 *valid &= self
768 .global
769 .compute_pass_set_pipeline(pass, pipeline_id)
770 .is_ok();
771 } else {
772 self.maybe_dispatch_error(
773 device_id,
774 Some(Error::Validation("pass already ended".to_string())),
775 );
776 };
777 },
778 WebGPURequest::ComputePassSetBindGroup {
779 compute_pass_id,
780 index,
781 bind_group_id,
782 offsets,
783 device_id,
784 } => {
785 let pass = self
786 .compute_passes
787 .get_mut(&compute_pass_id)
788 .expect("ComputePass should exists");
789 if let Pass::Open { pass, valid } = pass {
790 *valid &= self
791 .global
792 .compute_pass_set_bind_group(
793 pass,
794 index,
795 Some(bind_group_id),
796 &offsets,
797 )
798 .is_ok();
799 } else {
800 self.maybe_dispatch_error(
801 device_id,
802 Some(Error::Validation("pass already ended".to_string())),
803 );
804 };
805 },
806 WebGPURequest::ComputePassDispatchWorkgroups {
807 compute_pass_id,
808 x,
809 y,
810 z,
811 device_id,
812 } => {
813 let pass = self
814 .compute_passes
815 .get_mut(&compute_pass_id)
816 .expect("ComputePass should exists");
817 if let Pass::Open { pass, valid } = pass {
818 *valid &= self
819 .global
820 .compute_pass_dispatch_workgroups(pass, x, y, z)
821 .is_ok();
822 } else {
823 self.maybe_dispatch_error(
824 device_id,
825 Some(Error::Validation("pass already ended".to_string())),
826 );
827 };
828 },
829 WebGPURequest::ComputePassDispatchWorkgroupsIndirect {
830 compute_pass_id,
831 buffer_id,
832 offset,
833 device_id,
834 } => {
835 let pass = self
836 .compute_passes
837 .get_mut(&compute_pass_id)
838 .expect("ComputePass should exists");
839 if let Pass::Open { pass, valid } = pass {
840 *valid &= self
841 .global
842 .compute_pass_dispatch_workgroups_indirect(pass, buffer_id, offset)
843 .is_ok();
844 } else {
845 self.maybe_dispatch_error(
846 device_id,
847 Some(Error::Validation("pass already ended".to_string())),
848 );
849 };
850 },
851 WebGPURequest::EndComputePass {
852 compute_pass_id,
853 device_id,
854 command_encoder_id,
855 } => {
856 let pass = self
858 .compute_passes
859 .get_mut(&compute_pass_id)
860 .expect("ComputePass should exists");
861 if let Pass::Open { mut pass, valid } = pass.take() {
863 if self.global.compute_pass_end(&mut pass).is_ok() && !valid {
866 self.encoder_record_error(
867 command_encoder_id,
868 &Err::<(), _>("Pass is invalid".to_string()),
869 );
870 }
871 } else {
872 self.dispatch_error(
873 device_id,
874 Error::Validation("pass already ended".to_string()),
875 );
876 };
877 },
878 WebGPURequest::BeginRenderPass {
879 command_encoder_id,
880 render_pass_id,
881 label,
882 color_attachments,
883 depth_stencil_attachment,
884 device_id: _device_id,
885 } => {
886 let global = &self.global;
887 let desc = &RenderPassDescriptor {
888 label,
889 color_attachments: color_attachments.into(),
890 depth_stencil_attachment: depth_stencil_attachment.as_ref(),
891 timestamp_writes: None,
892 occlusion_query_set: None,
893 };
894 let (pass, error) =
895 global.command_encoder_begin_render_pass(command_encoder_id, desc);
896 assert!(
897 self.render_passes
898 .insert(render_pass_id, Pass::new(pass, error.is_none()))
899 .is_none(),
900 "RenderPass should not exist yet."
901 );
902 },
905 WebGPURequest::RenderPassCommand {
906 render_pass_id,
907 render_command,
908 device_id,
909 } => {
910 let pass = self
911 .render_passes
912 .get_mut(&render_pass_id)
913 .expect("RenderPass should exists");
914 if let Pass::Open { pass, valid } = pass {
915 *valid &=
916 apply_render_command(&self.global, pass, render_command).is_ok();
917 } else {
918 self.maybe_dispatch_error(
919 device_id,
920 Some(Error::Validation("pass already ended".to_string())),
921 );
922 };
923 },
924 WebGPURequest::EndRenderPass {
925 render_pass_id,
926 device_id,
927 command_encoder_id,
928 } => {
929 let pass = self
931 .render_passes
932 .get_mut(&render_pass_id)
933 .expect("RenderPass should exists");
934 if let Pass::Open { mut pass, valid } = pass.take() {
936 if self.global.render_pass_end(&mut pass).is_ok() && !valid {
939 self.encoder_record_error(
940 command_encoder_id,
941 &Err::<(), _>("Pass is invalid".to_string()),
942 );
943 }
944 } else {
945 self.dispatch_error(
946 device_id,
947 Error::Validation("Pass already ended".to_string()),
948 );
949 };
950 },
951 WebGPURequest::Submit {
952 device_id,
953 queue_id,
954 command_buffers,
955 } => {
956 let global = &self.global;
957 let cmd_id = command_buffers.iter().find(|id| {
958 self.error_command_encoders
959 .contains_key(&id.into_command_encoder_id())
960 });
961 let result = if cmd_id.is_some() {
962 Err(Error::Validation(String::from(
963 "Invalid command buffer submitted",
964 )))
965 } else {
966 let _guard = self.poller.lock();
967 global
968 .queue_submit(queue_id, &command_buffers)
969 .map_err(|(_, error)| Error::from_error(error))
970 };
971 self.maybe_dispatch_error(device_id, result.err());
972 },
973 WebGPURequest::UnmapBuffer { buffer_id, mapping } => {
974 let global = &self.global;
975 if let Some(mapping) = mapping {
976 if let Ok((slice_pointer, range_size)) = global.buffer_get_mapped_range(
977 buffer_id,
978 mapping.range.start,
979 Some(mapping.range.end - mapping.range.start),
980 ) {
981 unsafe {
982 slice::from_raw_parts_mut(
983 slice_pointer.as_ptr(),
984 range_size as usize,
985 )
986 }
987 .copy_from_slice(&mapping.data);
988 }
989 }
990 let _result = global.buffer_unmap(buffer_id);
992 },
993 WebGPURequest::WriteBuffer {
994 device_id,
995 queue_id,
996 buffer_id,
997 buffer_offset,
998 data,
999 } => {
1000 let global = &self.global;
1001 let result = global.queue_write_buffer(
1002 queue_id,
1003 buffer_id,
1004 buffer_offset as wgt::BufferAddress,
1005 &data,
1006 );
1007 self.maybe_dispatch_wgpu_error(device_id, result.err());
1008 },
1009 WebGPURequest::WriteTexture {
1010 device_id,
1011 queue_id,
1012 texture_cv,
1013 data_layout,
1014 size,
1015 data,
1016 } => {
1017 let global = &self.global;
1018 let _guard = self.poller.lock();
1019 let result = global.queue_write_texture(
1021 queue_id,
1022 &texture_cv,
1023 &data,
1024 &data_layout,
1025 &size,
1026 );
1027 drop(_guard);
1028 self.maybe_dispatch_wgpu_error(device_id, result.err());
1029 },
1030 WebGPURequest::QueueOnSubmittedWorkDone { sender, queue_id } => {
1031 let global = &self.global;
1032 let token = self.poller.token();
1033 let callback = Box::from(move || {
1034 drop(token);
1035 if let Err(e) = sender.send(()) {
1036 warn!("Could not send SubmittedWorkDone Response ({})", e);
1037 }
1038 });
1039 global.queue_on_submitted_work_done(queue_id, callback);
1040 self.poller.wake();
1041 },
1042 WebGPURequest::DropTexture(id) => {
1043 let global = &self.global;
1044 global.texture_drop(id);
1045 self.poller.wake();
1046 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(id)) {
1047 warn!("Unable to send FreeTexture({:?}) ({:?})", id, e);
1048 };
1049 },
1050 WebGPURequest::DropAdapter(id) => {
1051 let global = &self.global;
1052 global.adapter_drop(id);
1053 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeAdapter(id)) {
1054 warn!("Unable to send FreeAdapter({:?}) ({:?})", id, e);
1055 };
1056 },
1057 WebGPURequest::DropBuffer(id) => {
1058 let global = &self.global;
1059 global.buffer_drop(id);
1060 self.poller.wake();
1061 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBuffer(id)) {
1062 warn!("Unable to send FreeBuffer({:?}) ({:?})", id, e);
1063 };
1064 },
1065 WebGPURequest::DropPipelineLayout(id) => {
1066 let global = &self.global;
1067 global.pipeline_layout_drop(id);
1068 if let Err(e) = self.script_sender.send(WebGPUMsg::FreePipelineLayout(id)) {
1069 warn!("Unable to send FreePipelineLayout({:?}) ({:?})", id, e);
1070 };
1071 },
1072 WebGPURequest::DropComputePipeline(id) => {
1073 let global = &self.global;
1074 global.compute_pipeline_drop(id);
1075 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePipeline(id))
1076 {
1077 warn!("Unable to send FreeComputePipeline({:?}) ({:?})", id, e);
1078 };
1079 },
1080 WebGPURequest::DropComputePass(id) => {
1081 self.compute_passes.remove(&id);
1083 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeComputePass(id)) {
1084 warn!("Unable to send FreeComputePass({:?}) ({:?})", id, e);
1085 };
1086 },
1087 WebGPURequest::DropRenderPass(id) => {
1088 self.render_passes
1089 .remove(&id)
1090 .expect("RenderPass should exists");
1091 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPass(id)) {
1092 warn!("Unable to send FreeRenderPass({:?}) ({:?})", id, e);
1093 };
1094 },
1095 WebGPURequest::DropRenderPipeline(id) => {
1096 let global = &self.global;
1097 global.render_pipeline_drop(id);
1098 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderPipeline(id)) {
1099 warn!("Unable to send FreeRenderPipeline({:?}) ({:?})", id, e);
1100 };
1101 },
1102 WebGPURequest::DropBindGroup(id) => {
1103 let global = &self.global;
1104 global.bind_group_drop(id);
1105 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroup(id)) {
1106 warn!("Unable to send FreeBindGroup({:?}) ({:?})", id, e);
1107 };
1108 },
1109 WebGPURequest::DropBindGroupLayout(id) => {
1110 let global = &self.global;
1111 global.bind_group_layout_drop(id);
1112 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeBindGroupLayout(id))
1113 {
1114 warn!("Unable to send FreeBindGroupLayout({:?}) ({:?})", id, e);
1115 };
1116 },
1117 WebGPURequest::DropTextureView(id) => {
1118 let global = &self.global;
1119 let _result = global.texture_view_drop(id);
1120 self.poller.wake();
1121 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTextureView(id)) {
1122 warn!("Unable to send FreeTextureView({:?}) ({:?})", id, e);
1123 };
1124 },
1125 WebGPURequest::DropSampler(id) => {
1126 let global = &self.global;
1127 global.sampler_drop(id);
1128 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeSampler(id)) {
1129 warn!("Unable to send FreeSampler({:?}) ({:?})", id, e);
1130 };
1131 },
1132 WebGPURequest::DropShaderModule(id) => {
1133 let global = &self.global;
1134 global.shader_module_drop(id);
1135 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeShaderModule(id)) {
1136 warn!("Unable to send FreeShaderModule({:?}) ({:?})", id, e);
1137 };
1138 },
1139 WebGPURequest::DropRenderBundle(id) => {
1140 let global = &self.global;
1141 global.render_bundle_drop(id);
1142 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeRenderBundle(id)) {
1143 warn!("Unable to send FreeRenderBundle({:?}) ({:?})", id, e);
1144 };
1145 },
1146 WebGPURequest::DropQuerySet(id) => {
1147 let global = &self.global;
1148 global.query_set_drop(id);
1149 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeQuerySet(id)) {
1150 warn!("Unable to send FreeQuerySet({:?}) ({:?})", id, e);
1151 };
1152 },
1153 WebGPURequest::PushErrorScope { device_id, filter } => {
1154 let mut devices = self.devices.lock().unwrap();
1156 let device_scope = devices
1157 .get_mut(&device_id)
1158 .expect("Device should not be dropped by this point");
1159 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1160 error_scope_stack.push(ErrorScope::new(filter));
1161 } },
1163 WebGPURequest::DispatchError { device_id, error } => {
1164 self.dispatch_error(device_id, error);
1165 },
1166 WebGPURequest::PopErrorScope { device_id, sender } => {
1167 let mut devices = self.devices.lock().unwrap();
1169 let device_scope = devices
1170 .get_mut(&device_id)
1171 .expect("Device should not be dropped by this point");
1172 let result =
1173 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1174 if let Some(error_scope) = error_scope_stack.pop() {
1175 Ok(
1176 error_scope.errors.first().cloned(),
1178 )
1179 } else {
1180 Err(PopError::Empty)
1181 }
1182 } else {
1183 Err(PopError::Lost)
1185 };
1186 if let Err(error) = sender.send(result) {
1187 warn!("Error while sending PopErrorScope result: {error}");
1188 }
1189 },
1190 WebGPURequest::ComputeGetBindGroupLayout {
1191 device_id,
1192 pipeline_id,
1193 index,
1194 id,
1195 } => {
1196 let global = &self.global;
1197 let (_, error) = global.compute_pipeline_get_bind_group_layout(
1198 pipeline_id,
1199 index,
1200 Some(id),
1201 );
1202 self.maybe_dispatch_wgpu_error(device_id, error);
1203 },
1204 WebGPURequest::RenderGetBindGroupLayout {
1205 device_id,
1206 pipeline_id,
1207 index,
1208 id,
1209 } => {
1210 let global = &self.global;
1211 let (_, error) = global.render_pipeline_get_bind_group_layout(
1212 pipeline_id,
1213 index,
1214 Some(id),
1215 );
1216 self.maybe_dispatch_wgpu_error(device_id, error);
1217 },
1218 }
1219 }
1220 }
1221 if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) {
1222 warn!("Failed to send WebGPUMsg::Exit to script ({})", e);
1223 }
1224 }
1225
1226 fn maybe_dispatch_wgpu_error<E: std::error::Error + 'static>(
1227 &mut self,
1228 device_id: id::DeviceId,
1229 error: Option<E>,
1230 ) {
1231 self.maybe_dispatch_error(device_id, error.map(|e| Error::from_error(e)))
1232 }
1233
1234 fn maybe_dispatch_error(&mut self, device_id: id::DeviceId, error: Option<Error>) {
1236 if let Some(error) = error {
1237 self.dispatch_error(device_id, error);
1238 }
1239 }
1240
1241 fn dispatch_error(&mut self, device_id: id::DeviceId, error: Error) {
1243 let mut devices = self.devices.lock().unwrap();
1244 let device_scope = devices
1245 .get_mut(&device_id)
1246 .expect("Device should not be dropped by this point");
1247 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1248 if let Some(error_scope) = error_scope_stack
1249 .iter_mut()
1250 .rev()
1251 .find(|error_scope| error_scope.filter == error.filter())
1252 {
1253 error_scope.errors.push(error);
1254 } else if self
1255 .script_sender
1256 .send(WebGPUMsg::UncapturedError {
1257 device: WebGPUDevice(device_id),
1258 pipeline_id: device_scope.pipeline_id,
1259 error: error.clone(),
1260 })
1261 .is_err()
1262 {
1263 warn!("Failed to send WebGPUMsg::UncapturedError: {error:?}");
1264 }
1265 } }
1267
1268 fn encoder_record_error<U, T: std::fmt::Debug>(
1269 &mut self,
1270 encoder_id: id::CommandEncoderId,
1271 result: &Result<U, T>,
1272 ) {
1273 if let Err(e) = result {
1274 self.error_command_encoders
1275 .entry(encoder_id)
1276 .or_insert_with(|| format!("{:?}", e));
1277 }
1278 }
1279}