1use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec, vec::Vec};
2use core::{
3 fmt,
4 sync::atomic::{AtomicBool, Ordering},
5};
6
7use hashbrown::HashMap;
8use thiserror::Error;
9
10use crate::{
11 api_log, api_log_debug,
12 device::{
13 queue::Queue, resource::Device, DeviceDescriptor, DeviceError, UserClosures, WaitIdleError,
14 },
15 id::markers,
16 limits::{self, check_limits, FailedLimit},
17 lock::{rank, Mutex, MutexGuard},
18 present::{ConfigureSurfaceError, Presentation},
19 resource::ResourceType,
20 resource_log,
21 timestamp_normalization::TimestampNormalizerInitError,
22 weak_vec::WeakVec,
23 DOWNLEVEL_WARNING_MESSAGE,
24};
25
26use wgt::{Backend, Backends, InstanceFlags, PowerPreference};
27
28#[derive(Debug)]
29pub(crate) struct InstanceDevices(Mutex<WeakVec<Device>>);
30
31impl Default for InstanceDevices {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl InstanceDevices {
38 pub(crate) fn new() -> Self {
39 Self(Mutex::new(rank::INSTANCE_DEVICES, WeakVec::new()))
40 }
41
42 pub(crate) fn push(&self, device: &Arc<Device>) {
43 self.0.lock().push(Arc::downgrade(device));
44 }
45
46 fn poll_all_devices(
53 &self,
54 force_wait: bool,
55 closure_list: &mut UserClosures,
56 ) -> Result<bool, WaitIdleError> {
57 let mut all_queue_empty = true;
58 {
59 let device_guard = self.0.lock();
60
61 for device in device_guard.iter().filter_map(|device| device.upgrade()) {
62 let poll_type = if force_wait {
63 wgt::PollType::wait_indefinitely()
65 } else {
66 wgt::PollType::Poll
67 };
68
69 let (closures, result) = device.poll_and_return_closures(poll_type);
70
71 let is_queue_empty = matches!(result, Ok(wgt::PollStatus::QueueEmpty));
72
73 all_queue_empty &= is_queue_empty;
74
75 closure_list.extend(closures);
76 }
77 }
78
79 Ok(all_queue_empty)
80 }
81}
82
83#[derive(Default)]
84pub struct Instance {
85 _name: String,
86
87 instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,
91
92 requested_backends: Backends,
94
95 supported_backends: Backends,
103
104 pub flags: InstanceFlags,
105
106 display: Option<Box<dyn wgt::WgpuHasDisplayHandle>>,
112
113 devices: InstanceDevices,
115}
116
117impl Instance {
118 pub fn new(
119 name: &str,
120 instance_desc: wgt::InstanceDescriptor,
121 telemetry: Option<hal::Telemetry>,
122 ) -> Arc<Self> {
123 let mut this = InstanceBuilder::new(name, instance_desc, telemetry);
124
125 #[cfg(all(vulkan, not(target_os = "netbsd")))]
126 this.try_add_hal(hal::api::Vulkan);
127 #[cfg(metal)]
128 this.try_add_hal(hal::api::Metal);
129 #[cfg(dx12)]
130 this.try_add_hal(hal::api::Dx12);
131 #[cfg(gles)]
132 this.try_add_hal(hal::api::Gles);
133 #[cfg(feature = "noop")]
134 this.try_add_hal(hal::api::Noop);
135
136 this.build()
137 }
138
139 pub fn from_hal_instance<A: hal::Api>(
140 name: String,
141 hal_instance: <A as hal::Api>::Instance,
142 ) -> Arc<Self> {
143 Arc::new(Self {
144 _name: name,
145 instance_per_backend: vec![(A::VARIANT, Box::new(hal_instance))],
146 requested_backends: A::VARIANT.into(),
147 supported_backends: A::VARIANT.into(),
148 flags: InstanceFlags::default(),
149 display: None, devices: InstanceDevices::new(),
151 })
152 }
153
154 pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynInstance> {
155 self.instance_per_backend
156 .iter()
157 .find_map(|(instance_backend, instance)| {
158 (*instance_backend == backend).then(|| instance.as_ref())
159 })
160 }
161
162 pub unsafe fn as_hal<A: hal::Api>(&self) -> Option<&A::Instance> {
166 self.raw(A::VARIANT).map(|instance| {
167 instance
168 .as_any()
169 .downcast_ref()
170 .expect("Stored instance is not of the correct type")
172 })
173 }
174
175 pub unsafe fn create_surface(
191 &self,
192 display_handle: Option<raw_window_handle::RawDisplayHandle>,
193 window_handle: raw_window_handle::RawWindowHandle,
194 ) -> Result<Arc<Surface>, CreateSurfaceError> {
195 profiling::scope!("Instance::create_surface");
196
197 let instance_display_handle = self.display.as_ref().map(|d| {
198 d.display_handle()
199 .expect("Implementation did not provide a DisplayHandle")
200 .as_raw()
201 });
202 let display_handle = match (instance_display_handle, display_handle) {
203 (Some(a), Some(b)) => {
204 if a != b {
205 return Err(CreateSurfaceError::MismatchingDisplayHandle);
206 }
207 a
208 }
209 (Some(hnd), None) => hnd,
210 (None, Some(hnd)) => hnd,
211 (None, None) => return Err(CreateSurfaceError::MissingDisplayHandle),
212 };
213
214 let mut errors = HashMap::default();
215 let mut surface_per_backend = HashMap::default();
216
217 for (backend, instance) in &self.instance_per_backend {
218 match unsafe {
219 instance
220 .as_ref()
221 .create_surface(display_handle, window_handle)
222 } {
223 Ok(raw) => {
224 surface_per_backend.insert(*backend, raw);
225 }
226 Err(err) => {
227 log::debug!(
228 "Instance::create_surface: failed to create surface for {backend:?}: {err:?}"
229 );
230 errors.insert(*backend, err);
231 }
232 }
233 }
234
235 if surface_per_backend.is_empty() {
236 Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
237 errors,
238 ))
239 } else {
240 let surface = Arc::new(Surface {
241 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
242 surface_per_backend,
243 });
244
245 Ok(surface)
246 }
247 }
248
249 #[cfg(drm)]
261 #[cfg_attr(not(vulkan), expect(unused_variables, unused_mut))]
262 pub unsafe fn create_surface_from_drm(
263 &self,
264 fd: i32,
265 plane: u32,
266 connector_id: u32,
267 width: u32,
268 height: u32,
269 refresh_rate: u32,
270 ) -> Result<Arc<Surface>, CreateSurfaceError> {
271 profiling::scope!("Instance::create_surface_from_drm");
272
273 let mut errors = HashMap::default();
274 let mut surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>> =
275 HashMap::default();
276
277 #[cfg(vulkan)]
278 {
279 let instance = unsafe { self.as_hal::<hal::api::Vulkan>() }
280 .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Vulkan))?;
281
282 match unsafe {
284 instance.create_surface_from_drm(
285 fd,
286 plane,
287 connector_id,
288 width,
289 height,
290 refresh_rate,
291 )
292 } {
293 Ok(surface) => {
294 surface_per_backend.insert(Backend::Vulkan, Box::new(surface));
295 }
296 Err(err) => {
297 errors.insert(Backend::Vulkan, err);
298 }
299 }
300 }
301
302 if surface_per_backend.is_empty() {
303 Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
304 errors,
305 ))
306 } else {
307 let surface = Arc::new(Surface {
308 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
309 surface_per_backend,
310 });
311
312 Ok(surface)
313 }
314 }
315
316 #[cfg(metal)]
320 pub unsafe fn create_surface_metal(
321 &self,
322 layer: *mut core::ffi::c_void,
323 ) -> Result<Arc<Surface>, CreateSurfaceError> {
324 profiling::scope!("Instance::create_surface_metal");
325
326 let instance = unsafe { self.as_hal::<hal::api::Metal>() }
327 .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Metal))?;
328
329 let layer = layer.cast();
330 let layer = unsafe { &*layer };
342 let raw_surface: Box<dyn hal::DynSurface> =
343 Box::new(instance.create_surface_from_layer(layer));
344
345 let surface = Arc::new(Surface {
346 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
347 surface_per_backend: core::iter::once((Backend::Metal, raw_surface)).collect(),
348 });
349
350 Ok(surface)
351 }
352
353 #[cfg(dx12)]
354 fn create_surface_dx12(
355 &self,
356 create_surface_func: impl FnOnce(&hal::dx12::Instance) -> hal::dx12::Surface,
357 ) -> Result<Arc<Surface>, CreateSurfaceError> {
358 let instance = unsafe { self.as_hal::<hal::api::Dx12>() }
359 .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?;
360 let surface: Box<dyn hal::DynSurface> = Box::new(create_surface_func(instance));
361
362 let surface = Arc::new(Surface {
363 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
364 surface_per_backend: core::iter::once((Backend::Dx12, surface)).collect(),
365 });
366
367 Ok(surface)
368 }
369
370 #[cfg(dx12)]
371 pub unsafe fn create_surface_from_visual(
375 &self,
376 visual: *mut core::ffi::c_void,
377 ) -> Result<Arc<Surface>, CreateSurfaceError> {
378 profiling::scope!("Instance::instance_create_surface_from_visual");
379 self.create_surface_dx12(|inst| unsafe { inst.create_surface_from_visual(visual) })
380 }
381
382 #[cfg(dx12)]
383 pub unsafe fn create_surface_from_surface_handle(
387 &self,
388 surface_handle: *mut core::ffi::c_void,
389 ) -> Result<Arc<Surface>, CreateSurfaceError> {
390 profiling::scope!("Instance::instance_create_surface_from_surface_handle");
391 self.create_surface_dx12(|inst| unsafe {
392 inst.create_surface_from_surface_handle(surface_handle)
393 })
394 }
395
396 #[cfg(dx12)]
397 pub unsafe fn create_surface_from_swap_chain_panel(
401 &self,
402 swap_chain_panel: *mut core::ffi::c_void,
403 ) -> Result<Arc<Surface>, CreateSurfaceError> {
404 profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
405 self.create_surface_dx12(|inst| unsafe {
406 inst.create_surface_from_swap_chain_panel(swap_chain_panel)
407 })
408 }
409
410 fn adapter_allowed(&self, raw: &hal::DynExposedAdapter) -> bool {
411 adapter_allowed(
412 self.flags,
413 &raw.info,
414 &raw.capabilities.limits,
415 &raw.capabilities.downlevel,
416 )
417 }
418
419 pub fn enumerate_adapters(
420 self: &Arc<Self>,
421 backends: Backends,
422 apply_limit_buckets: bool,
423 ) -> Vec<Arc<Adapter>> {
424 profiling::scope!("Instance::enumerate_adapters");
425 api_log!("Instance::enumerate_adapters");
426
427 let mut adapters = Vec::new();
428 for (_backend, instance) in self
429 .instance_per_backend
430 .iter()
431 .filter(|(backend, _)| backends.contains(Backends::from(*backend)))
432 {
433 profiling::scope!("enumerating", &*alloc::format!("{_backend:?}"));
436
437 let hal_adapters = unsafe { instance.enumerate_adapters(None) };
438
439 adapters.extend(
440 hal_adapters
441 .into_iter()
442 .map(|mut raw| {
443 self.adjust_limits_for_indirect_validation(&mut raw.capabilities.limits);
444 raw
445 })
446 .map(|mut raw| {
447 filter_features_and_limits(
448 self.flags,
449 &mut raw.features,
450 &mut raw.capabilities.limits,
451 );
452 raw
453 })
454 .filter(|raw| self.adapter_allowed(raw))
455 .filter_map(|raw| {
456 if apply_limit_buckets {
457 limits::apply_limit_buckets(raw)
458 } else {
459 Some(raw)
460 }
461 })
462 .map(|raw| {
463 let adapter = Adapter::new(raw, self.clone());
464 api_log_debug!("Adapter {:?}", adapter.raw.info);
465 adapter
466 }),
467 );
468 }
469 adapters
470 }
471
472 pub fn request_adapter(
473 self: &Arc<Self>,
474 desc: &wgt::RequestAdapterOptions<&Surface>,
475 backends: Backends,
476 ) -> Result<Arc<Adapter>, wgt::RequestAdapterError> {
477 profiling::scope!("Instance::request_adapter");
478 api_log!("Instance::request_adapter");
479
480 let mut adapters = Vec::new();
481 let mut incompatible_surface_backends = Backends::empty();
482 let mut no_fallback_backends = Backends::empty();
483 let mut no_adapter_backends = Backends::empty();
484
485 for &(backend, ref instance) in self
486 .instance_per_backend
487 .iter()
488 .filter(|&&(backend, _)| backends.contains(Backends::from(backend)))
489 {
490 let compatible_hal_surface = desc
491 .compatible_surface
492 .and_then(|surface| surface.raw(backend));
493
494 let mut backend_adapters =
495 unsafe { instance.enumerate_adapters(compatible_hal_surface) };
496 if backend_adapters.is_empty() {
497 log::debug!("enabled backend `{backend:?}` has no adapters");
498 no_adapter_backends |= Backends::from(backend);
499 continue;
501 }
502
503 if desc.force_fallback_adapter {
504 log::debug!("Filtering `{backend:?}` for `force_fallback_adapter`");
505 backend_adapters.retain(|exposed| {
506 let keep = exposed.info.device_type == wgt::DeviceType::Cpu;
507 if !keep {
508 log::debug!("* Eliminating adapter `{}`", exposed.info.name);
509 }
510 keep
511 });
512 if backend_adapters.is_empty() {
513 log::debug!("* Backend `{backend:?}` has no fallback adapters");
514 no_fallback_backends |= Backends::from(backend);
515 continue;
516 }
517 }
518
519 if let Some(surface) = desc.compatible_surface {
520 backend_adapters.retain(|exposed| {
521 let capabilities = surface.get_capabilities_with_raw(exposed);
522 if let Err(err) = capabilities {
523 log::debug!(
524 "Adapter {:?} not compatible with surface: {}",
525 exposed.info,
526 err
527 );
528 incompatible_surface_backends |= Backends::from(backend);
529 false
530 } else {
531 true
532 }
533 });
534 if backend_adapters.is_empty() {
535 incompatible_surface_backends |= Backends::from(backend);
536 continue;
537 }
538 }
539
540 let backend_adapters = backend_adapters
541 .into_iter()
542 .map(|mut raw| {
543 self.adjust_limits_for_indirect_validation(&mut raw.capabilities.limits);
544 raw
545 })
546 .map(|mut raw| {
547 filter_features_and_limits(
548 self.flags,
549 &mut raw.features,
550 &mut raw.capabilities.limits,
551 );
552 raw
553 })
554 .filter(|raw| self.adapter_allowed(raw));
555
556 if desc.apply_limit_buckets {
557 adapters.extend(backend_adapters.filter_map(limits::apply_limit_buckets));
558 } else {
559 adapters.extend(backend_adapters);
560 }
561 }
562
563 match desc.power_preference {
564 PowerPreference::LowPower => {
565 sort(&mut adapters, true);
566 }
567 PowerPreference::HighPerformance => {
568 sort(&mut adapters, false);
569 }
570 PowerPreference::None => {}
571 };
572
573 fn sort(adapters: &mut [hal::DynExposedAdapter], prefer_integrated_gpu: bool) {
574 adapters
575 .sort_by_key(|adapter| get_order(adapter.info.device_type, prefer_integrated_gpu));
576 }
577
578 fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 {
579 match device_type {
587 wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2,
588 wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1,
589 wgt::DeviceType::DiscreteGpu => 1,
590 wgt::DeviceType::IntegratedGpu => 2,
591 wgt::DeviceType::Other => 3,
592 wgt::DeviceType::VirtualGpu => 4,
593 wgt::DeviceType::Cpu => 5,
594 }
595 }
596
597 if adapters.is_empty() {
600 log::debug!("Request adapter didn't find compatible adapters.");
601 } else {
602 log::debug!(
603 "Found {} compatible adapters. Sorted by preference:",
604 adapters.len()
605 );
606 for adapter in &adapters {
607 log::debug!("* {:?}", adapter.info);
608 }
609 }
610
611 if let Some(adapter) = adapters.into_iter().next() {
612 api_log_debug!("Request adapter result {:?}", adapter.info);
613 let adapter = Adapter::new(adapter, self.clone());
614 Ok(adapter)
615 } else {
616 Err(wgt::RequestAdapterError::NotFound {
617 supported_backends: self.supported_backends,
618 requested_backends: self.requested_backends,
619 active_backends: self.active_backends(),
620 no_fallback_backends,
621 no_adapter_backends,
622 incompatible_surface_backends,
623 })
624 }
625 }
626
627 fn adjust_limits_for_indirect_validation(&self, limits: &mut wgt::Limits) {
630 if self.flags.contains(InstanceFlags::VALIDATION_INDIRECT_CALL) {
633 limits.max_buffer_size = limits.max_buffer_size.min(u32::MAX as u64);
634 limits.max_uniform_buffer_binding_size =
635 limits.max_uniform_buffer_binding_size.min(u32::MAX as u64);
636 limits.max_storage_buffer_binding_size = limits
637 .max_storage_buffer_binding_size
638 .min(u32::MAX as u64 & !(wgt::STORAGE_BINDING_SIZE_ALIGNMENT as u64 - 1));
639 }
640 }
641
642 fn active_backends(&self) -> Backends {
643 self.instance_per_backend
644 .iter()
645 .map(|&(backend, _)| Backends::from(backend))
646 .collect()
647 }
648
649 pub unsafe fn create_adapter_from_hal(
663 self: &Arc<Self>,
664 hal_adapter: hal::DynExposedAdapter,
665 ) -> Arc<Adapter> {
666 profiling::scope!("Instance::create_adapter_from_hal");
667
668 let adapter = Adapter::new(hal_adapter, self.clone());
669
670 resource_log!("Created Adapter {:?}", Arc::as_ptr(&adapter));
671 adapter
672 }
673
674 pub fn poll_all_devices(&self, force_wait: bool) -> Result<bool, WaitIdleError> {
681 api_log!("poll_all_devices");
682 let mut closures = UserClosures::default();
683 let all_queue_empty = self.devices.poll_all_devices(force_wait, &mut closures)?;
684
685 closures.fire();
686
687 Ok(all_queue_empty)
688 }
689}
690
691pub struct InstanceBuilder {
692 instance: Instance,
693 instance_desc: wgt::InstanceDescriptor,
694 telemetry: Option<hal::Telemetry>,
695}
696
697impl InstanceBuilder {
698 pub fn new(
699 name: &str,
700 mut desc: wgt::InstanceDescriptor,
701 telemetry: Option<hal::Telemetry>,
702 ) -> Self {
703 Self {
704 instance: Instance {
705 _name: name.to_owned(),
706 instance_per_backend: Vec::new(),
707 requested_backends: desc.backends,
708 supported_backends: Backends::empty(),
709 flags: desc.flags,
710 display: desc.display.take(),
714 devices: InstanceDevices::new(),
715 },
716 instance_desc: desc,
717 telemetry,
718 }
719 }
720
721 pub fn try_add_hal<A: hal::Api>(&mut self, api: A) {
722 use hal::Instance as _;
723 self.try_add_custom_hal(api, |hal_desc| unsafe { A::Instance::init(hal_desc) });
724 }
725
726 pub fn try_add_custom_hal<A: hal::Api>(
727 &mut self,
728 _: A,
729 f: impl FnOnce(&hal::InstanceDescriptor) -> Result<A::Instance, hal::InstanceError>,
730 ) {
731 if self.instance.supported_backends.contains(A::VARIANT.into()) {
732 return;
733 }
734
735 self.instance.supported_backends |= A::VARIANT.into();
738
739 if !self.instance_desc.backends.contains(A::VARIANT.into()) {
740 log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
741 return;
742 }
743
744 assert!(self.instance_desc.display.is_none());
746
747 let hal_desc = hal::InstanceDescriptor {
748 name: "wgpu",
749 flags: self.instance.flags,
750 memory_budget_thresholds: self.instance_desc.memory_budget_thresholds,
751 backend_options: self.instance_desc.backend_options.clone(),
752 telemetry: self.telemetry,
753 display: self.instance.display.as_ref().map(|hdh| {
756 hdh.display_handle()
757 .expect("Implementation did not provide a DisplayHandle")
758 }),
759 };
760
761 match f(&hal_desc) {
762 Ok(instance) => {
763 log::debug!("Instance::new: created {:?} backend", A::VARIANT);
764 self.instance
765 .instance_per_backend
766 .push((A::VARIANT, Box::new(instance)));
767 }
768 Err(err) => {
769 log::debug!(
770 "Instance::new: failed to create {:?} backend: {:?}",
771 A::VARIANT,
772 err
773 );
774 }
775 }
776 }
777
778 pub fn build(self) -> Arc<Instance> {
779 Arc::new(self.instance)
780 }
781}
782
783pub struct Surface {
784 pub(crate) presentation: Mutex<Option<Presentation>>,
785 pub surface_per_backend: HashMap<Backend, Box<dyn hal::DynSurface>>,
786}
787
788impl ResourceType for Surface {
789 const TYPE: &'static str = "Surface";
790}
791impl crate::storage::StorageItem for Surface {
792 type Marker = markers::Surface;
793}
794
795impl Surface {
796 pub fn get_capabilities(
797 &self,
798 adapter: &Adapter,
799 ) -> Result<wgt::SurfaceCapabilities, GetSurfaceSupportError> {
800 profiling::scope!("Surface::get_capabilities");
801 let mut hal_caps = self.get_hal_capabilities(adapter)?;
802
803 hal_caps
804 .formats
805 .sort_by_key(|fc| !fc.format.has_srgb_suffix());
806
807 let usages = crate::conv::map_texture_usage_from_hal(hal_caps.usage);
808
809 Ok(wgt::SurfaceCapabilities {
815 formats: hal_caps
816 .formats
817 .iter()
818 .filter(|fc| {
819 crate::device::surface_config::resolve_auto_color_space(
820 fc.format,
821 fc.color_spaces,
822 )
823 .is_some()
824 })
825 .map(|fc| fc.format)
826 .collect(),
827 format_capabilities: hal_caps.formats,
828 present_modes: hal_caps.present_modes,
829 alpha_modes: hal_caps.composite_alpha_modes,
830 usages,
831 })
832 }
833
834 pub fn get_hal_capabilities(
835 &self,
836 adapter: &Adapter,
837 ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
838 self.get_capabilities_with_raw(&adapter.raw)
839 }
840
841 pub fn get_capabilities_with_raw(
842 &self,
843 adapter: &hal::DynExposedAdapter,
844 ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
845 let backend = adapter.backend();
846 let suf = self
847 .raw(backend)
848 .ok_or(GetSurfaceSupportError::NotSupportedByBackend(backend))?;
849 profiling::scope!("surface_capabilities");
850 let caps = unsafe { adapter.adapter.surface_capabilities(suf) }
851 .ok_or(GetSurfaceSupportError::FailedToRetrieveSurfaceCapabilitiesForAdapter)?;
852 Ok(caps)
853 }
854
855 pub fn display_hdr_info(&self, adapter: &Adapter) -> wgt::DisplayHdrInfo {
861 profiling::scope!("Surface::display_hdr_info");
862 self.display_hdr_info_with_raw(&adapter.raw)
863 }
864
865 pub fn display_hdr_info_with_raw(
866 &self,
867 adapter: &hal::DynExposedAdapter,
868 ) -> wgt::DisplayHdrInfo {
869 let backend = adapter.backend();
870 let Some(suf) = self.raw(backend) else {
871 return wgt::DisplayHdrInfo::default();
872 };
873 profiling::scope!("surface_display_hdr_info");
874 unsafe { adapter.adapter.surface_display_hdr_info(suf) }.unwrap_or_default()
875 }
876
877 pub fn raw(&self, backend: Backend) -> Option<&dyn hal::DynSurface> {
878 self.surface_per_backend
879 .get(&backend)
880 .map(|surface| surface.as_ref())
881 }
882
883 fn unconfigure_inner<'a>(
884 &self,
885 presentation: &mut MutexGuard<'a, Option<Presentation>>,
886 ) -> UserClosures {
887 let mut result = UserClosures::default();
888 if let Some(mut present) = presentation.take() {
889 if let Some(texture) = present.acquired_texture.take() {
890 texture.destroy();
891 }
892
893 let user_callbacks;
894 {
895 let snatch_guard = present.device.snatchable_lock.read();
897
898 let result;
899 (user_callbacks, result) = present
900 .device
901 .maintain(wgt::PollType::wait_indefinitely(), snatch_guard);
902 match result {
903 Ok(_) => {}
904 Err(WaitIdleError::Device(_)) => {
905 }
907 Err(WaitIdleError::Timeout) if cfg!(target_family = "wasm") => {
908 }
912 Err(WaitIdleError::Timeout) => {
913 unreachable!("wait_indefinitely() should never timeout")
914 }
915 Err(WaitIdleError::WrongSubmissionIndex(_, _)) => {
916 unreachable!("no submission index was provided")
917 }
918 }
919 }
920 result.extend(user_callbacks);
921
922 for (&backend, surface) in &self.surface_per_backend {
923 if backend == present.device.backend() {
924 unsafe { surface.unconfigure(present.device.raw()) };
925 }
926 }
927 }
928 result
929 }
930
931 pub fn unconfigure(self: &Arc<Self>) {
932 profiling::scope!("Surface::unconfigure");
933 let user_callbacks;
934 {
935 let mut presentation = self.presentation.lock();
936 user_callbacks = self.unconfigure_inner(&mut presentation);
937 }
938 user_callbacks.fire();
939 }
940
941 pub fn configure(
942 self: &Arc<Self>,
943 device: &Arc<Device>,
944 config: &wgt::SurfaceConfiguration<Vec<wgt::TextureFormat>>,
945 ) -> Result<(), ConfigureSurfaceError> {
946 use ConfigureSurfaceError as E;
947 profiling::scope!("Surface::configure");
948
949 #[cfg(feature = "trace")]
950 if let Some(ref mut trace) = *device.trace.lock() {
951 use crate::device::trace::{Action, IntoTrace};
952
953 trace.add(Action::ConfigureSurface(self.to_trace(), config.clone()));
954 }
955
956 log::debug!("configuring surface with {config:?}");
957
958 let caps = self
959 .get_hal_capabilities(&device.adapter)
960 .map_err(|_| E::UnsupportedQueueFamily)?;
961
962 let mut hal_view_formats = Vec::new();
963 for format in config.view_formats.iter() {
964 if *format == config.format {
965 continue;
966 }
967 if !caps.formats.iter().any(|fc| fc.format == config.format) {
968 return Err(E::UnsupportedFormat {
969 requested: config.format,
970 available: caps.texture_formats().collect(),
971 });
972 }
973 if config.format.remove_srgb_suffix() != format.remove_srgb_suffix() {
974 return Err(E::InvalidViewFormat(*format, config.format));
975 }
976 hal_view_formats.push(*format);
977 }
978
979 if !hal_view_formats.is_empty() {
980 device.require_downlevel_flags(wgt::DownlevelFlags::SURFACE_VIEW_FORMATS)?;
981 }
982
983 let maximum_frame_latency = config.desired_maximum_frame_latency.clamp(
984 *caps.maximum_frame_latency.start(),
985 *caps.maximum_frame_latency.end(),
986 );
987 let mut hal_config = hal::SurfaceConfiguration {
988 maximum_frame_latency,
989 present_mode: config.present_mode,
990 composite_alpha_mode: config.alpha_mode,
991 format: config.format,
992 color_space: config.color_space,
993 extent: wgt::Extent3d {
994 width: config.width,
995 height: config.height,
996 depth_or_array_layers: 1,
997 },
998 usage: crate::conv::map_texture_usage(
999 config.usage,
1000 hal::FormatAspects::COLOR,
1001 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY
1002 | wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY
1003 | wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1004 ),
1005 view_formats: hal_view_formats,
1006 };
1007
1008 crate::device::surface_config::validate_surface_configuration(
1009 &mut hal_config,
1010 &caps,
1011 device.limits.max_texture_dimension_2d,
1012 )?;
1013
1014 device.check_is_valid()?;
1015
1016 let user_callbacks;
1017 let result = {
1018 let mut presentation = self.presentation.lock();
1021
1022 user_callbacks = self.unconfigure_inner(&mut presentation);
1023
1024 let surface_raw = self.raw(device.backend()).unwrap();
1025 match unsafe { surface_raw.configure(device.raw(), &hal_config) } {
1026 Ok(()) => {
1027 *presentation = Some(Presentation {
1028 device: Arc::clone(device),
1029 config: config.clone(),
1030 acquired_texture: None,
1031 });
1032 Ok(())
1033 }
1034 Err(error) => Err(match error {
1035 hal::SurfaceError::Outdated
1036 | hal::SurfaceError::Lost
1037 | hal::SurfaceError::Occluded
1038 | hal::SurfaceError::Timeout => E::InvalidSurface,
1039 hal::SurfaceError::Device(error) => E::Device(device.handle_hal_error(error)),
1040 hal::SurfaceError::Other(message) => {
1041 log::error!("surface configuration failed: {message}");
1042 E::InvalidSurface
1043 }
1044 }),
1045 }
1046 };
1047 user_callbacks.fire();
1048
1049 result
1050 }
1051}
1052
1053impl Drop for Surface {
1054 #[allow(trivial_casts)]
1055 fn drop(&mut self) {
1056 profiling::scope!("Surface::drop");
1057
1058 api_log!("Surface::drop {:?}", self as *const _);
1059 let user_closures;
1060 {
1061 let mut presentation = self.presentation.lock();
1062 user_closures = self.unconfigure_inner(&mut presentation);
1063 }
1064 user_closures.fire();
1065 }
1066}
1067
1068pub struct Adapter {
1069 pub(crate) raw: hal::DynExposedAdapter,
1070 pub(crate) instance: Arc<Instance>,
1071 consumed: AtomicBool,
1072}
1073
1074impl Adapter {
1075 pub(crate) fn new(raw: hal::DynExposedAdapter, instance: Arc<Instance>) -> Arc<Self> {
1076 Arc::new(Self {
1077 raw,
1078 instance,
1079 consumed: AtomicBool::new(false),
1080 })
1081 }
1082
1083 pub fn backend(&self) -> Backend {
1085 self.raw.backend()
1086 }
1087
1088 pub fn is_surface_supported(&self, surface: &Surface) -> bool {
1089 surface.get_hal_capabilities(self).is_ok()
1094 }
1095
1096 pub fn get_info(&self) -> wgt::AdapterInfo {
1097 self.raw.info.clone()
1098 }
1099
1100 pub fn features(&self) -> wgt::Features {
1101 self.raw.features
1102 }
1103
1104 pub fn limits(&self) -> wgt::Limits {
1105 self.raw.capabilities.limits.clone()
1106 }
1107
1108 pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
1109 self.raw.capabilities.downlevel.clone()
1110 }
1111
1112 pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1113 unsafe { self.raw.adapter.get_presentation_timestamp() }
1114 }
1115
1116 pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1117 self.raw.capabilities.cooperative_matrix_properties.clone()
1118 }
1119
1120 pub fn get_texture_format_features(
1121 &self,
1122 format: wgt::TextureFormat,
1123 ) -> wgt::TextureFormatFeatures {
1124 use hal::TextureFormatCapabilities as Tfc;
1125
1126 let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
1127 let mut allowed_usages = wgt::TextureUsages::empty();
1128
1129 allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
1130 allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
1131 allowed_usages.set(
1132 wgt::TextureUsages::TEXTURE_BINDING,
1133 caps.contains(Tfc::SAMPLED),
1134 );
1135 allowed_usages.set(
1136 wgt::TextureUsages::STORAGE_BINDING,
1137 caps.intersects(
1138 Tfc::STORAGE_WRITE_ONLY
1139 | Tfc::STORAGE_READ_ONLY
1140 | Tfc::STORAGE_READ_WRITE
1141 | Tfc::STORAGE_ATOMIC,
1142 ),
1143 );
1144 allowed_usages.set(
1145 wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT_ATTACHMENT,
1146 caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
1147 );
1148 allowed_usages.set(
1149 wgt::TextureUsages::STORAGE_ATOMIC,
1150 caps.contains(Tfc::STORAGE_ATOMIC),
1151 );
1152
1153 let mut flags = wgt::TextureFormatFeatureFlags::empty();
1154 flags.set(
1155 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
1156 caps.contains(Tfc::STORAGE_READ_ONLY),
1157 );
1158 flags.set(
1159 wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
1160 caps.contains(Tfc::STORAGE_WRITE_ONLY),
1161 );
1162 flags.set(
1163 wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1164 caps.contains(Tfc::STORAGE_READ_WRITE),
1165 );
1166
1167 flags.set(
1168 wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
1169 caps.contains(Tfc::STORAGE_ATOMIC),
1170 );
1171
1172 flags.set(
1173 wgt::TextureFormatFeatureFlags::FILTERABLE,
1174 caps.contains(Tfc::SAMPLED_LINEAR),
1175 );
1176
1177 flags.set(
1178 wgt::TextureFormatFeatureFlags::BLENDABLE,
1179 caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
1180 );
1181
1182 flags.set(
1183 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
1184 caps.contains(Tfc::MULTISAMPLE_X2),
1185 );
1186 flags.set(
1187 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
1188 caps.contains(Tfc::MULTISAMPLE_X4),
1189 );
1190 flags.set(
1191 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
1192 caps.contains(Tfc::MULTISAMPLE_X8),
1193 );
1194 flags.set(
1195 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1196 caps.contains(Tfc::MULTISAMPLE_X16),
1197 );
1198
1199 flags.set(
1200 wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
1201 caps.contains(Tfc::MULTISAMPLE_RESOLVE),
1202 );
1203
1204 wgt::TextureFormatFeatures {
1205 allowed_usages,
1206 flags,
1207 }
1208 }
1209
1210 pub unsafe fn create_device_and_queue_from_hal(
1215 self: &Arc<Self>,
1216 hal_device: hal::DynOpenDevice,
1217 desc: &DeviceDescriptor,
1218 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1219 profiling::scope!("Adapter::create_device_and_queue_from_hal");
1220 api_log!("Adapter::create_device_and_queue_from_hal");
1221
1222 if self
1223 .instance
1224 .flags
1225 .contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
1226 && self.consumed.load(Ordering::SeqCst)
1227 {
1228 return Err(RequestDeviceError::AdapterConsumed);
1229 }
1230
1231 let default_queue_desc = desc.default_queue.clone();
1232
1233 let device = Device::new(hal_device.device, self, desc, self.instance.flags)?;
1234 let device = Arc::new(device);
1235
1236 let queue = Queue::new(
1237 device.clone(),
1238 hal_device.queue,
1239 default_queue_desc,
1240 self.instance.flags,
1241 )?;
1242 let queue = Arc::new(queue);
1243
1244 device.set_queue(&queue);
1245 device.late_init_resources_with_queue()?;
1246
1247 resource_log!("Created Device {:?}", Arc::as_ptr(&device));
1248 resource_log!("Created Queue {:?}", Arc::as_ptr(&queue));
1249
1250 self.instance.devices.push(&device);
1251
1252 self.consumed.store(true, Ordering::SeqCst);
1253
1254 Ok((device, queue))
1255 }
1256
1257 pub fn validate_device_descriptor(
1267 &self,
1268 desc: &mut DeviceDescriptor,
1269 ) -> Result<(), RequestDeviceError> {
1270 filter_features_and_limits(
1271 self.instance.flags,
1272 &mut desc.required_features,
1273 &mut desc.required_limits,
1274 );
1275
1276 if !self.raw.features.contains(desc.required_features) {
1278 return Err(RequestDeviceError::UnsupportedFeature(
1279 desc.required_features - self.raw.features,
1280 ));
1281 }
1282
1283 if desc
1285 .required_features
1286 .intersects(wgt::Features::all_experimental_mask())
1287 && !desc.experimental_features.is_enabled()
1288 {
1289 return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
1290 desc.required_features
1291 .intersection(wgt::Features::all_experimental_mask()),
1292 ));
1293 }
1294
1295 let caps = &self.raw.capabilities;
1296 if Backends::PRIMARY.contains(Backends::from(self.backend()))
1297 && !caps.downlevel.is_webgpu_compliant()
1298 {
1299 let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
1300 log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
1301 log::warn!("{:#?}", caps.downlevel);
1302 }
1303
1304 if desc
1306 .required_features
1307 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1308 && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
1309 {
1310 log::warn!(
1311 "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
1312 This is a massive performance footgun and likely not what you wanted"
1313 );
1314 }
1315
1316 if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
1317 return Err(RequestDeviceError::LimitsExceeded(failed));
1318 }
1319
1320 normalize_max_resource_per_shader_stage_limits(&mut desc.required_limits);
1321
1322 Ok(())
1323 }
1324
1325 pub fn request_device(
1326 self: &Arc<Self>,
1327 desc: &DeviceDescriptor,
1328 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1329 profiling::scope!("Adapter::request_device");
1330 api_log!("Adapter::request_device");
1331
1332 let mut desc = desc.clone();
1333 self.validate_device_descriptor(&mut desc)?;
1334
1335 let open = unsafe {
1336 self.raw.adapter.open(
1337 desc.required_features,
1338 &desc.required_limits,
1339 &desc.memory_hints,
1340 )
1341 }
1342 .map_err(DeviceError::from_hal)?;
1343
1344 unsafe { self.create_device_and_queue_from_hal(open, &desc) }
1345 }
1346}
1347
1348impl Drop for Adapter {
1349 #[allow(trivial_casts)]
1350 fn drop(&mut self) {
1351 profiling::scope!("Adapter::drop");
1352 api_log!("Adapter::drop {:?}", self as *const _);
1353 }
1354}
1355
1356crate::impl_resource_type!(Adapter);
1357crate::impl_storage_item!(Adapter);
1358
1359#[derive(Clone, Debug, Error)]
1360#[non_exhaustive]
1361pub enum GetSurfaceSupportError {
1362 #[error("Surface is not supported for the specified backend {0}")]
1363 NotSupportedByBackend(Backend),
1364 #[error("Failed to retrieve surface capabilities for the specified adapter.")]
1365 FailedToRetrieveSurfaceCapabilitiesForAdapter,
1366}
1367
1368#[derive(Clone, Debug, Error)]
1369#[non_exhaustive]
1371pub enum RequestDeviceError {
1372 #[error(transparent)]
1373 Device(#[from] DeviceError),
1374 #[error(transparent)]
1375 LimitsExceeded(#[from] FailedLimit),
1376 #[error("Failed to initialize Timestamp Normalizer")]
1377 TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
1378 #[error("Unsupported features were requested: {0}")]
1379 UnsupportedFeature(wgt::Features),
1380 #[error(
1381 "Some experimental features, {0}, were requested, but experimental features are not enabled"
1382 )]
1383 ExperimentalFeaturesNotEnabled(wgt::Features),
1384 #[error("The adapter has already been consumed.")]
1385 AdapterConsumed,
1386}
1387
1388#[derive(Clone, Debug, Error)]
1389#[non_exhaustive]
1390pub enum CreateSurfaceError {
1391 #[error("The backend {0} was not enabled on the instance.")]
1392 BackendNotEnabled(Backend),
1393 #[error("Failed to create surface for any enabled backend: {0:?}")]
1394 FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
1395 #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
1396 MismatchingDisplayHandle,
1397 #[error(
1398 "No `DisplayHandle` is available to create this surface with. When creating a surface with `create_surface()` \
1399 you must specify a display handle in `InstanceDescriptor::display`. \
1400 Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
1401 you must use `create_surface_unsafe()`."
1402 )]
1403 MissingDisplayHandle,
1404}
1405
1406fn adapter_allowed(
1411 flags: InstanceFlags,
1412 info: &impl fmt::Debug,
1413 limits: &wgt::Limits,
1414 downlevel: &wgt::DownlevelCapabilities,
1415) -> bool {
1416 let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1422 if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1423 log::error!(
1424 "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1425 info,
1426 min_uniform_buffer_offset_alignment
1427 );
1428 return false;
1429 }
1430 let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1431 if !min_storage_buffer_offset_alignment.is_power_of_two() {
1432 log::error!(
1433 "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1434 info,
1435 min_storage_buffer_offset_alignment
1436 );
1437 return false;
1438 }
1439
1440 if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1442 return true;
1443 }
1444
1445 let mut min_limits = wgt::Limits::defaults();
1447 min_limits.zero_native_only();
1448 let failed_limits = check_limits(&min_limits, limits);
1449 if !failed_limits.is_empty() {
1450 log::debug!(
1451 "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1452 info,
1453 failed_limits
1454 );
1455 return false;
1456 }
1457
1458 if !downlevel.is_webgpu_compliant() {
1459 let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1460 log::debug!(
1461 "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1462 info,
1463 missing_flags
1464 );
1465 return false;
1466 }
1467
1468 true
1469}
1470
1471fn filter_features_and_limits(
1472 flags: InstanceFlags,
1473 features: &mut wgt::Features,
1474 limits: &mut wgt::Limits,
1475) {
1476 if flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1477 *features &= wgt::Features::all_webgpu_mask() | limits::EXEMPT_FEATURES;
1478 limits.zero_native_only();
1479 }
1480}
1481
1482fn normalize_max_resource_per_shader_stage_limits(limits: &mut wgt::Limits) {
1483 limits.max_storage_buffers_per_shader_stage = [
1490 limits.max_storage_buffers_per_shader_stage,
1491 limits.max_storage_buffers_in_vertex_stage,
1492 limits.max_storage_buffers_in_fragment_stage,
1493 ]
1494 .into_iter()
1495 .max()
1496 .unwrap();
1497
1498 limits.max_storage_textures_per_shader_stage = [
1503 limits.max_storage_textures_per_shader_stage,
1504 limits.max_storage_textures_in_vertex_stage,
1505 limits.max_storage_textures_in_fragment_stage,
1506 ]
1507 .into_iter()
1508 .max()
1509 .unwrap();
1510
1511 limits.max_storage_buffers_in_vertex_stage = limits.max_storage_buffers_per_shader_stage;
1520 limits.max_storage_buffers_in_fragment_stage = limits.max_storage_buffers_per_shader_stage;
1521
1522 limits.max_storage_textures_in_vertex_stage = limits.max_storage_textures_per_shader_stage;
1526 limits.max_storage_textures_in_fragment_stage = limits.max_storage_textures_per_shader_stage;
1527}
1528
1529#[cfg(test)]
1530mod tests {
1531 use super::*;
1532
1533 fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1534 wgt::DownlevelCapabilities {
1535 flags: wgt::DownlevelFlags::compliant(),
1536 ..Default::default()
1537 }
1538 }
1539
1540 #[test]
1541 fn non_power_of_two_uniform_alignment_always_rejected() {
1542 let limits = wgt::Limits {
1543 min_uniform_buffer_offset_alignment: 3,
1544 ..wgt::Limits::defaults()
1545 };
1546 assert!(!adapter_allowed(
1547 InstanceFlags::empty(),
1548 &"",
1549 &limits,
1550 &compliant_downlevel()
1551 ));
1552 assert!(!adapter_allowed(
1553 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1554 &"",
1555 &limits,
1556 &compliant_downlevel()
1557 ));
1558 }
1559
1560 #[test]
1561 fn non_power_of_two_storage_alignment_always_rejected() {
1562 let limits = wgt::Limits {
1563 min_storage_buffer_offset_alignment: 96,
1564 ..wgt::Limits::defaults()
1565 };
1566 assert!(!adapter_allowed(
1567 InstanceFlags::empty(),
1568 &"",
1569 &limits,
1570 &compliant_downlevel()
1571 ));
1572 assert!(!adapter_allowed(
1573 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1574 &"",
1575 &limits,
1576 &compliant_downlevel()
1577 ));
1578 }
1579
1580 #[test]
1581 fn low_limits_allowed_without_strict_compliance() {
1582 let limits = wgt::Limits {
1583 max_texture_dimension_1d: 1,
1584 ..wgt::Limits::defaults()
1585 };
1586 assert!(adapter_allowed(
1587 InstanceFlags::empty(),
1588 &"",
1589 &limits,
1590 &wgt::DownlevelCapabilities::default()
1591 ));
1592 }
1593
1594 #[test]
1595 fn low_limits_rejected_with_strict_compliance() {
1596 let limits = wgt::Limits {
1597 max_texture_dimension_1d: 1,
1598 ..wgt::Limits::defaults()
1599 };
1600 assert!(!adapter_allowed(
1601 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1602 &"",
1603 &limits,
1604 &compliant_downlevel()
1605 ));
1606 }
1607
1608 #[test]
1609 fn missing_downlevel_flags_rejected_with_strict_compliance() {
1610 let downlevel = wgt::DownlevelCapabilities {
1611 flags: wgt::DownlevelFlags::empty(),
1612 ..Default::default()
1613 };
1614 assert!(!adapter_allowed(
1615 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1616 &"",
1617 &wgt::Limits::defaults(),
1618 &downlevel
1619 ));
1620 }
1621
1622 #[test]
1623 fn missing_surface_view_formats_allowed_with_strict_compliance() {
1624 let downlevel = wgt::DownlevelCapabilities {
1625 flags: wgt::DownlevelFlags::all() - wgt::DownlevelFlags::SURFACE_VIEW_FORMATS,
1626 ..Default::default()
1627 };
1628 assert!(adapter_allowed(
1629 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1630 &"",
1631 &wgt::Limits::defaults(),
1632 &downlevel
1633 ));
1634 }
1635
1636 #[test]
1637 fn fully_compliant_adapter_always_allowed() {
1638 assert!(adapter_allowed(
1639 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1640 &"",
1641 &wgt::Limits::defaults(),
1642 &compliant_downlevel()
1643 ));
1644 }
1645
1646 mod storage_resource_limits {
1647 use super::*;
1648
1649 #[track_caller]
1650 fn assert_normalized_eq(non_normalized: &wgt::Limits, expected: &wgt::Limits) {
1651 let mut normalized = non_normalized.clone();
1652 normalize_max_resource_per_shader_stage_limits(&mut normalized);
1653 assert_eq!(&normalized, expected);
1654 }
1655
1656 #[test]
1657 fn normalization_is_idempotent() {
1658 let original = wgt::Limits {
1659 max_storage_buffers_in_vertex_stage: 16,
1660 max_storage_textures_in_vertex_stage: 9,
1661 ..wgt::Limits::defaults()
1662 };
1663
1664 let mut first_normalization = original.clone();
1665 normalize_max_resource_per_shader_stage_limits(&mut first_normalization);
1666 assert_ne!(original, first_normalization);
1667
1668 let mut second_normalization = first_normalization.clone();
1669 normalize_max_resource_per_shader_stage_limits(&mut second_normalization);
1670 assert_eq!(first_normalization, second_normalization);
1671 }
1672
1673 #[test]
1674 fn limits_presets_already_normalized() {
1675 [
1676 wgt::Limits::defaults(),
1677 wgt::Limits::downlevel_defaults(),
1678 wgt::Limits::downlevel_webgl2_defaults(),
1679 wgt::Limits::unlimited(),
1680 ]
1681 .iter()
1682 .for_each(|l| assert_normalized_eq(l, l))
1683 }
1684
1685 #[test]
1686 fn in_stage_raises_per_shader_stage() {
1687 assert_normalized_eq(
1688 &wgt::Limits {
1689 max_storage_buffers_per_shader_stage: 8,
1690 max_storage_buffers_in_vertex_stage: 16,
1691 max_storage_buffers_in_fragment_stage: 16,
1692 ..wgt::Limits::defaults()
1693 },
1694 &wgt::Limits {
1695 max_storage_buffers_per_shader_stage: 16,
1696 max_storage_buffers_in_vertex_stage: 16,
1697 max_storage_buffers_in_fragment_stage: 16,
1698 ..wgt::Limits::defaults()
1699 },
1700 );
1701
1702 assert_normalized_eq(
1703 &wgt::Limits {
1704 max_storage_textures_per_shader_stage: 8,
1705 max_storage_textures_in_vertex_stage: 9,
1706 max_storage_textures_in_fragment_stage: 9,
1707 ..wgt::Limits::defaults()
1708 },
1709 &wgt::Limits {
1710 max_storage_textures_per_shader_stage: 9,
1711 max_storage_textures_in_vertex_stage: 9,
1712 max_storage_textures_in_fragment_stage: 9,
1713 ..wgt::Limits::defaults()
1714 },
1715 );
1716 }
1717
1718 #[test]
1719 fn per_shader_stage_raises_in_stage() {
1720 assert_normalized_eq(
1721 &wgt::Limits {
1722 max_storage_buffers_per_shader_stage: 16,
1723 max_storage_buffers_in_vertex_stage: 4,
1724 max_storage_buffers_in_fragment_stage: 4,
1725 max_storage_textures_per_shader_stage: 8,
1726 max_storage_textures_in_vertex_stage: 4,
1727 max_storage_textures_in_fragment_stage: 4,
1728 ..wgt::Limits::defaults()
1729 },
1730 &wgt::Limits {
1731 max_storage_buffers_per_shader_stage: 16,
1732 max_storage_buffers_in_vertex_stage: 16,
1733 max_storage_buffers_in_fragment_stage: 16,
1734 max_storage_textures_per_shader_stage: 8,
1735 max_storage_textures_in_vertex_stage: 8,
1736 max_storage_textures_in_fragment_stage: 8,
1737 ..wgt::Limits::defaults()
1738 },
1739 );
1740 }
1741
1742 #[test]
1743 fn lowering_per_shader_stage_noop() {
1744 assert_normalized_eq(
1745 &wgt::Limits {
1746 max_storage_buffers_per_shader_stage: 1,
1747 max_storage_textures_per_shader_stage: 1,
1748 ..wgt::Limits::defaults()
1749 },
1750 &wgt::Limits::defaults(),
1751 );
1752 }
1753 }
1754
1755 #[test]
1756 fn downlevel_default_limits_less_than_default_limits() {
1757 let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
1758 assert!(
1759 res.is_empty(),
1760 "Downlevel limits are greater than default limits",
1761 )
1762 }
1763}