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, MutexGuard},
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 fn unconfigure_inner<'a>(
860 &self,
861 presentation: &mut MutexGuard<'a, Option<Presentation>>,
862 ) -> UserClosures {
863 let mut result = UserClosures::default();
864 if let Some(mut present) = presentation.take() {
865 if let Some(texture) = present.acquired_texture.take() {
866 texture.destroy();
867 }
868
869 let user_callbacks;
870 {
871 let snatch_guard = present.device.snatchable_lock.read();
873
874 let result;
875 (user_callbacks, result) = present
876 .device
877 .maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
878 match result {
879 Ok(_) => {}
880 Err(WaitIdleError::Device(_)) => {
881 }
883 Err(WaitIdleError::Timeout) if cfg!(target_arch = "wasm32") => {
884 }
888 Err(WaitIdleError::Timeout) => {
889 unreachable!("wait_indefinitely() should never timeout")
890 }
891 Err(WaitIdleError::WrongSubmissionIndex(_, _)) => {
892 unreachable!("no submission index was provided")
893 }
894 }
895 }
896 result.extend(user_callbacks);
897
898 for (&backend, surface) in &self.surface_per_backend {
899 if backend == present.device.backend() {
900 unsafe { surface.unconfigure(present.device.raw()) };
901 }
902 }
903 }
904 result
905 }
906
907 pub fn unconfigure(self: &Arc<Self>) {
908 profiling::scope!("Surface::unconfigure");
909 let user_callbacks;
910 {
911 let mut presentation = self.presentation.lock();
912 user_callbacks = self.unconfigure_inner(&mut presentation);
913 }
914 user_callbacks.fire();
915 }
916
917 pub fn configure(
918 self: &Arc<Self>,
919 device: &Arc<Device>,
920 config: &wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
921 ) -> Result<(), ConfigureSurfaceError> {
922 use ConfigureSurfaceError as E;
923 profiling::scope!("Surface::configure");
924
925 #[cfg(feature = "trace")]
926 if let Some(ref mut trace) = *device.trace.lock() {
927 use crate::device::trace::{Action, IntoTrace};
928
929 trace.add(Action::ConfigureSurface(self.to_trace(), config.clone()));
930 }
931
932 log::debug!("configuring surface with {config:?}");
933
934 let caps = self
935 .get_hal_capabilities(&device.adapter)
936 .map_err(|_| E::UnsupportedQueueFamily)?;
937
938 let mut hal_view_formats = Vec::new();
939 for format in config.view_formats.iter() {
940 if *format == config.format {
941 continue;
942 }
943 if !caps.formats.iter().any(|fc| fc.format == config.format) {
944 return Err(E::UnsupportedFormat {
945 requested: config.format,
946 available: caps.texture_formats().collect(),
947 });
948 }
949 if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
950 return Err(E::InvalidViewFormat(*format, config.format));
951 }
952 hal_view_formats.push(*format);
953 }
954
955 if !hal_view_formats.is_empty() {
956 device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)?;
957 }
958
959 let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
960 *caps.maximum_frame_latency.start(),
961 *caps.maximum_frame_latency.end(),
962 );
963 let mut hal_config = hal::SurfaceConfiguration {
964 maximum_frame_latency,
965 present_mode: config.present_mode,
966 composite_alpha_mode: config.alpha_mode,
967 format: config.format,
968 color_space: config.color_space,
969 extent: wgt::Extent3d {
970 width: config.width,
971 height: config.height,
972 depth_or_array_layers: 1,
973 },
974 usage: crate::conv::map_texture_usage(
975 config.usage,
976 hal::FormatAspects::COLOR,
977 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
978 | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
979 | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
980 ),
981 view_formats: hal_view_formats,
982 };
983
984 crate::device::surface_config::validate_surface_configuration(
985 &mut hal_config,
986 &caps,
987 device.limits.max_texture_dimension_2d,
988 )?;
989
990 device.check_is_valid()?;
991
992 let user_callbacks;
993 {
994 let mut presentation = self.presentation.lock();
997
998 user_callbacks = self.unconfigure_inner(&mut presentation);
999
1000 let surface_raw = self.raw(device.backend()).unwrap();
1001 match unsafe { surface_raw.configure(device.raw(), &hal_config) } {
1002 Ok(()) => (),
1003 Err(error) => {
1004 return Err(match error {
1005 hal::SurfaceError::Outdated
1006 | hal::SurfaceError::Lost
1007 | hal::SurfaceError::Occluded
1008 | hal::SurfaceError::Timeout => E::InvalidSurface,
1009 hal::SurfaceError::Device(error) => {
1010 E::Device(device.handle_hal_error(error))
1011 }
1012 hal::SurfaceError::Other(message) => {
1013 log::error!("surface configuration failed: {message}");
1014 E::InvalidSurface
1015 }
1016 });
1017 }
1018 }
1019
1020 *presentation = Some(Presentation {
1021 device: Arc::clone(device),
1022 config: config.clone(),
1023 acquired_texture: None,
1024 });
1025 }
1026 user_callbacks.fire();
1027
1028 Ok(())
1029 }
1030}
1031
1032impl Drop for Surface {
1033 #[allow(trivial_casts)]
1034 fn drop(&mut self) {
1035 profiling::scope!("Surface::drop");
1036
1037 api_log!("Surface::drop {:?}", self as *const _);
1038 let user_closures;
1039 {
1040 let mut presentation = self.presentation.lock();
1041 user_closures = self.unconfigure_inner(&mut presentation);
1042 }
1043 user_closures.fire();
1044 }
1045}
1046
1047pub struct Adapter {
1048 pub(crate) raw: hal::DynExposedAdapter,
1049 pub(crate) instance: Arc<Instance>,
1050}
1051
1052impl Adapter {
1053 pub(crate) fn new(raw: hal::DynExposedAdapter, instance: Arc<Instance>) -> Arc<Self> {
1054 Arc::new(Self { raw, instance })
1055 }
1056
1057 pub fn backend(&self) -> Backend {
1059 self.raw.backend()
1060 }
1061
1062 pub fn is_surface_supported(&self, surface: &Surface) -> bool {
1063 surface.get_hal_capabilities(self).is_ok()
1068 }
1069
1070 pub fn get_info(&self) -> wgt::AdapterInfo {
1071 self.raw.info.clone()
1072 }
1073
1074 pub fn features(&self) -> wgt::Features {
1075 self.raw.features
1076 }
1077
1078 pub fn limits(&self) -> wgt::Limits {
1079 self.raw.capabilities.limits.clone()
1080 }
1081
1082 pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
1083 self.raw.capabilities.downlevel.clone()
1084 }
1085
1086 pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1087 unsafe { self.raw.adapter.get_presentation_timestamp() }
1088 }
1089
1090 pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1091 self.raw.capabilities.cooperative_matrix_properties.clone()
1092 }
1093
1094 pub fn get_texture_format_features(
1095 &self,
1096 format: wgt::TextureFormat,
1097 ) -> wgt::TextureFormatFeatures {
1098 use hal::TextureFormatCapabilities as Tfc;
1099
1100 let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
1101 let mut allowed_usages = wgt::TextureUsages::empty();
1102
1103 allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
1104 allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
1105 allowed_usages.set(
1106 wgt::TextureUsages::TEXTURE_BINDING,
1107 caps.contains(Tfc::SAMPLED),
1108 );
1109 allowed_usages.set(
1110 wgt::TextureUsages::STORAGE_BINDING,
1111 caps.intersects(
1112 Tfc::STORAGE_WRITE_ONLY
1113 | Tfc::STORAGE_READ_ONLY
1114 | Tfc::STORAGE_READ_WRITE
1115 | Tfc::STORAGE_ATOMIC,
1116 ),
1117 );
1118 allowed_usages.set(
1119 wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT_ATTACHMENT,
1120 caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
1121 );
1122 allowed_usages.set(
1123 wgt::TextureUsages::STORAGE_ATOMIC,
1124 caps.contains(Tfc::STORAGE_ATOMIC),
1125 );
1126
1127 let mut flags = wgt::TextureFormatFeatureFlags::empty();
1128 flags.set(
1129 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
1130 caps.contains(Tfc::STORAGE_READ_ONLY),
1131 );
1132 flags.set(
1133 wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
1134 caps.contains(Tfc::STORAGE_WRITE_ONLY),
1135 );
1136 flags.set(
1137 wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1138 caps.contains(Tfc::STORAGE_READ_WRITE),
1139 );
1140
1141 flags.set(
1142 wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
1143 caps.contains(Tfc::STORAGE_ATOMIC),
1144 );
1145
1146 flags.set(
1147 wgt::TextureFormatFeatureFlags::FILTERABLE,
1148 caps.contains(Tfc::SAMPLED_LINEAR),
1149 );
1150
1151 flags.set(
1152 wgt::TextureFormatFeatureFlags::BLENDABLE,
1153 caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
1154 );
1155
1156 flags.set(
1157 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
1158 caps.contains(Tfc::MULTISAMPLE_X2),
1159 );
1160 flags.set(
1161 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
1162 caps.contains(Tfc::MULTISAMPLE_X4),
1163 );
1164 flags.set(
1165 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
1166 caps.contains(Tfc::MULTISAMPLE_X8),
1167 );
1168 flags.set(
1169 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1170 caps.contains(Tfc::MULTISAMPLE_X16),
1171 );
1172
1173 flags.set(
1174 wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
1175 caps.contains(Tfc::MULTISAMPLE_RESOLVE),
1176 );
1177
1178 wgt::TextureFormatFeatures {
1179 allowed_usages,
1180 flags,
1181 }
1182 }
1183
1184 pub unsafe fn create_device_and_queue_from_hal(
1189 self: &Arc<Self>,
1190 hal_device: hal::DynOpenDevice,
1191 desc: &DeviceDescriptor,
1192 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1193 profiling::scope!("Adapter::create_device_and_queue_from_hal");
1194 api_log!("Adapter::create_device_and_queue_from_hal");
1195
1196 let default_queue_desc = desc.default_queue.clone();
1197
1198 let device = Device::new(hal_device.device, self, desc, self.instance.flags)?;
1199 let device = Arc::new(device);
1200
1201 let queue = Queue::new(
1202 device.clone(),
1203 hal_device.queue,
1204 default_queue_desc,
1205 self.instance.flags,
1206 )?;
1207 let queue = Arc::new(queue);
1208
1209 device.set_queue(&queue);
1210 device.late_init_resources_with_queue()?;
1211
1212 resource_log!("Created Device {:?}", Arc::as_ptr(&device));
1213 resource_log!("Created Queue {:?}", Arc::as_ptr(&queue));
1214
1215 self.instance.devices.push(&device);
1216
1217 Ok((device, queue))
1218 }
1219
1220 pub fn validate_device_descriptor(
1230 &self,
1231 desc: &mut DeviceDescriptor,
1232 ) -> Result<(), RequestDeviceError> {
1233 filter_features_and_limits(
1234 self.instance.flags,
1235 &mut desc.required_features,
1236 &mut desc.required_limits,
1237 );
1238
1239 if !self.raw.features.contains(desc.required_features) {
1241 return Err(RequestDeviceError::UnsupportedFeature(
1242 desc.required_features - self.raw.features,
1243 ));
1244 }
1245
1246 if desc
1248 .required_features
1249 .intersects(wgt::Features::all_experimental_mask())
1250 && !desc.experimental_features.is_enabled()
1251 {
1252 return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
1253 desc.required_features
1254 .intersection(wgt::Features::all_experimental_mask()),
1255 ));
1256 }
1257
1258 let caps = &self.raw.capabilities;
1259 if Backends::PRIMARY.contains(Backends::from(self.backend()))
1260 && !caps.downlevel.is_webgpu_compliant()
1261 {
1262 let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
1263 log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
1264 log::warn!("{:#?}", caps.downlevel);
1265 }
1266
1267 if desc
1269 .required_features
1270 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1271 && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
1272 {
1273 log::warn!(
1274 "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
1275 This is a massive performance footgun and likely not what you wanted"
1276 );
1277 }
1278
1279 if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
1280 return Err(RequestDeviceError::LimitsExceeded(failed));
1281 }
1282
1283 normalize_max_resource_per_shader_stage_limits(&mut desc.required_limits);
1284
1285 Ok(())
1286 }
1287
1288 pub fn request_device(
1289 self: &Arc<Self>,
1290 desc: &DeviceDescriptor,
1291 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1292 profiling::scope!("Adapter::request_device");
1293 api_log!("Adapter::request_device");
1294
1295 let mut desc = desc.clone();
1296 self.validate_device_descriptor(&mut desc)?;
1297
1298 let open = unsafe {
1299 self.raw.adapter.open(
1300 desc.required_features,
1301 &desc.required_limits,
1302 &desc.memory_hints,
1303 )
1304 }
1305 .map_err(DeviceError::from_hal)?;
1306
1307 unsafe { self.create_device_and_queue_from_hal(open, &desc) }
1308 }
1309}
1310
1311impl Drop for Adapter {
1312 #[allow(trivial_casts)]
1313 fn drop(&mut self) {
1314 profiling::scope!("Adapter::drop");
1315 api_log!("Adapter::drop {:?}", self as *const _);
1316 }
1317}
1318
1319crate::impl_resource_type!(Adapter);
1320crate::impl_storage_item!(Adapter);
1321
1322#[derive(Clone, Debug, Error)]
1323#[non_exhaustive]
1324pub enum GetSurfaceSupportError {
1325 #[error("Surface is not supported for the specified backend {0}")]
1326 NotSupportedByBackend(Backend),
1327 #[error("Failed to retrieve surface capabilities for the specified adapter.")]
1328 FailedToRetrieveSurfaceCapabilitiesForAdapter,
1329}
1330
1331#[derive(Clone, Debug, Error)]
1332#[non_exhaustive]
1334pub enum RequestDeviceError {
1335 #[error(transparent)]
1336 Device(#[from] DeviceError),
1337 #[error(transparent)]
1338 LimitsExceeded(#[from] FailedLimit),
1339 #[error("Failed to initialize Timestamp Normalizer")]
1340 TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
1341 #[error("Unsupported features were requested: {0}")]
1342 UnsupportedFeature(wgt::Features),
1343 #[error(
1344 "Some experimental features, {0}, were requested, but experimental features are not enabled"
1345 )]
1346 ExperimentalFeaturesNotEnabled(wgt::Features),
1347}
1348
1349#[derive(Clone, Debug, Error)]
1350#[non_exhaustive]
1351pub enum CreateSurfaceError {
1352 #[error("The backend {0} was not enabled on the instance.")]
1353 BackendNotEnabled(Backend),
1354 #[error("Failed to create surface for any enabled backend: {0:?}")]
1355 FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
1356 #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
1357 MismatchingDisplayHandle,
1358 #[error(
1359 "No `DisplayHandle` is available to create this surface with. When creating a surface with `create_surface()` \
1360 you must specify a display handle in `InstanceDescriptor::display`. \
1361 Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
1362 you must use `create_surface_unsafe()`."
1363 )]
1364 MissingDisplayHandle,
1365}
1366
1367fn adapter_allowed(
1372 flags: InstanceFlags,
1373 info: &impl fmt::Debug,
1374 limits: &wgt::Limits,
1375 downlevel: &wgt::DownlevelCapabilities,
1376) -> bool {
1377 let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1383 if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1384 log::error!(
1385 "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1386 info,
1387 min_uniform_buffer_offset_alignment
1388 );
1389 return false;
1390 }
1391 let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1392 if !min_storage_buffer_offset_alignment.is_power_of_two() {
1393 log::error!(
1394 "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1395 info,
1396 min_storage_buffer_offset_alignment
1397 );
1398 return false;
1399 }
1400
1401 if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1403 return true;
1404 }
1405
1406 let mut min_limits = wgt::Limits::defaults();
1408 min_limits.zero_native_only();
1409 let failed_limits = check_limits(&min_limits, limits);
1410 if !failed_limits.is_empty() {
1411 log::debug!(
1412 "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1413 info,
1414 failed_limits
1415 );
1416 return false;
1417 }
1418
1419 if !downlevel.is_webgpu_compliant() {
1420 let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1421 log::debug!(
1422 "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1423 info,
1424 missing_flags
1425 );
1426 return false;
1427 }
1428
1429 true
1430}
1431
1432fn filter_features_and_limits(
1433 flags: InstanceFlags,
1434 features: &mut wgt::Features,
1435 limits: &mut wgt::Limits,
1436) {
1437 if flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1438 *features &= wgt::Features::all_webgpu_mask() | limits::EXEMPT_FEATURES;
1439 limits.zero_native_only();
1440 }
1441}
1442
1443fn normalize_max_resource_per_shader_stage_limits(limits: &mut wgt::Limits) {
1444 limits.max_storage_buffers_per_shader_stage = [
1451 limits.max_storage_buffers_per_shader_stage,
1452 limits.max_storage_buffers_in_vertex_stage,
1453 limits.max_storage_buffers_in_fragment_stage,
1454 ]
1455 .into_iter()
1456 .max()
1457 .unwrap();
1458
1459 limits.max_storage_textures_per_shader_stage = [
1464 limits.max_storage_textures_per_shader_stage,
1465 limits.max_storage_textures_in_vertex_stage,
1466 limits.max_storage_textures_in_fragment_stage,
1467 ]
1468 .into_iter()
1469 .max()
1470 .unwrap();
1471
1472 limits.max_storage_buffers_in_vertex_stage = limits.max_storage_buffers_per_shader_stage;
1481 limits.max_storage_buffers_in_fragment_stage = limits.max_storage_buffers_per_shader_stage;
1482
1483 limits.max_storage_textures_in_vertex_stage = limits.max_storage_textures_per_shader_stage;
1487 limits.max_storage_textures_in_fragment_stage = limits.max_storage_textures_per_shader_stage;
1488}
1489
1490#[cfg(test)]
1491mod tests {
1492 use super::*;
1493
1494 fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1495 wgt::DownlevelCapabilities {
1496 flags: wgt::DownlevelFlags::compliant(),
1497 ..Default::default()
1498 }
1499 }
1500
1501 #[test]
1502 fn non_power_of_two_uniform_alignment_always_rejected() {
1503 let limits = wgt::Limits {
1504 min_uniform_buffer_offset_alignment: 3,
1505 ..wgt::Limits::defaults()
1506 };
1507 assert!(!adapter_allowed(
1508 InstanceFlags::empty(),
1509 &"",
1510 &limits,
1511 &compliant_downlevel()
1512 ));
1513 assert!(!adapter_allowed(
1514 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1515 &"",
1516 &limits,
1517 &compliant_downlevel()
1518 ));
1519 }
1520
1521 #[test]
1522 fn non_power_of_two_storage_alignment_always_rejected() {
1523 let limits = wgt::Limits {
1524 min_storage_buffer_offset_alignment: 96,
1525 ..wgt::Limits::defaults()
1526 };
1527 assert!(!adapter_allowed(
1528 InstanceFlags::empty(),
1529 &"",
1530 &limits,
1531 &compliant_downlevel()
1532 ));
1533 assert!(!adapter_allowed(
1534 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1535 &"",
1536 &limits,
1537 &compliant_downlevel()
1538 ));
1539 }
1540
1541 #[test]
1542 fn low_limits_allowed_without_strict_compliance() {
1543 let limits = wgt::Limits {
1544 max_texture_dimension_1d: 1,
1545 ..wgt::Limits::defaults()
1546 };
1547 assert!(adapter_allowed(
1548 InstanceFlags::empty(),
1549 &"",
1550 &limits,
1551 &wgt::DownlevelCapabilities::default()
1552 ));
1553 }
1554
1555 #[test]
1556 fn low_limits_rejected_with_strict_compliance() {
1557 let limits = wgt::Limits {
1558 max_texture_dimension_1d: 1,
1559 ..wgt::Limits::defaults()
1560 };
1561 assert!(!adapter_allowed(
1562 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1563 &"",
1564 &limits,
1565 &compliant_downlevel()
1566 ));
1567 }
1568
1569 #[test]
1570 fn missing_downlevel_flags_rejected_with_strict_compliance() {
1571 let downlevel = wgt::DownlevelCapabilities {
1572 flags: wgt::DownlevelFlags::empty(),
1573 ..Default::default()
1574 };
1575 assert!(!adapter_allowed(
1576 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1577 &"",
1578 &wgt::Limits::defaults(),
1579 &downlevel
1580 ));
1581 }
1582
1583 #[test]
1584 fn fully_compliant_adapter_always_allowed() {
1585 assert!(adapter_allowed(
1586 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1587 &"",
1588 &wgt::Limits::defaults(),
1589 &compliant_downlevel()
1590 ));
1591 }
1592
1593 mod storage_resource_limits {
1594 use super::*;
1595
1596 #[track_caller]
1597 fn assert_normalized_eq(non_normalized: &wgt::Limits, expected: &wgt::Limits) {
1598 let mut normalized = non_normalized.clone();
1599 normalize_max_resource_per_shader_stage_limits(&mut normalized);
1600 assert_eq!(&normalized, expected);
1601 }
1602
1603 #[test]
1604 fn normalization_is_idempotent() {
1605 let original = wgt::Limits {
1606 max_storage_buffers_in_vertex_stage: 16,
1607 max_storage_textures_in_vertex_stage: 9,
1608 ..wgt::Limits::defaults()
1609 };
1610
1611 let mut first_normalization = original.clone();
1612 normalize_max_resource_per_shader_stage_limits(&mut first_normalization);
1613 assert_ne!(original, first_normalization);
1614
1615 let mut second_normalization = first_normalization.clone();
1616 normalize_max_resource_per_shader_stage_limits(&mut second_normalization);
1617 assert_eq!(first_normalization, second_normalization);
1618 }
1619
1620 #[test]
1621 fn limits_presets_already_normalized() {
1622 [
1623 wgt::Limits::defaults(),
1624 wgt::Limits::downlevel_defaults(),
1625 wgt::Limits::downlevel_webgl2_defaults(),
1626 wgt::Limits::unlimited(),
1627 ]
1628 .iter()
1629 .for_each(|l| assert_normalized_eq(l, l))
1630 }
1631
1632 #[test]
1633 fn in_stage_raises_per_shader_stage() {
1634 assert_normalized_eq(
1635 &wgt::Limits {
1636 max_storage_buffers_per_shader_stage: 8,
1637 max_storage_buffers_in_vertex_stage: 16,
1638 max_storage_buffers_in_fragment_stage: 16,
1639 ..wgt::Limits::defaults()
1640 },
1641 &wgt::Limits {
1642 max_storage_buffers_per_shader_stage: 16,
1643 max_storage_buffers_in_vertex_stage: 16,
1644 max_storage_buffers_in_fragment_stage: 16,
1645 ..wgt::Limits::defaults()
1646 },
1647 );
1648
1649 assert_normalized_eq(
1650 &wgt::Limits {
1651 max_storage_textures_per_shader_stage: 8,
1652 max_storage_textures_in_vertex_stage: 9,
1653 max_storage_textures_in_fragment_stage: 9,
1654 ..wgt::Limits::defaults()
1655 },
1656 &wgt::Limits {
1657 max_storage_textures_per_shader_stage: 9,
1658 max_storage_textures_in_vertex_stage: 9,
1659 max_storage_textures_in_fragment_stage: 9,
1660 ..wgt::Limits::defaults()
1661 },
1662 );
1663 }
1664
1665 #[test]
1666 fn per_shader_stage_raises_in_stage() {
1667 assert_normalized_eq(
1668 &wgt::Limits {
1669 max_storage_buffers_per_shader_stage: 16,
1670 max_storage_buffers_in_vertex_stage: 4,
1671 max_storage_buffers_in_fragment_stage: 4,
1672 max_storage_textures_per_shader_stage: 8,
1673 max_storage_textures_in_vertex_stage: 4,
1674 max_storage_textures_in_fragment_stage: 4,
1675 ..wgt::Limits::defaults()
1676 },
1677 &wgt::Limits {
1678 max_storage_buffers_per_shader_stage: 16,
1679 max_storage_buffers_in_vertex_stage: 16,
1680 max_storage_buffers_in_fragment_stage: 16,
1681 max_storage_textures_per_shader_stage: 8,
1682 max_storage_textures_in_vertex_stage: 8,
1683 max_storage_textures_in_fragment_stage: 8,
1684 ..wgt::Limits::defaults()
1685 },
1686 );
1687 }
1688
1689 #[test]
1690 fn lowering_per_shader_stage_noop() {
1691 assert_normalized_eq(
1692 &wgt::Limits {
1693 max_storage_buffers_per_shader_stage: 1,
1694 max_storage_textures_per_shader_stage: 1,
1695 ..wgt::Limits::defaults()
1696 },
1697 &wgt::Limits::defaults(),
1698 );
1699 }
1700 }
1701}