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