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