1use std::borrow::Cow;
8use std::slice;
9use std::sync::{Arc, Mutex};
10
11use log::{info, warn};
12use paint_api::{CrossProcessPaintApi, WebRenderExternalImageIdManager, WebRenderImageHandlerType};
13use rustc_hash::FxHashMap;
14use servo_base::generic_channel::{GenericReceiver, GenericSender, GenericSharedMemory};
15use servo_base::id::PipelineId;
16use servo_config::pref;
17use webgpu_traits::{
18 Adapter, ComputePassId, DeviceLostReason, Error, ErrorScope, Mapping, Pipeline, PopError,
19 RenderPassId, ShaderCompilationInfo, WebGPU, WebGPUAdapter, WebGPUContextId, WebGPUDevice,
20 WebGPUMsg, WebGPUQueue, WebGPURequest, apply_render_command,
21};
22use webrender_api::ExternalImageId;
23use wgc::command::{ComputePass, ComputePassDescriptor, RenderPass};
24use wgc::device::{DeviceDescriptor, ImplicitPipelineIds};
25use wgc::id;
26use wgc::id::DeviceId;
27use wgc::pipeline::ShaderModuleDescriptor;
28use wgc::resource::BufferMapOperation;
29pub use wgpu_core as wgc;
30use wgpu_core::command::RenderPassDescriptor;
31use wgpu_core::device::DeviceError;
32use wgpu_core::pipeline::{CreateComputePipelineError, CreateRenderPipelineError};
33use wgpu_core::resource::BufferAccessResult;
34pub use wgpu_types as wgt;
35use wgpu_types::MemoryHints;
36use wgt::InstanceDescriptor;
37
38use crate::canvas_context::WebGpuExternalImageMap;
39use crate::poll_thread::Poller;
40
41#[derive(Eq, Hash, PartialEq)]
42pub(crate) struct DeviceScope {
43 pub device_id: DeviceId,
44 pub pipeline_id: PipelineId,
45 pub error_scope_stack: Option<Vec<ErrorScope>>,
49 }
55
56impl DeviceScope {
57 pub fn new(device_id: DeviceId, pipeline_id: PipelineId) -> Self {
58 Self {
59 device_id,
60 pipeline_id,
61 error_scope_stack: Some(Vec::new()),
62 }
63 }
64}
65
66#[derive(Debug, Default, Eq, PartialEq)]
68enum Pass<P> {
69 Open {
71 pass: P,
73 valid: bool,
76 },
77 #[default]
79 Ended,
80}
81
82impl<P> Pass<P> {
83 fn new(pass: P, valid: bool) -> Self {
85 Self::Open { pass, valid }
86 }
87
88 fn take(&mut self) -> Self {
90 std::mem::take(self)
91 }
92}
93
94#[expect(clippy::upper_case_acronyms)] pub(crate) struct WGPU {
96 receiver: GenericReceiver<WebGPURequest>,
97 sender: GenericSender<WebGPURequest>,
98 pub(crate) script_sender: GenericSender<WebGPUMsg>,
99 pub(crate) global: Arc<wgc::global::Global>,
100 devices: Arc<Mutex<FxHashMap<DeviceId, DeviceScope>>>,
101 error_command_encoders: FxHashMap<id::CommandEncoderId, String>,
106 pub(crate) paint_api: CrossProcessPaintApi,
107 pub(crate) webrender_external_image_id_manager: WebRenderExternalImageIdManager,
108 pub(crate) wgpu_image_map: WebGpuExternalImageMap,
109 pub(crate) poller: Poller,
111 compute_passes: FxHashMap<ComputePassId, Pass<ComputePass>>,
113 render_passes: FxHashMap<RenderPassId, Pass<RenderPass>>,
115}
116
117impl WGPU {
118 pub(crate) fn new(
119 receiver: GenericReceiver<WebGPURequest>,
120 sender: GenericSender<WebGPURequest>,
121 script_sender: GenericSender<WebGPUMsg>,
122 paint_api: CrossProcessPaintApi,
123 webrender_external_image_id_manager: WebRenderExternalImageIdManager,
124 wgpu_image_map: WebGpuExternalImageMap,
125 ) -> Self {
126 let backend_pref = pref!(dom_webgpu_wgpu_backend);
127 let backends = if backend_pref.is_empty() {
128 wgt::Backends::PRIMARY
129 } else {
130 info!(
131 "Selecting backends based on dom.webgpu.wgpu_backend pref: {:?}",
132 backend_pref
133 );
134 wgt::Backends::from_comma_list(&backend_pref)
135 };
136 let global = Arc::new(wgc::global::Global::new(
137 "wgpu-core",
138 &InstanceDescriptor {
139 backends,
140 ..Default::default()
141 },
142 ));
143 WGPU {
144 poller: Poller::new(Arc::clone(&global)),
145 receiver,
146 sender,
147 script_sender,
148 global,
149 devices: Arc::new(Mutex::new(FxHashMap::default())),
150 error_command_encoders: FxHashMap::default(),
151 paint_api,
152 webrender_external_image_id_manager,
153 wgpu_image_map,
154 compute_passes: FxHashMap::default(),
155 render_passes: FxHashMap::default(),
156 }
157 }
158
159 pub(crate) fn run(&mut self) {
160 loop {
161 if let Ok(msg) = self.receiver.recv() {
162 log::trace!("recv: {msg:?}");
163 match msg {
164 WebGPURequest::SetImageKey {
165 context_id,
166 image_key,
167 } => self.set_image_key(context_id, image_key),
168 WebGPURequest::BufferMapAsync {
169 callback: sender,
170 buffer_id,
171 device_id,
172 host_map,
173 offset,
174 size,
175 } => {
176 let glob = Arc::clone(&self.global);
177 let resp_sender = sender.clone();
178 let token = self.poller.token();
179 let callback = Box::from(move |result: BufferAccessResult| {
180 drop(token);
181 let response = result.and_then(|_| {
182 let global = &glob;
183 let (slice_pointer, range_size) =
184 global.buffer_get_mapped_range(buffer_id, offset, size)?;
185 let data = unsafe {
187 slice::from_raw_parts(
188 slice_pointer.as_ptr(),
189 range_size as usize,
190 )
191 };
192
193 Ok(Mapping {
194 data: GenericSharedMemory::from_bytes(data),
195 range: offset..offset + range_size,
196 mode: host_map,
197 })
198 });
199 if let Err(e) = resp_sender.send(response) {
200 warn!("Could not send BufferMapAsync Response ({})", e);
201 }
202 });
203
204 let operation = BufferMapOperation {
205 host: host_map,
206 callback: Some(callback),
207 };
208 let global = &self.global;
209 let result = global.buffer_map_async(buffer_id, offset, size, operation);
210 self.poller.wake();
211 self.maybe_dispatch_wgpu_error(device_id, result.err());
213 },
214 WebGPURequest::CommandEncoderFinish {
215 command_encoder_id,
216 device_id,
217 desc,
218 } => {
219 let global = &self.global;
220 let result = if let Some(err) =
221 self.error_command_encoders.get(&command_encoder_id)
222 {
223 Err(Error::Validation(err.clone()))
224 } else if let Some(error) =
225 global.command_encoder_finish(command_encoder_id, &desc).1
226 {
227 Err(Error::from_error(error))
228 } else {
229 Ok(())
230 };
231
232 self.encoder_record_error(command_encoder_id, &result);
234 self.maybe_dispatch_error(device_id, result.err());
236 },
237 WebGPURequest::CopyBufferToBuffer {
238 command_encoder_id,
239 source_id,
240 source_offset,
241 destination_id,
242 destination_offset,
243 size,
244 } => {
245 let global = &self.global;
246 let result = global.command_encoder_copy_buffer_to_buffer(
247 command_encoder_id,
248 source_id,
249 source_offset,
250 destination_id,
251 destination_offset,
252 Some(size),
253 );
254 self.encoder_record_error(command_encoder_id, &result);
255 },
256 WebGPURequest::CopyBufferToTexture {
257 command_encoder_id,
258 source,
259 destination,
260 copy_size,
261 } => {
262 let global = &self.global;
263 let result = global.command_encoder_copy_buffer_to_texture(
264 command_encoder_id,
265 &source,
266 &destination,
267 ©_size,
268 );
269 self.encoder_record_error(command_encoder_id, &result);
270 },
271 WebGPURequest::CopyTextureToBuffer {
272 command_encoder_id,
273 source,
274 destination,
275 copy_size,
276 } => {
277 let global = &self.global;
278 let result = global.command_encoder_copy_texture_to_buffer(
279 command_encoder_id,
280 &source,
281 &destination,
282 ©_size,
283 );
284 self.encoder_record_error(command_encoder_id, &result);
285 },
286 WebGPURequest::CopyTextureToTexture {
287 command_encoder_id,
288 source,
289 destination,
290 copy_size,
291 } => {
292 let global = &self.global;
293 let result = global.command_encoder_copy_texture_to_texture(
294 command_encoder_id,
295 &source,
296 &destination,
297 ©_size,
298 );
299 self.encoder_record_error(command_encoder_id, &result);
300 },
301 WebGPURequest::CreateBindGroup {
302 device_id,
303 bind_group_id,
304 descriptor,
305 } => {
306 let global = &self.global;
307 let (_, error) = global.device_create_bind_group(
308 device_id,
309 &descriptor,
310 Some(bind_group_id),
311 );
312 self.maybe_dispatch_wgpu_error(device_id, error);
313 },
314 WebGPURequest::CreateBindGroupLayout {
315 device_id,
316 bind_group_layout_id,
317 descriptor,
318 } => {
319 let global = &self.global;
320 if let Some(desc) = descriptor {
321 let (_, error) = global.device_create_bind_group_layout(
322 device_id,
323 &desc,
324 Some(bind_group_layout_id),
325 );
326
327 self.maybe_dispatch_wgpu_error(device_id, error);
328 }
329 },
330 WebGPURequest::CreateBuffer {
331 device_id,
332 buffer_id,
333 descriptor,
334 } => {
335 let global = &self.global;
336 let (_, error) =
337 global.device_create_buffer(device_id, &descriptor, Some(buffer_id));
338
339 self.maybe_dispatch_wgpu_error(device_id, error);
340 },
341 WebGPURequest::CreateCommandEncoder {
342 device_id,
343 command_encoder_id,
344 desc,
345 } => {
346 let global = &self.global;
347 let (_, error) = global.device_create_command_encoder(
348 device_id,
349 &desc,
350 Some(command_encoder_id),
351 );
352
353 self.maybe_dispatch_wgpu_error(device_id, error);
354 },
355 WebGPURequest::CreateComputePipeline {
356 device_id,
357 compute_pipeline_id,
358 descriptor,
359 implicit_ids,
360 async_sender: sender,
361 } => {
362 let global = &self.global;
363 let bgls = implicit_ids
364 .as_ref()
365 .map_or(Vec::with_capacity(0), |(_, bgls)| {
366 bgls.iter().map(|x| x.to_owned()).collect()
367 });
368 let implicit =
369 implicit_ids
370 .as_ref()
371 .map(|(layout, _)| ImplicitPipelineIds {
372 root_id: *layout,
373 group_ids: bgls.as_slice(),
374 });
375 let (_, error) = global.device_create_compute_pipeline(
376 device_id,
377 &descriptor,
378 Some(compute_pipeline_id),
379 implicit,
380 );
381 if let Some(sender) = sender {
382 let res = match error {
383 Some(CreateComputePipelineError::Device(DeviceError::Lost)) |
385 None => Ok(Pipeline {
386 id: compute_pipeline_id,
387 label: descriptor.label.unwrap_or_default().to_string(),
388 }),
389 Some(e) => Err(Error::from_error(e)),
390 };
391 if let Err(e) = sender.send(res) {
392 warn!("Failed sending WebGPUComputePipelineResponse {e:?}");
393 }
394 } else {
395 self.maybe_dispatch_wgpu_error(device_id, error);
396 }
397 },
398 WebGPURequest::CreatePipelineLayout {
399 device_id,
400 pipeline_layout_id,
401 descriptor,
402 } => {
403 let global = &self.global;
404 let (_, error) = global.device_create_pipeline_layout(
405 device_id,
406 &descriptor,
407 Some(pipeline_layout_id),
408 );
409 self.maybe_dispatch_wgpu_error(device_id, error);
410 },
411 WebGPURequest::CreateRenderPipeline {
412 device_id,
413 render_pipeline_id,
414 descriptor,
415 implicit_ids,
416 async_sender: sender,
417 } => {
418 let global = &self.global;
419 let bgls = implicit_ids
420 .as_ref()
421 .map_or(Vec::with_capacity(0), |(_, bgls)| {
422 bgls.iter().map(|x| x.to_owned()).collect()
423 });
424 let implicit =
425 implicit_ids
426 .as_ref()
427 .map(|(layout, _)| ImplicitPipelineIds {
428 root_id: *layout,
429 group_ids: bgls.as_slice(),
430 });
431 let (_, error) = global.device_create_render_pipeline(
432 device_id,
433 &descriptor,
434 Some(render_pipeline_id),
435 implicit,
436 );
437
438 if let Some(sender) = sender {
439 let res = match error {
440 Some(CreateRenderPipelineError::Device(DeviceError::Lost)) |
442 None => Ok(Pipeline {
443 id: render_pipeline_id,
444 label: descriptor.label.unwrap_or_default().to_string(),
445 }),
446 Some(e) => Err(Error::from_error(e)),
447 };
448 if let Err(e) = sender.send(res) {
449 warn!("Failed sending WebGPURenderPipelineResponse {e:?}");
450 }
451 } else {
452 self.maybe_dispatch_wgpu_error(device_id, error);
453 }
454 },
455 WebGPURequest::CreateSampler {
456 device_id,
457 sampler_id,
458 descriptor,
459 } => {
460 let global = &self.global;
461 let (_, error) =
462 global.device_create_sampler(device_id, &descriptor, Some(sampler_id));
463 self.maybe_dispatch_wgpu_error(device_id, error);
464 },
465 WebGPURequest::CreateShaderModule {
466 device_id,
467 program_id,
468 program,
469 label,
470 callback: sender,
471 } => {
472 let global = &self.global;
473 let source =
474 wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&program));
475 let desc = ShaderModuleDescriptor {
476 label: label.map(|s| s.into()),
477 runtime_checks: wgt::ShaderRuntimeChecks::checked(),
478 };
479 let (_, error) = global.device_create_shader_module(
480 device_id,
481 &desc,
482 source,
483 Some(program_id),
484 );
485 if let Err(e) = sender.send(
486 error
487 .as_ref()
488 .map(|e| ShaderCompilationInfo::from(e, &program)),
489 ) {
490 warn!("Failed to send CompilationInfo {e:?}");
491 }
492 self.maybe_dispatch_wgpu_error(device_id, error);
493 },
494 WebGPURequest::CreateContext {
495 buffer_ids,
496 size,
497 sender,
498 } => {
499 let id = self
500 .webrender_external_image_id_manager
501 .next_id(WebRenderImageHandlerType::WebGpu);
502 let context_id = WebGPUContextId(id.0);
503
504 if let Err(error) = sender.send(context_id) {
505 warn!("Failed to send ContextId to new context ({error})");
506 };
507
508 self.create_context(context_id, size, buffer_ids);
509 },
510 WebGPURequest::Present {
511 context_id,
512 pending_texture,
513 size,
514 canvas_epoch,
515 } => {
516 self.present(context_id, pending_texture, size, canvas_epoch);
517 },
518 WebGPURequest::GetImage {
519 context_id,
520 pending_texture,
521 sender,
522 } => self.get_image(context_id, pending_texture, sender),
523 WebGPURequest::ValidateTextureDescriptor {
524 device_id,
525 texture_id,
526 descriptor,
527 } => {
528 let global = &self.global;
531 let (_, error) =
532 global.device_create_texture(device_id, &descriptor, Some(texture_id));
533 global.texture_drop(texture_id);
534 self.poller.wake();
535 if let Err(e) = self.script_sender.send(WebGPUMsg::FreeTexture(texture_id))
536 {
537 warn!("Unable to send FreeTexture({:?}) ({:?})", texture_id, e);
538 };
539 self.maybe_dispatch_wgpu_error(device_id, error);
540 },
541 WebGPURequest::DestroyContext { context_id } => {
542 self.destroy_context(context_id);
543 self.webrender_external_image_id_manager
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 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 {
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 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 {
1166 device_id,
1167 callback: sender,
1168 } => {
1169 let mut devices = self.devices.lock().unwrap();
1171 let device_scope = devices
1172 .get_mut(&device_id)
1173 .expect("Device should not be dropped by this point");
1174 let result =
1175 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1176 if let Some(error_scope) = error_scope_stack.pop() {
1177 Ok(
1178 error_scope.errors.first().cloned(),
1180 )
1181 } else {
1182 Err(PopError::Empty)
1183 }
1184 } else {
1185 Err(PopError::Lost)
1187 };
1188 if let Err(error) = sender.send(result) {
1189 warn!("Error while sending PopErrorScope result: {error}");
1190 }
1191 },
1192 WebGPURequest::ComputeGetBindGroupLayout {
1193 device_id,
1194 pipeline_id,
1195 index,
1196 id,
1197 } => {
1198 let global = &self.global;
1199 let (_, error) = global.compute_pipeline_get_bind_group_layout(
1200 pipeline_id,
1201 index,
1202 Some(id),
1203 );
1204 self.maybe_dispatch_wgpu_error(device_id, error);
1205 },
1206 WebGPURequest::RenderGetBindGroupLayout {
1207 device_id,
1208 pipeline_id,
1209 index,
1210 id,
1211 } => {
1212 let global = &self.global;
1213 let (_, error) = global.render_pipeline_get_bind_group_layout(
1214 pipeline_id,
1215 index,
1216 Some(id),
1217 );
1218 self.maybe_dispatch_wgpu_error(device_id, error);
1219 },
1220 }
1221 }
1222 }
1223 if let Err(e) = self.script_sender.send(WebGPUMsg::Exit) {
1224 warn!("Failed to send WebGPUMsg::Exit to script ({})", e);
1225 }
1226 }
1227
1228 fn maybe_dispatch_wgpu_error<E: std::error::Error + 'static>(
1229 &mut self,
1230 device_id: id::DeviceId,
1231 error: Option<E>,
1232 ) {
1233 self.maybe_dispatch_error(device_id, error.map(|e| Error::from_error(e)))
1234 }
1235
1236 fn maybe_dispatch_error(&mut self, device_id: id::DeviceId, error: Option<Error>) {
1238 if let Some(error) = error {
1239 self.dispatch_error(device_id, error);
1240 }
1241 }
1242
1243 fn dispatch_error(&mut self, device_id: id::DeviceId, error: Error) {
1245 let mut devices = self.devices.lock().unwrap();
1246 let device_scope = devices
1247 .get_mut(&device_id)
1248 .expect("Device should not be dropped by this point");
1249 if let Some(error_scope_stack) = &mut device_scope.error_scope_stack {
1250 if let Some(error_scope) = error_scope_stack
1251 .iter_mut()
1252 .rev()
1253 .find(|error_scope| error_scope.filter == error.filter())
1254 {
1255 error_scope.errors.push(error);
1256 } else if self
1257 .script_sender
1258 .send(WebGPUMsg::UncapturedError {
1259 device: WebGPUDevice(device_id),
1260 pipeline_id: device_scope.pipeline_id,
1261 error: error.clone(),
1262 })
1263 .is_err()
1264 {
1265 warn!("Failed to send WebGPUMsg::UncapturedError: {error:?}");
1266 }
1267 } }
1269
1270 fn encoder_record_error<U, T: std::fmt::Debug>(
1271 &mut self,
1272 encoder_id: id::CommandEncoderId,
1273 result: &Result<U, T>,
1274 ) {
1275 if let Err(e) = result {
1276 self.error_command_encoders
1277 .entry(encoder_id)
1278 .or_insert_with(|| format!("{:?}", e));
1279 }
1280 }
1281}