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