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