1use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec, vec::Vec};
2use core::fmt;
3
4use hashbrown::HashMap;
5use thiserror::Error;
6
7use crate::{
8 api_log, api_log_debug,
9 device::{
10 queue::Queue, resource::Device, DeviceDescriptor, DeviceError, UserClosures, WaitIdleError,
11 },
12 id::markers,
13 limits::{self, check_limits, FailedLimit},
14 lock::{rank, Mutex},
15 present::{ConfigureSurfaceError, Presentation},
16 resource::ResourceType,
17 resource_log,
18 timestamp_normalization::TimestampNormalizerInitError,
19 weak_vec::WeakVec,
20 DOWNLEVEL_WARNING_MESSAGE,
21};
22
23use wgt::{Backend, Backends, InstanceFlags, PowerPreference};
24
25#[test]
26fn downlevel_default_limits_less_than_default_limits() {
27 let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
28 assert!(
29 res.is_empty(),
30 "Downlevel limits are greater than default limits",
31 )
32}
33
34#[derive(Debug)]
35pub(crate) struct InstanceDevices(Mutex<WeakVec<Device>>);
36
37impl Default for InstanceDevices {
38 fn default() -> Self {
39 Self::new()
40 }
41}
42
43impl InstanceDevices {
44 pub(crate) fn new() -> Self {
45 Self(Mutex::new(rank::INSTANCE_DEVICES, WeakVec::new()))
46 }
47
48 pub(crate) fn push(&self, device: &Arc<Device>) {
49 self.0.lock().push(Arc::downgrade(device));
50 }
51
52 fn poll_all_devices(
59 &self,
60 force_wait: bool,
61 closure_list: &mut UserClosures,
62 ) -> Result<bool, WaitIdleError> {
63 let mut all_queue_empty = true;
64 {
65 let device_guard = self.0.lock();
66
67 for device in device_guard.iter().filter_map(|device| device.upgrade()) {
68 let poll_type = if force_wait {
69 wgt::PollType::wait_indefinitely()
71 } else {
72 wgt::PollType::Poll
73 };
74
75 let (closures, result) = device.poll_and_return_closures(poll_type);
76
77 let is_queue_empty = matches!(result, Ok(wgt::PollStatus::QueueEmpty));
78
79 all_queue_empty &= is_queue_empty;
80
81 closure_list.extend(closures);
82 }
83 }
84
85 Ok(all_queue_empty)
86 }
87}
88
89#[derive(Default)]
90pub struct Instance {
91 _name: String,
92
93 instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,
97
98 requested_backends: Backends,
100
101 supported_backends: Backends,
109
110 pub flags: InstanceFlags,
111
112 display: Option<Box<dyn wgt::WgpuHasDisplayHandle>>,
118
119 devices: InstanceDevices,
121}
122
123impl Instance {
124 pub fn new(
125 name: &str,
126 mut instance_desc: wgt::InstanceDescriptor,
127 telemetry: Option<hal::Telemetry>,
128 ) -> Arc<Self> {
129 let mut this = Self {
130 _name: name.to_owned(),
131 instance_per_backend: Vec::new(),
132 requested_backends: instance_desc.backends,
133 supported_backends: Backends::empty(),
134 flags: instance_desc.flags,
135 display: instance_desc.display.take(),
139 devices: InstanceDevices::new(),
140 };
141
142 #[cfg(all(vulkan, not(target_os = "netbsd")))]
143 this.try_add_hal(hal::api::Vulkan, &instance_desc, telemetry);
144 #[cfg(metal)]
145 this.try_add_hal(hal::api::Metal, &instance_desc, telemetry);
146 #[cfg(dx12)]
147 this.try_add_hal(hal::api::Dx12, &instance_desc, telemetry);
148 #[cfg(gles)]
149 this.try_add_hal(hal::api::Gles, &instance_desc, telemetry);
150 #[cfg(feature = "noop")]
151 this.try_add_hal(hal::api::Noop, &instance_desc, telemetry);
152
153 Arc::new(this)
154 }
155
156 fn try_add_hal<A: hal::Api>(
158 &mut self,
159 _: A,
160 instance_desc: &wgt::InstanceDescriptor,
161 telemetry: Option<hal::Telemetry>,
162 ) {
163 self.supported_backends |= A::VARIANT.into();
166
167 if !instance_desc.backends.contains(A::VARIANT.into()) {
168 log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
169 return;
170 }
171
172 assert!(instance_desc.display.is_none());
174
175 let hal_desc = hal::InstanceDescriptor {
176 name: "wgpu",
177 flags: self.flags,
178 memory_budget_thresholds: instance_desc.memory_budget_thresholds,
179 backend_options: instance_desc.backend_options.clone(),
180 telemetry,
181 display: self.display.as_ref().map(|hdh| {
184 hdh.display_handle()
185 .expect("Implementation did not provide a DisplayHandle")
186 }),
187 };
188
189 use hal::Instance as _;
190 match unsafe { A::Instance::init(&hal_desc) } {
192 Ok(instance) => {
193 log::debug!("Instance::new: created {:?} backend", A::VARIANT);
194 self.instance_per_backend
195 .push((A::VARIANT, Box::new(instance)));
196 }
197 Err(err) => {
198 log::debug!(
199 "Instance::new: failed to create {:?} backend: {:?}",
200 A::VARIANT,
201 err
202 );
203 }
204 }
205 }
206
207 pub fn from_hal_instance<A: hal::Api>(
208 name: String,
209 hal_instance: <A as hal::Api>::Instance,
210 ) -> Arc<Self> {
211 Arc::new(Self {
212 _name: name,
213 instance_per_backend: vec![(A::VARIANT, Box::new(hal_instance))],
214 requested_backends: A::VARIANT.into(),
215 supported_backends: A::VARIANT.into(),
216 flags: InstanceFlags::default(),
217 display: None, devices: InstanceDevices::new(),
219 })
220 }
221
222 pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynInstance> {
223 self.instance_per_backend
224 .iter()
225 .find_map(|(instance_backend, instance)| {
226 (*instance_backend == backend).then(|| instance.as_ref())
227 })
228 }
229
230 pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {
234 self.raw(A::VARIANT).map(|instance| {
235 instance
236 .as_any()
237 .downcast_ref()
238 .expect("Stored instance is not of the correct type")
240 })
241 }
242
243 pub unsafe fn create_surface(
259 &self,
260 display_handle: Option<raw_window_handle::RawDisplayHandle>,
261 window_handle: raw_window_handle::RawWindowHandle,
262 ) -> Result<Arc<Surface>, CreateSurfaceError> {
263 profiling::scope!("Instance::create_surface");
264
265 let instance_display_handle = self.display.as_ref().map(|d| {
266 d.display_handle()
267 .expect("Implementation did not provide a DisplayHandle")
268 .as_raw()
269 });
270 let display_handle = match (instance_display_handle, display_handle) {
271 (Some(a), Some(b)) => {
272 if a != b {
273 return Err(CreateSurfaceError::MismatchingDisplayHandle);
274 }
275 a
276 }
277 (Some(hnd), None) => hnd,
278 (None, Some(hnd)) => hnd,
279 (None, None) => return Err(CreateSurfaceError::MissingDisplayHandle),
280 };
281
282 let mut errors = HashMap::default();
283 let mut surface_per_backend = HashMap::default();
284
285 for (backend, instance) in &self.instance_per_backend {
286 match unsafe {
287 instance
288 .as_ref()
289 .create_surface(display_handle, window_handle)
290 } {
291 Ok(raw) => {
292 surface_per_backend.insert(*backend, raw);
293 }
294 Err(err) => {
295 log::debug!(
296 "Instance::create_surface: failed to create surface for {backend:?}: {err:?}"
297 );
298 errors.insert(*backend, err);
299 }
300 }
301 }
302
303 if surface_per_backend.is_empty() {
304 Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
305 errors,
306 ))
307 } else {
308 let surface = Arc::new(Surface {
309 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
310 surface_per_backend,
311 });
312
313 Ok(surface)
314 }
315 }
316
317 #[cfg(drm)]
329 #[cfg_attr(not(vulkan), expect(unused_variables, unused_mut))]
330 pub unsafe fn create_surface_from_drm(
331 &self,
332 fd: i32,
333 plane: u32,
334 connector_id: u32,
335 width: u32,
336 height: u32,
337 refresh_rate: u32,
338 ) -> Result<Arc<Surface>, CreateSurfaceError> {
339 profiling::scope!("Instance::create_surface_from_drm");
340
341 let mut errors = HashMap::default();
342 let mut surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>> =
343 HashMap::default();
344
345 #[cfg(vulkan)]
346 {
347 let instance = unsafe { self.as_hal::<hal::api::Vulkan>() }
348 .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Vulkan))?;
349
350 match unsafe {
352 instance.create_surface_from_drm(
353 fd,
354 plane,
355 connector_id,
356 width,
357 height,
358 refresh_rate,
359 )
360 } {
361 Ok(surface) => {
362 surface_per_backend.insert(Backend::Vulkan, Box::new(surface));
363 }
364 Err(err) => {
365 errors.insert(Backend::Vulkan, err);
366 }
367 }
368 }
369
370 if surface_per_backend.is_empty() {
371 Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
372 errors,
373 ))
374 } else {
375 let surface = Arc::new(Surface {
376 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
377 surface_per_backend,
378 });
379
380 Ok(surface)
381 }
382 }
383
384 #[cfg(metal)]
388 pub unsafe fn create_surface_metal(
389 &self,
390 layer: *mut core::ffi::c_void,
391 ) -> Result<Arc<Surface>, CreateSurfaceError> {
392 profiling::scope!("Instance::create_surface_metal");
393
394 let instance = unsafe { self.as_hal::<hal::api::Metal>() }
395 .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Metal))?;
396
397 let layer = layer.cast();
398 let layer = unsafe { &*layer };
410 let raw_surface: Box<dyn hal::DynSurface> =
411 Box::new(instance.create_surface_from_layer(layer));
412
413 let surface = Arc::new(Surface {
414 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
415 surface_per_backend: core::iter::once((Backend::Metal, raw_surface)).collect(),
416 });
417
418 Ok(surface)
419 }
420
421 #[cfg(dx12)]
422 fn create_surface_dx12(
423 &self,
424 create_surface_func: impl FnOnce(&hal::dx12::Instance) -> hal::dx12::Surface,
425 ) -> Result<Arc<Surface>, CreateSurfaceError> {
426 let instance = unsafe { self.as_hal::<hal::api::Dx12>() }
427 .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?;
428 let surface: Box<dyn hal::DynSurface> = Box::new(create_surface_func(instance));
429
430 let surface = Arc::new(Surface {
431 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
432 surface_per_backend: core::iter::once((Backend::Dx12, surface)).collect(),
433 });
434
435 Ok(surface)
436 }
437
438 #[cfg(dx12)]
439 pub unsafe fn create_surface_from_visual(
443 &self,
444 visual: *mut core::ffi::c_void,
445 ) -> Result<Arc<Surface>, CreateSurfaceError> {
446 profiling::scope!("Instance::instance_create_surface_from_visual");
447 self.create_surface_dx12(|inst| unsafe { inst.create_surface_from_visual(visual) })
448 }
449
450 #[cfg(dx12)]
451 pub unsafe fn create_surface_from_surface_handle(
455 &self,
456 surface_handle: *mut core::ffi::c_void,
457 ) -> Result<Arc<Surface>, CreateSurfaceError> {
458 profiling::scope!("Instance::instance_create_surface_from_surface_handle");
459 self.create_surface_dx12(|inst| unsafe {
460 inst.create_surface_from_surface_handle(surface_handle)
461 })
462 }
463
464 #[cfg(dx12)]
465 pub unsafe fn create_surface_from_swap_chain_panel(
469 &self,
470 swap_chain_panel: *mut core::ffi::c_void,
471 ) -> Result<Arc<Surface>, CreateSurfaceError> {
472 profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
473 self.create_surface_dx12(|inst| unsafe {
474 inst.create_surface_from_swap_chain_panel(swap_chain_panel)
475 })
476 }
477
478 fn adapter_allowed(&self, raw: &hal::DynExposedAdapter) -> bool {
479 adapter_allowed(
480 self.flags,
481 &raw.info,
482 &raw.capabilities.limits,
483 &raw.capabilities.downlevel,
484 )
485 }
486
487 pub fn enumerate_adapters(
488 self: &Arc<Self>,
489 backends: Backends,
490 apply_limit_buckets: bool,
491 ) -> Vec<Arc<Adapter>> {
492 profiling::scope!("Instance::enumerate_adapters");
493 api_log!("Instance::enumerate_adapters");
494
495 let mut adapters = Vec::new();
496 for (_backend, instance) in self
497 .instance_per_backend
498 .iter()
499 .filter(|(backend, _)| backends.contains(Backends::from(*backend)))
500 {
501 profiling::scope!("enumerating", &*alloc::format!("{_backend:?}"));
504
505 let hal_adapters = unsafe { instance.enumerate_adapters(None) };
506
507 adapters.extend(
508 hal_adapters
509 .into_iter()
510 .map(|mut raw| {
511 self.adjust_limits_for_indirect_validation(&mut raw.capabilities.limits);
512 raw
513 })
514 .map(|mut raw| {
515 filter_features_and_limits(
516 self.flags,
517 &mut raw.features,
518 &mut raw.capabilities.limits,
519 );
520 raw
521 })
522 .filter(|raw| self.adapter_allowed(raw))
523 .filter_map(|raw| {
524 if apply_limit_buckets {
525 limits::apply_limit_buckets(raw)
526 } else {
527 Some(raw)
528 }
529 })
530 .map(|raw| {
531 let adapter = Adapter::new(raw, self.clone());
532 api_log_debug!("Adapter {:?}", adapter.raw.info);
533 adapter
534 }),
535 );
536 }
537 adapters
538 }
539
540 pub fn request_adapter(
541 self: &Arc<Self>,
542 desc: &wgt::RequestAdapterOptions<&Surface>,
543 backends: Backends,
544 ) -> Result<Arc<Adapter>, wgt::RequestAdapterError> {
545 profiling::scope!("Instance::request_adapter");
546 api_log!("Instance::request_adapter");
547
548 let mut adapters = Vec::new();
549 let mut incompatible_surface_backends = Backends::empty();
550 let mut no_fallback_backends = Backends::empty();
551 let mut no_adapter_backends = Backends::empty();
552
553 for &(backend, ref instance) in self
554 .instance_per_backend
555 .iter()
556 .filter(|&&(backend, _)| backends.contains(Backends::from(backend)))
557 {
558 let compatible_hal_surface = desc
559 .compatible_surface
560 .and_then(|surface| surface.raw(backend));
561
562 let mut backend_adapters =
563 unsafe { instance.enumerate_adapters(compatible_hal_surface) };
564 if backend_adapters.is_empty() {
565 log::debug!("enabled backend `{backend:?}` has no adapters");
566 no_adapter_backends |= Backends::from(backend);
567 continue;
569 }
570
571 if desc.force_fallback_adapter {
572 log::debug!("Filtering `{backend:?}` for `force_fallback_adapter`");
573 backend_adapters.retain(|exposed| {
574 let keep = exposed.info.device_type == wgt::DeviceType::Cpu;
575 if !keep {
576 log::debug!("* Eliminating adapter `{}`", exposed.info.name);
577 }
578 keep
579 });
580 if backend_adapters.is_empty() {
581 log::debug!("* Backend `{backend:?}` has no fallback adapters");
582 no_fallback_backends |= Backends::from(backend);
583 continue;
584 }
585 }
586
587 if let Some(surface) = desc.compatible_surface {
588 backend_adapters.retain(|exposed| {
589 let capabilities = surface.get_capabilities_with_raw(exposed);
590 if let Err(err) = capabilities {
591 log::debug!(
592 "Adapter {:?} not compatible with surface: {}",
593 exposed.info,
594 err
595 );
596 incompatible_surface_backends |= Backends::from(backend);
597 false
598 } else {
599 true
600 }
601 });
602 if backend_adapters.is_empty() {
603 incompatible_surface_backends |= Backends::from(backend);
604 continue;
605 }
606 }
607
608 let backend_adapters = backend_adapters
609 .into_iter()
610 .map(|mut raw| {
611 self.adjust_limits_for_indirect_validation(&mut raw.capabilities.limits);
612 raw
613 })
614 .map(|mut raw| {
615 filter_features_and_limits(
616 self.flags,
617 &mut raw.features,
618 &mut raw.capabilities.limits,
619 );
620 raw
621 })
622 .filter(|raw| self.adapter_allowed(raw));
623
624 if desc.apply_limit_buckets {
625 adapters.extend(backend_adapters.filter_map(limits::apply_limit_buckets));
626 } else {
627 adapters.extend(backend_adapters);
628 }
629 }
630
631 match desc.power_preference {
632 PowerPreference::LowPower => {
633 sort(&mut adapters, true);
634 }
635 PowerPreference::HighPerformance => {
636 sort(&mut adapters, false);
637 }
638 PowerPreference::None => {}
639 };
640
641 fn sort(adapters: &mut [hal::DynExposedAdapter], prefer_integrated_gpu: bool) {
642 adapters
643 .sort_by_key(|adapter| get_order(adapter.info.device_type, prefer_integrated_gpu));
644 }
645
646 fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 {
647 match device_type {
655 wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2,
656 wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1,
657 wgt::DeviceType::DiscreteGpu => 1,
658 wgt::DeviceType::IntegratedGpu => 2,
659 wgt::DeviceType::Other => 3,
660 wgt::DeviceType::VirtualGpu => 4,
661 wgt::DeviceType::Cpu => 5,
662 }
663 }
664
665 if adapters.is_empty() {
668 log::debug!("Request adapter didn't find compatible adapters.");
669 } else {
670 log::debug!(
671 "Found {} compatible adapters. Sorted by preference:",
672 adapters.len()
673 );
674 for adapter in &adapters {
675 log::debug!("* {:?}", adapter.info);
676 }
677 }
678
679 if let Some(adapter) = adapters.into_iter().next() {
680 api_log_debug!("Request adapter result {:?}", adapter.info);
681 let adapter = Adapter::new(adapter, self.clone());
682 Ok(adapter)
683 } else {
684 Err(wgt::RequestAdapterError::NotFound {
685 supported_backends: self.supported_backends,
686 requested_backends: self.requested_backends,
687 active_backends: self.active_backends(),
688 no_fallback_backends,
689 no_adapter_backends,
690 incompatible_surface_backends,
691 })
692 }
693 }
694
695 fn adjust_limits_for_indirect_validation(&self, limits: &mut wgt::Limits) {
698 if self.flags.contains(InstanceFlags::VALIDATION_INDIRECT_CALL) {
701 limits.max_buffer_size = limits.max_buffer_size.min(u32::MAX as u64);
702 limits.max_uniform_buffer_binding_size =
703 limits.max_uniform_buffer_binding_size.min(u32::MAX as u64);
704 limits.max_storage_buffer_binding_size = limits
705 .max_storage_buffer_binding_size
706 .min(u32::MAX as u64 & !(wgt::STORAGE_BINDING_SIZE_ALIGNMENT as u64 - 1));
707 }
708 }
709
710 fn active_backends(&self) -> Backends {
711 self.instance_per_backend
712 .iter()
713 .map(|&(backend, _)| Backends::from(backend))
714 .collect()
715 }
716
717 pub unsafe fn create_adapter_from_hal(
731 self: &Arc<Self>,
732 hal_adapter: hal::DynExposedAdapter,
733 ) -> Arc<Adapter> {
734 profiling::scope!("Instance::create_adapter_from_hal");
735
736 let adapter = Adapter::new(hal_adapter, self.clone());
737
738 resource_log!("Created Adapter {:?}", Arc::as_ptr(&adapter));
739 adapter
740 }
741
742 pub fn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {
749 api_log!("poll_all_devices");
750 let mut closures = UserClosures::default();
751 let all_queue_empty = self.devices.poll_all_devices(force_wait, &mut closures)?;
752
753 closures.fire();
754
755 Ok(all_queue_empty)
756 }
757}
758
759pub struct Surface {
760 pub(crate) presentation: Mutex<Option<Presentation>>,
761 pub surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>>,
762}
763
764impl ResourceType for Surface {
765 const TYPE: &'static str = "Surface";
766}
767impl crate::storage::StorageItem for Surface {
768 type Marker = markers::Surface;
769}
770
771impl Surface {
772 pub fn get_capabilities(
773 &self,
774 adapter: &Adapter,
775 ) -> Result<wgt::SurfaceCapabilities, GetSurfaceSupportError> {
776 profiling::scope!("Surface::get_capabilities");
777 let mut hal_caps = self.get_hal_capabilities(adapter)?;
778
779 hal_caps
780 .formats
781 .sort_by_key(|fc| !fc.format.has_srgb_suffix());
782
783 let usages = crate::conv::map_texture_usage_from_hal(hal_caps.usage);
784
785 Ok(wgt::SurfaceCapabilities {
791 formats: hal_caps
792 .formats
793 .iter()
794 .filter(|fc| {
795 crate::device::surface_config::resolve_auto_color_space(
796 fc.format,
797 fc.color_spaces,
798 )
799 .is_some()
800 })
801 .map(|fc| fc.format)
802 .collect(),
803 format_capabilities: hal_caps.formats,
804 present_modes: hal_caps.present_modes,
805 alpha_modes: hal_caps.composite_alpha_modes,
806 usages,
807 })
808 }
809
810 pub fn get_hal_capabilities(
811 &self,
812 adapter: &Adapter,
813 ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
814 self.get_capabilities_with_raw(&adapter.raw)
815 }
816
817 pub fn get_capabilities_with_raw(
818 &self,
819 adapter: &hal::DynExposedAdapter,
820 ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
821 let backend = adapter.backend();
822 let suf = self
823 .raw(backend)
824 .ok_or(GetSurfaceSupportError::NotSupportedByBackend(backend))?;
825 profiling::scope!("surface_capabilities");
826 let caps = unsafe { adapter.adapter.surface_capabilities(suf) }
827 .ok_or(GetSurfaceSupportError::FailedToRetrieveSurfaceCapabilitiesForAdapter)?;
828 Ok(caps)
829 }
830
831 pub fn display_hdr_info(&self, adapter: &Adapter) -> wgt::DisplayHdrInfo {
837 profiling::scope!("Surface::display_hdr_info");
838 self.display_hdr_info_with_raw(&adapter.raw)
839 }
840
841 pub fn display_hdr_info_with_raw(
842 &self,
843 adapter: &hal::DynExposedAdapter,
844 ) -> wgt::DisplayHdrInfo {
845 let backend = adapter.backend();
846 let Some(suf) = self.raw(backend) else {
847 return wgt::DisplayHdrInfo::default();
848 };
849 profiling::scope!("surface_display_hdr_info");
850 unsafe { adapter.adapter.surface_display_hdr_info(suf) }.unwrap_or_default()
851 }
852
853 pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynSurface> {
854 self.surface_per_backend
855 .get(&backend)
856 .map(|surface| surface.as_ref())
857 }
858
859 pub fn configure(
860 self: &Arc<Self>,
861 device: &Arc<Device>,
862 config: &wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
863 ) -> Option<ConfigureSurfaceError> {
864 use ConfigureSurfaceError as E;
865 profiling::scope!("Surface::configure");
866
867 #[cfg(feature = "trace")]
868 if let Some(ref mut trace) = *device.trace.lock() {
869 use crate::device::trace::{Action, IntoTrace};
870
871 trace.add(Action::ConfigureSurface(self.to_trace(), config.clone()));
872 }
873
874 log::debug!("configuring surface with {config:?}");
875
876 let error = 'error: {
877 let user_callbacks;
879 {
880 if let Err(e) = device.check_is_valid() {
881 break 'error e.into();
882 }
883
884 let caps = match self.get_hal_capabilities(&device.adapter) {
885 Ok(caps) => caps,
886 Err(_) => break 'error E::UnsupportedQueueFamily,
887 };
888
889 let mut hal_view_formats = Vec::new();
890 for format in config.view_formats.iter() {
891 if *format == config.format {
892 continue;
893 }
894 if !caps.formats.iter().any(|fc| fc.format == config.format) {
895 break 'error E::UnsupportedFormat {
896 requested: config.format,
897 available: caps.texture_formats().collect(),
898 };
899 }
900 if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
901 break 'error E::InvalidViewFormat(*format, config.format);
902 }
903 hal_view_formats.push(*format);
904 }
905
906 if !hal_view_formats.is_empty() {
907 if let Err(missing_flag) =
908 device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)
909 {
910 break 'error E::MissingDownlevelFlags(missing_flag);
911 }
912 }
913
914 let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
915 *caps.maximum_frame_latency.start(),
916 *caps.maximum_frame_latency.end(),
917 );
918 let mut hal_config = hal::SurfaceConfiguration {
919 maximum_frame_latency,
920 present_mode: config.present_mode,
921 composite_alpha_mode: config.alpha_mode,
922 format: config.format,
923 color_space: config.color_space,
924 extent: wgt::Extent3d {
925 width: config.width,
926 height: config.height,
927 depth_or_array_layers: 1,
928 },
929 usage: crate::conv::map_texture_usage(
930 config.usage,
931 hal::FormatAspects::COLOR,
932 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
933 | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
934 | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
935 ),
936 view_formats: hal_view_formats,
937 };
938
939 if let Err(error) = crate::device::surface_config::validate_surface_configuration(
940 &mut hal_config,
941 &caps,
942 device.limits.max_texture_dimension_2d,
943 ) {
944 break 'error error;
945 }
946
947 let snatch_guard = device.snatchable_lock.read();
949
950 let maintain_result;
951 (user_callbacks, maintain_result) =
952 device.maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
953
954 match maintain_result {
955 Ok(wgt::PollStatus::QueueEmpty) => {}
957 Ok(wgt::PollStatus::WaitSucceeded) => {
958 break 'error E::GpuWaitTimeout;
961 }
962 Ok(wgt::PollStatus::Poll) => {
963 unreachable!("Cannot get a Poll result from a Wait action.")
964 }
965 Err(WaitIdleError::Timeout) if cfg!(target_family = "wasm") => {
966 }
971 Err(e) => {
972 break 'error e.into();
973 }
974 }
975
976 if let Some(present) = self.presentation.lock().take() {
978 if present.acquired_texture.is_some() {
979 break 'error E::PreviousOutputExists;
980 }
981 }
982
983 let surface_raw = self.raw(device.backend()).unwrap();
990 match unsafe { surface_raw.configure(device.raw(), &hal_config) } {
991 Ok(()) => (),
992 Err(error) => {
993 break 'error match error {
994 hal::SurfaceError::Outdated
995 | hal::SurfaceError::Lost
996 | hal::SurfaceError::Occluded
997 | hal::SurfaceError::Timeout => E::InvalidSurface,
998 hal::SurfaceError::Device(error) => {
999 E::Device(device.handle_hal_error(error))
1000 }
1001 hal::SurfaceError::Other(message) => {
1002 log::error!("surface configuration failed: {message}");
1003 E::InvalidSurface
1004 }
1005 }
1006 }
1007 }
1008
1009 let mut presentation = self.presentation.lock();
1010 *presentation = Some(Presentation {
1011 device: Arc::clone(device),
1012 config: config.clone(),
1013 acquired_texture: None,
1014 });
1015 }
1016
1017 user_callbacks.fire();
1018 return None;
1019 };
1020
1021 Some(error)
1022 }
1023}
1024
1025impl Drop for Surface {
1026 #[allow(trivial_casts)]
1027 fn drop(&mut self) {
1028 profiling::scope!("Surface::drop");
1029
1030 api_log!("Surface::drop {:?}", self as *const _);
1031 if let Some(present) = self.presentation.lock().take() {
1032 for (&backend, surface) in &self.surface_per_backend {
1033 if backend == present.device.backend() {
1034 unsafe { surface.unconfigure(present.device.raw()) };
1035 }
1036 }
1037 }
1038 }
1039}
1040
1041pub struct Adapter {
1042 pub(crate) raw: hal::DynExposedAdapter,
1043 pub(crate) instance: Arc<Instance>,
1044}
1045
1046impl Adapter {
1047 pub(crate) fn new(raw: hal::DynExposedAdapter, instance: Arc<Instance>) -> Arc<Self> {
1048 Arc::new(Self { raw, instance })
1049 }
1050
1051 pub fn backend(&self) -> Backend {
1053 self.raw.backend()
1054 }
1055
1056 pub fn is_surface_supported(&self, surface: &Surface) -> bool {
1057 surface.get_hal_capabilities(self).is_ok()
1062 }
1063
1064 pub fn get_info(&self) -> wgt::AdapterInfo {
1065 self.raw.info.clone()
1066 }
1067
1068 pub fn features(&self) -> wgt::Features {
1069 self.raw.features
1070 }
1071
1072 pub fn limits(&self) -> wgt::Limits {
1073 self.raw.capabilities.limits.clone()
1074 }
1075
1076 pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
1077 self.raw.capabilities.downlevel.clone()
1078 }
1079
1080 pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1081 unsafe { self.raw.adapter.get_presentation_timestamp() }
1082 }
1083
1084 pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1085 self.raw.capabilities.cooperative_matrix_properties.clone()
1086 }
1087
1088 pub fn get_texture_format_features(
1089 &self,
1090 format: wgt::TextureFormat,
1091 ) -> wgt::TextureFormatFeatures {
1092 use hal::TextureFormatCapabilities as Tfc;
1093
1094 let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
1095 let mut allowed_usages = wgt::TextureUsages::empty();
1096
1097 allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
1098 allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
1099 allowed_usages.set(
1100 wgt::TextureUsages::TEXTURE_BINDING,
1101 caps.contains(Tfc::SAMPLED),
1102 );
1103 allowed_usages.set(
1104 wgt::TextureUsages::STORAGE_BINDING,
1105 caps.intersects(
1106 Tfc::STORAGE_WRITE_ONLY
1107 | Tfc::STORAGE_READ_ONLY
1108 | Tfc::STORAGE_READ_WRITE
1109 | Tfc::STORAGE_ATOMIC,
1110 ),
1111 );
1112 allowed_usages.set(
1113 wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT_ATTACHMENT,
1114 caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
1115 );
1116 allowed_usages.set(
1117 wgt::TextureUsages::STORAGE_ATOMIC,
1118 caps.contains(Tfc::STORAGE_ATOMIC),
1119 );
1120
1121 let mut flags = wgt::TextureFormatFeatureFlags::empty();
1122 flags.set(
1123 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
1124 caps.contains(Tfc::STORAGE_READ_ONLY),
1125 );
1126 flags.set(
1127 wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
1128 caps.contains(Tfc::STORAGE_WRITE_ONLY),
1129 );
1130 flags.set(
1131 wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1132 caps.contains(Tfc::STORAGE_READ_WRITE),
1133 );
1134
1135 flags.set(
1136 wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
1137 caps.contains(Tfc::STORAGE_ATOMIC),
1138 );
1139
1140 flags.set(
1141 wgt::TextureFormatFeatureFlags::FILTERABLE,
1142 caps.contains(Tfc::SAMPLED_LINEAR),
1143 );
1144
1145 flags.set(
1146 wgt::TextureFormatFeatureFlags::BLENDABLE,
1147 caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
1148 );
1149
1150 flags.set(
1151 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
1152 caps.contains(Tfc::MULTISAMPLE_X2),
1153 );
1154 flags.set(
1155 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
1156 caps.contains(Tfc::MULTISAMPLE_X4),
1157 );
1158 flags.set(
1159 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
1160 caps.contains(Tfc::MULTISAMPLE_X8),
1161 );
1162 flags.set(
1163 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1164 caps.contains(Tfc::MULTISAMPLE_X16),
1165 );
1166
1167 flags.set(
1168 wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
1169 caps.contains(Tfc::MULTISAMPLE_RESOLVE),
1170 );
1171
1172 wgt::TextureFormatFeatures {
1173 allowed_usages,
1174 flags,
1175 }
1176 }
1177
1178 pub unsafe fn create_device_and_queue_from_hal(
1183 self: &Arc<Self>,
1184 hal_device: hal::DynOpenDevice,
1185 desc: &DeviceDescriptor,
1186 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1187 profiling::scope!("Adapter::create_device_and_queue_from_hal");
1188 api_log!("Adapter::create_device_and_queue_from_hal");
1189
1190 let default_queue_desc = desc.default_queue.clone();
1191
1192 let device = Device::new(hal_device.device, self, desc, self.instance.flags)?;
1193 let device = Arc::new(device);
1194
1195 let queue = Queue::new(
1196 device.clone(),
1197 hal_device.queue,
1198 default_queue_desc,
1199 self.instance.flags,
1200 )?;
1201 let queue = Arc::new(queue);
1202
1203 device.set_queue(&queue);
1204 device.late_init_resources_with_queue()?;
1205
1206 resource_log!("Created Device {:?}", Arc::as_ptr(&device));
1207 resource_log!("Created Queue {:?}", Arc::as_ptr(&queue));
1208
1209 self.instance.devices.push(&device);
1210
1211 Ok((device, queue))
1212 }
1213
1214 pub fn validate_device_descriptor(
1224 &self,
1225 desc: &mut DeviceDescriptor,
1226 ) -> Result<(), RequestDeviceError> {
1227 filter_features_and_limits(
1228 self.instance.flags,
1229 &mut desc.required_features,
1230 &mut desc.required_limits,
1231 );
1232
1233 if !self.raw.features.contains(desc.required_features) {
1235 return Err(RequestDeviceError::UnsupportedFeature(
1236 desc.required_features - self.raw.features,
1237 ));
1238 }
1239
1240 if desc
1242 .required_features
1243 .intersects(wgt::Features::all_experimental_mask())
1244 && !desc.experimental_features.is_enabled()
1245 {
1246 return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
1247 desc.required_features
1248 .intersection(wgt::Features::all_experimental_mask()),
1249 ));
1250 }
1251
1252 let caps = &self.raw.capabilities;
1253 if Backends::PRIMARY.contains(Backends::from(self.backend()))
1254 && !caps.downlevel.is_webgpu_compliant()
1255 {
1256 let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
1257 log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
1258 log::warn!("{:#?}", caps.downlevel);
1259 }
1260
1261 if desc
1263 .required_features
1264 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1265 && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
1266 {
1267 log::warn!(
1268 "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
1269 This is a massive performance footgun and likely not what you wanted"
1270 );
1271 }
1272
1273 if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
1274 return Err(RequestDeviceError::LimitsExceeded(failed));
1275 }
1276
1277 Ok(())
1278 }
1279
1280 pub fn request_device(
1281 self: &Arc<Self>,
1282 desc: &DeviceDescriptor,
1283 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1284 profiling::scope!("Adapter::request_device");
1285 api_log!("Adapter::request_device");
1286
1287 let mut desc = desc.clone();
1288 self.validate_device_descriptor(&mut desc)?;
1289
1290 let open = unsafe {
1291 self.raw.adapter.open(
1292 desc.required_features,
1293 &desc.required_limits,
1294 &desc.memory_hints,
1295 )
1296 }
1297 .map_err(DeviceError::from_hal)?;
1298
1299 unsafe { self.create_device_and_queue_from_hal(open, &desc) }
1300 }
1301}
1302
1303impl Drop for Adapter {
1304 #[allow(trivial_casts)]
1305 fn drop(&mut self) {
1306 profiling::scope!("Adapter::drop");
1307 api_log!("Adapter::drop {:?}", self as *const _);
1308 }
1309}
1310
1311crate::impl_resource_type!(Adapter);
1312crate::impl_storage_item!(Adapter);
1313
1314#[derive(Clone, Debug, Error)]
1315#[non_exhaustive]
1316pub enum GetSurfaceSupportError {
1317 #[error("Surface is not supported for the specified backend {0}")]
1318 NotSupportedByBackend(Backend),
1319 #[error("Failed to retrieve surface capabilities for the specified adapter.")]
1320 FailedToRetrieveSurfaceCapabilitiesForAdapter,
1321}
1322
1323#[derive(Clone, Debug, Error)]
1324#[non_exhaustive]
1326pub enum RequestDeviceError {
1327 #[error(transparent)]
1328 Device(#[from] DeviceError),
1329 #[error(transparent)]
1330 LimitsExceeded(#[from] FailedLimit),
1331 #[error("Failed to initialize Timestamp Normalizer")]
1332 TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
1333 #[error("Unsupported features were requested: {0}")]
1334 UnsupportedFeature(wgt::Features),
1335 #[error(
1336 "Some experimental features, {0}, were requested, but experimental features are not enabled"
1337 )]
1338 ExperimentalFeaturesNotEnabled(wgt::Features),
1339}
1340
1341#[derive(Clone, Debug, Error)]
1342#[non_exhaustive]
1343pub enum CreateSurfaceError {
1344 #[error("The backend {0} was not enabled on the instance.")]
1345 BackendNotEnabled(Backend),
1346 #[error("Failed to create surface for any enabled backend: {0:?}")]
1347 FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
1348 #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
1349 MismatchingDisplayHandle,
1350 #[error(
1351 "No `DisplayHandle` is available to create this surface with. When creating a surface with `create_surface()` \
1352 you must specify a display handle in `InstanceDescriptor::display`. \
1353 Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
1354 you must use `create_surface_unsafe()`."
1355 )]
1356 MissingDisplayHandle,
1357}
1358
1359fn adapter_allowed(
1364 flags: InstanceFlags,
1365 info: &impl fmt::Debug,
1366 limits: &wgt::Limits,
1367 downlevel: &wgt::DownlevelCapabilities,
1368) -> bool {
1369 let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1375 if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1376 log::error!(
1377 "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1378 info,
1379 min_uniform_buffer_offset_alignment
1380 );
1381 return false;
1382 }
1383 let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1384 if !min_storage_buffer_offset_alignment.is_power_of_two() {
1385 log::error!(
1386 "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1387 info,
1388 min_storage_buffer_offset_alignment
1389 );
1390 return false;
1391 }
1392
1393 if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1395 return true;
1396 }
1397
1398 let mut min_limits = wgt::Limits::defaults();
1400 min_limits.zero_native_only();
1401 let failed_limits = check_limits(&min_limits, limits);
1402 if !failed_limits.is_empty() {
1403 log::debug!(
1404 "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1405 info,
1406 failed_limits
1407 );
1408 return false;
1409 }
1410
1411 if !downlevel.is_webgpu_compliant() {
1412 let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1413 log::debug!(
1414 "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1415 info,
1416 missing_flags
1417 );
1418 return false;
1419 }
1420
1421 true
1422}
1423
1424fn filter_features_and_limits(
1425 flags: InstanceFlags,
1426 features: &mut wgt::Features,
1427 limits: &mut wgt::Limits,
1428) {
1429 if flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1430 *features &= wgt::Features::all_webgpu_mask() | limits::EXEMPT_FEATURES;
1431 limits.zero_native_only();
1432 }
1433}
1434
1435#[cfg(test)]
1436mod tests {
1437 use super::*;
1438
1439 fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1440 wgt::DownlevelCapabilities {
1441 flags: wgt::DownlevelFlags::compliant(),
1442 ..Default::default()
1443 }
1444 }
1445
1446 #[test]
1447 fn non_power_of_two_uniform_alignment_always_rejected() {
1448 let limits = wgt::Limits {
1449 min_uniform_buffer_offset_alignment: 3,
1450 ..wgt::Limits::defaults()
1451 };
1452 assert!(!adapter_allowed(
1453 InstanceFlags::empty(),
1454 &"",
1455 &limits,
1456 &compliant_downlevel()
1457 ));
1458 assert!(!adapter_allowed(
1459 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1460 &"",
1461 &limits,
1462 &compliant_downlevel()
1463 ));
1464 }
1465
1466 #[test]
1467 fn non_power_of_two_storage_alignment_always_rejected() {
1468 let limits = wgt::Limits {
1469 min_storage_buffer_offset_alignment: 96,
1470 ..wgt::Limits::defaults()
1471 };
1472 assert!(!adapter_allowed(
1473 InstanceFlags::empty(),
1474 &"",
1475 &limits,
1476 &compliant_downlevel()
1477 ));
1478 assert!(!adapter_allowed(
1479 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1480 &"",
1481 &limits,
1482 &compliant_downlevel()
1483 ));
1484 }
1485
1486 #[test]
1487 fn low_limits_allowed_without_strict_compliance() {
1488 let limits = wgt::Limits {
1489 max_texture_dimension_1d: 1,
1490 ..wgt::Limits::defaults()
1491 };
1492 assert!(adapter_allowed(
1493 InstanceFlags::empty(),
1494 &"",
1495 &limits,
1496 &wgt::DownlevelCapabilities::default()
1497 ));
1498 }
1499
1500 #[test]
1501 fn low_limits_rejected_with_strict_compliance() {
1502 let limits = wgt::Limits {
1503 max_texture_dimension_1d: 1,
1504 ..wgt::Limits::defaults()
1505 };
1506 assert!(!adapter_allowed(
1507 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1508 &"",
1509 &limits,
1510 &compliant_downlevel()
1511 ));
1512 }
1513
1514 #[test]
1515 fn missing_downlevel_flags_rejected_with_strict_compliance() {
1516 let downlevel = wgt::DownlevelCapabilities {
1517 flags: wgt::DownlevelFlags::empty(),
1518 ..Default::default()
1519 };
1520 assert!(!adapter_allowed(
1521 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1522 &"",
1523 &wgt::Limits::defaults(),
1524 &downlevel
1525 ));
1526 }
1527
1528 #[test]
1529 fn fully_compliant_adapter_always_allowed() {
1530 assert!(adapter_allowed(
1531 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1532 &"",
1533 &wgt::Limits::defaults(),
1534 &compliant_downlevel()
1535 ));
1536 }
1537}