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 {
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 Err(error) => {
1028 return Err(match error {
1029 hal::SurfaceError::Outdated
1030 | hal::SurfaceError::Lost
1031 | hal::SurfaceError::Occluded
1032 | hal::SurfaceError::Timeout => E::InvalidSurface,
1033 hal::SurfaceError::Device(error) => {
1034 E::Device(device.handle_hal_error(error))
1035 }
1036 hal::SurfaceError::Other(message) => {
1037 log::error!("surface configuration failed: {message}");
1038 E::InvalidSurface
1039 }
1040 });
1041 }
1042 }
1043
1044 *presentation = Some(Presentation {
1045 device: Arc::clone(device),
1046 config: config.clone(),
1047 acquired_texture: None,
1048 });
1049 }
1050 user_callbacks.fire();
1051
1052 Ok(())
1053 }
1054}
1055
1056impl Drop for Surface {
1057 #[allow(trivial_casts)]
1058 fn drop(&mut self) {
1059 profiling::scope!("Surface::drop");
1060
1061 api_log!("Surface::drop {:?}", self as *const _);
1062 let user_closures;
1063 {
1064 let mut presentation = self.presentation.lock();
1065 user_closures = self.unconfigure_inner(&mut presentation);
1066 }
1067 user_closures.fire();
1068 }
1069}
1070
1071pub struct Adapter {
1072 pub(crate) raw: hal::DynExposedAdapter,
1073 pub(crate) instance: Arc<Instance>,
1074 consumed: AtomicBool,
1075}
1076
1077impl Adapter {
1078 pub(crate) fn new(raw: hal::DynExposedAdapter, instance: Arc<Instance>) -> Arc<Self> {
1079 Arc::new(Self {
1080 raw,
1081 instance,
1082 consumed: AtomicBool::new(false),
1083 })
1084 }
1085
1086 pub fn backend(&self) -> Backend {
1088 self.raw.backend()
1089 }
1090
1091 pub fn is_surface_supported(&self, surface: &Surface) -> bool {
1092 surface.get_hal_capabilities(self).is_ok()
1097 }
1098
1099 pub fn get_info(&self) -> wgt::AdapterInfo {
1100 self.raw.info.clone()
1101 }
1102
1103 pub fn features(&self) -> wgt::Features {
1104 self.raw.features
1105 }
1106
1107 pub fn limits(&self) -> wgt::Limits {
1108 self.raw.capabilities.limits.clone()
1109 }
1110
1111 pub fn downlevel_capabilities(&self) -> wgt::DownlevelCapabilities {
1112 self.raw.capabilities.downlevel.clone()
1113 }
1114
1115 pub fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
1116 unsafe { self.raw.adapter.get_presentation_timestamp() }
1117 }
1118
1119 pub fn cooperative_matrix_properties(&self) -> Vec<wgt::CooperativeMatrixProperties> {
1120 self.raw.capabilities.cooperative_matrix_properties.clone()
1121 }
1122
1123 pub fn get_texture_format_features(
1124 &self,
1125 format: wgt::TextureFormat,
1126 ) -> wgt::TextureFormatFeatures {
1127 use hal::TextureFormatCapabilities as Tfc;
1128
1129 let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
1130 let mut allowed_usages = wgt::TextureUsages::empty();
1131
1132 allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
1133 allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
1134 allowed_usages.set(
1135 wgt::TextureUsages::TEXTURE_BINDING,
1136 caps.contains(Tfc::SAMPLED),
1137 );
1138 allowed_usages.set(
1139 wgt::TextureUsages::STORAGE_BINDING,
1140 caps.intersects(
1141 Tfc::STORAGE_WRITE_ONLY
1142 | Tfc::STORAGE_READ_ONLY
1143 | Tfc::STORAGE_READ_WRITE
1144 | Tfc::STORAGE_ATOMIC,
1145 ),
1146 );
1147 allowed_usages.set(
1148 wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TRANSIENT_ATTACHMENT,
1149 caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
1150 );
1151 allowed_usages.set(
1152 wgt::TextureUsages::STORAGE_ATOMIC,
1153 caps.contains(Tfc::STORAGE_ATOMIC),
1154 );
1155
1156 let mut flags = wgt::TextureFormatFeatureFlags::empty();
1157 flags.set(
1158 wgt::TextureFormatFeatureFlags::STORAGE_READ_ONLY,
1159 caps.contains(Tfc::STORAGE_READ_ONLY),
1160 );
1161 flags.set(
1162 wgt::TextureFormatFeatureFlags::STORAGE_WRITE_ONLY,
1163 caps.contains(Tfc::STORAGE_WRITE_ONLY),
1164 );
1165 flags.set(
1166 wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
1167 caps.contains(Tfc::STORAGE_READ_WRITE),
1168 );
1169
1170 flags.set(
1171 wgt::TextureFormatFeatureFlags::STORAGE_ATOMIC,
1172 caps.contains(Tfc::STORAGE_ATOMIC),
1173 );
1174
1175 flags.set(
1176 wgt::TextureFormatFeatureFlags::FILTERABLE,
1177 caps.contains(Tfc::SAMPLED_LINEAR),
1178 );
1179
1180 flags.set(
1181 wgt::TextureFormatFeatureFlags::BLENDABLE,
1182 caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
1183 );
1184
1185 flags.set(
1186 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
1187 caps.contains(Tfc::MULTISAMPLE_X2),
1188 );
1189 flags.set(
1190 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
1191 caps.contains(Tfc::MULTISAMPLE_X4),
1192 );
1193 flags.set(
1194 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
1195 caps.contains(Tfc::MULTISAMPLE_X8),
1196 );
1197 flags.set(
1198 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
1199 caps.contains(Tfc::MULTISAMPLE_X16),
1200 );
1201
1202 flags.set(
1203 wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
1204 caps.contains(Tfc::MULTISAMPLE_RESOLVE),
1205 );
1206
1207 wgt::TextureFormatFeatures {
1208 allowed_usages,
1209 flags,
1210 }
1211 }
1212
1213 pub unsafe fn create_device_and_queue_from_hal(
1218 self: &Arc<Self>,
1219 hal_device: hal::DynOpenDevice,
1220 desc: &DeviceDescriptor,
1221 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1222 profiling::scope!("Adapter::create_device_and_queue_from_hal");
1223 api_log!("Adapter::create_device_and_queue_from_hal");
1224
1225 if self
1226 .instance
1227 .flags
1228 .contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
1229 && self.consumed.load(Ordering::SeqCst)
1230 {
1231 return Err(RequestDeviceError::AdapterConsumed);
1232 }
1233
1234 let default_queue_desc = desc.default_queue.clone();
1235
1236 let device = Device::new(hal_device.device, self, desc, self.instance.flags)?;
1237 let device = Arc::new(device);
1238
1239 let queue = Queue::new(
1240 device.clone(),
1241 hal_device.queue,
1242 default_queue_desc,
1243 self.instance.flags,
1244 )?;
1245 let queue = Arc::new(queue);
1246
1247 device.set_queue(&queue);
1248 device.late_init_resources_with_queue()?;
1249
1250 resource_log!("Created Device {:?}", Arc::as_ptr(&device));
1251 resource_log!("Created Queue {:?}", Arc::as_ptr(&queue));
1252
1253 self.instance.devices.push(&device);
1254
1255 self.consumed.store(true, Ordering::SeqCst);
1256
1257 Ok((device, queue))
1258 }
1259
1260 pub fn validate_device_descriptor(
1270 &self,
1271 desc: &mut DeviceDescriptor,
1272 ) -> Result<(), RequestDeviceError> {
1273 filter_features_and_limits(
1274 self.instance.flags,
1275 &mut desc.required_features,
1276 &mut desc.required_limits,
1277 );
1278
1279 if !self.raw.features.contains(desc.required_features) {
1281 return Err(RequestDeviceError::UnsupportedFeature(
1282 desc.required_features - self.raw.features,
1283 ));
1284 }
1285
1286 if desc
1288 .required_features
1289 .intersects(wgt::Features::all_experimental_mask())
1290 && !desc.experimental_features.is_enabled()
1291 {
1292 return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
1293 desc.required_features
1294 .intersection(wgt::Features::all_experimental_mask()),
1295 ));
1296 }
1297
1298 let caps = &self.raw.capabilities;
1299 if Backends::PRIMARY.contains(Backends::from(self.backend()))
1300 && !caps.downlevel.is_webgpu_compliant()
1301 {
1302 let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
1303 log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
1304 log::warn!("{:#?}", caps.downlevel);
1305 }
1306
1307 if desc
1309 .required_features
1310 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
1311 && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
1312 {
1313 log::warn!(
1314 "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
1315 This is a massive performance footgun and likely not what you wanted"
1316 );
1317 }
1318
1319 if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
1320 return Err(RequestDeviceError::LimitsExceeded(failed));
1321 }
1322
1323 normalize_max_resource_per_shader_stage_limits(&mut desc.required_limits);
1324
1325 Ok(())
1326 }
1327
1328 pub fn request_device(
1329 self: &Arc<Self>,
1330 desc: &DeviceDescriptor,
1331 ) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> {
1332 profiling::scope!("Adapter::request_device");
1333 api_log!("Adapter::request_device");
1334
1335 let mut desc = desc.clone();
1336 self.validate_device_descriptor(&mut desc)?;
1337
1338 let open = unsafe {
1339 self.raw.adapter.open(
1340 desc.required_features,
1341 &desc.required_limits,
1342 &desc.memory_hints,
1343 )
1344 }
1345 .map_err(DeviceError::from_hal)?;
1346
1347 unsafe { self.create_device_and_queue_from_hal(open, &desc) }
1348 }
1349}
1350
1351impl Drop for Adapter {
1352 #[allow(trivial_casts)]
1353 fn drop(&mut self) {
1354 profiling::scope!("Adapter::drop");
1355 api_log!("Adapter::drop {:?}", self as *const _);
1356 }
1357}
1358
1359crate::impl_resource_type!(Adapter);
1360crate::impl_storage_item!(Adapter);
1361
1362#[derive(Clone, Debug, Error)]
1363#[non_exhaustive]
1364pub enum GetSurfaceSupportError {
1365 #[error("Surface is not supported for the specified backend {0}")]
1366 NotSupportedByBackend(Backend),
1367 #[error("Failed to retrieve surface capabilities for the specified adapter.")]
1368 FailedToRetrieveSurfaceCapabilitiesForAdapter,
1369}
1370
1371#[derive(Clone, Debug, Error)]
1372#[non_exhaustive]
1374pub enum RequestDeviceError {
1375 #[error(transparent)]
1376 Device(#[from] DeviceError),
1377 #[error(transparent)]
1378 LimitsExceeded(#[from] FailedLimit),
1379 #[error("Failed to initialize Timestamp Normalizer")]
1380 TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
1381 #[error("Unsupported features were requested: {0}")]
1382 UnsupportedFeature(wgt::Features),
1383 #[error(
1384 "Some experimental features, {0}, were requested, but experimental features are not enabled"
1385 )]
1386 ExperimentalFeaturesNotEnabled(wgt::Features),
1387 #[error("The adapter has already been consumed.")]
1388 AdapterConsumed,
1389}
1390
1391#[derive(Clone, Debug, Error)]
1392#[non_exhaustive]
1393pub enum CreateSurfaceError {
1394 #[error("The backend {0} was not enabled on the instance.")]
1395 BackendNotEnabled(Backend),
1396 #[error("Failed to create surface for any enabled backend: {0:?}")]
1397 FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
1398 #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
1399 MismatchingDisplayHandle,
1400 #[error(
1401 "No `DisplayHandle` is available to create this surface with. When creating a surface with `create_surface()` \
1402 you must specify a display handle in `InstanceDescriptor::display`. \
1403 Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
1404 you must use `create_surface_unsafe()`."
1405 )]
1406 MissingDisplayHandle,
1407}
1408
1409fn adapter_allowed(
1414 flags: InstanceFlags,
1415 info: &impl fmt::Debug,
1416 limits: &wgt::Limits,
1417 downlevel: &wgt::DownlevelCapabilities,
1418) -> bool {
1419 let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment;
1425 if !min_uniform_buffer_offset_alignment.is_power_of_two() {
1426 log::error!(
1427 "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
1428 info,
1429 min_uniform_buffer_offset_alignment
1430 );
1431 return false;
1432 }
1433 let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment;
1434 if !min_storage_buffer_offset_alignment.is_power_of_two() {
1435 log::error!(
1436 "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
1437 info,
1438 min_storage_buffer_offset_alignment
1439 );
1440 return false;
1441 }
1442
1443 if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1445 return true;
1446 }
1447
1448 let mut min_limits = wgt::Limits::defaults();
1450 min_limits.zero_native_only();
1451 let failed_limits = check_limits(&min_limits, limits);
1452 if !failed_limits.is_empty() {
1453 log::debug!(
1454 "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
1455 info,
1456 failed_limits
1457 );
1458 return false;
1459 }
1460
1461 if !downlevel.is_webgpu_compliant() {
1462 let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
1463 log::debug!(
1464 "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
1465 info,
1466 missing_flags
1467 );
1468 return false;
1469 }
1470
1471 true
1472}
1473
1474fn filter_features_and_limits(
1475 flags: InstanceFlags,
1476 features: &mut wgt::Features,
1477 limits: &mut wgt::Limits,
1478) {
1479 if flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
1480 *features &= wgt::Features::all_webgpu_mask() | limits::EXEMPT_FEATURES;
1481 limits.zero_native_only();
1482 }
1483}
1484
1485fn normalize_max_resource_per_shader_stage_limits(limits: &mut wgt::Limits) {
1486 limits.max_storage_buffers_per_shader_stage = [
1493 limits.max_storage_buffers_per_shader_stage,
1494 limits.max_storage_buffers_in_vertex_stage,
1495 limits.max_storage_buffers_in_fragment_stage,
1496 ]
1497 .into_iter()
1498 .max()
1499 .unwrap();
1500
1501 limits.max_storage_textures_per_shader_stage = [
1506 limits.max_storage_textures_per_shader_stage,
1507 limits.max_storage_textures_in_vertex_stage,
1508 limits.max_storage_textures_in_fragment_stage,
1509 ]
1510 .into_iter()
1511 .max()
1512 .unwrap();
1513
1514 limits.max_storage_buffers_in_vertex_stage = limits.max_storage_buffers_per_shader_stage;
1523 limits.max_storage_buffers_in_fragment_stage = limits.max_storage_buffers_per_shader_stage;
1524
1525 limits.max_storage_textures_in_vertex_stage = limits.max_storage_textures_per_shader_stage;
1529 limits.max_storage_textures_in_fragment_stage = limits.max_storage_textures_per_shader_stage;
1530}
1531
1532#[cfg(test)]
1533mod tests {
1534 use super::*;
1535
1536 fn compliant_downlevel() -> wgt::DownlevelCapabilities {
1537 wgt::DownlevelCapabilities {
1538 flags: wgt::DownlevelFlags::compliant(),
1539 ..Default::default()
1540 }
1541 }
1542
1543 #[test]
1544 fn non_power_of_two_uniform_alignment_always_rejected() {
1545 let limits = wgt::Limits {
1546 min_uniform_buffer_offset_alignment: 3,
1547 ..wgt::Limits::defaults()
1548 };
1549 assert!(!adapter_allowed(
1550 InstanceFlags::empty(),
1551 &"",
1552 &limits,
1553 &compliant_downlevel()
1554 ));
1555 assert!(!adapter_allowed(
1556 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1557 &"",
1558 &limits,
1559 &compliant_downlevel()
1560 ));
1561 }
1562
1563 #[test]
1564 fn non_power_of_two_storage_alignment_always_rejected() {
1565 let limits = wgt::Limits {
1566 min_storage_buffer_offset_alignment: 96,
1567 ..wgt::Limits::defaults()
1568 };
1569 assert!(!adapter_allowed(
1570 InstanceFlags::empty(),
1571 &"",
1572 &limits,
1573 &compliant_downlevel()
1574 ));
1575 assert!(!adapter_allowed(
1576 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1577 &"",
1578 &limits,
1579 &compliant_downlevel()
1580 ));
1581 }
1582
1583 #[test]
1584 fn low_limits_allowed_without_strict_compliance() {
1585 let limits = wgt::Limits {
1586 max_texture_dimension_1d: 1,
1587 ..wgt::Limits::defaults()
1588 };
1589 assert!(adapter_allowed(
1590 InstanceFlags::empty(),
1591 &"",
1592 &limits,
1593 &wgt::DownlevelCapabilities::default()
1594 ));
1595 }
1596
1597 #[test]
1598 fn low_limits_rejected_with_strict_compliance() {
1599 let limits = wgt::Limits {
1600 max_texture_dimension_1d: 1,
1601 ..wgt::Limits::defaults()
1602 };
1603 assert!(!adapter_allowed(
1604 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1605 &"",
1606 &limits,
1607 &compliant_downlevel()
1608 ));
1609 }
1610
1611 #[test]
1612 fn missing_downlevel_flags_rejected_with_strict_compliance() {
1613 let downlevel = wgt::DownlevelCapabilities {
1614 flags: wgt::DownlevelFlags::empty(),
1615 ..Default::default()
1616 };
1617 assert!(!adapter_allowed(
1618 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1619 &"",
1620 &wgt::Limits::defaults(),
1621 &downlevel
1622 ));
1623 }
1624
1625 #[test]
1626 fn missing_surface_view_formats_allowed_with_strict_compliance() {
1627 let downlevel = wgt::DownlevelCapabilities {
1628 flags: wgt::DownlevelFlags::all() - wgt::DownlevelFlags::SURFACE_VIEW_FORMATS,
1629 ..Default::default()
1630 };
1631 assert!(adapter_allowed(
1632 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1633 &"",
1634 &wgt::Limits::defaults(),
1635 &downlevel
1636 ));
1637 }
1638
1639 #[test]
1640 fn fully_compliant_adapter_always_allowed() {
1641 assert!(adapter_allowed(
1642 InstanceFlags::STRICT_WEBGPU_COMPLIANCE,
1643 &"",
1644 &wgt::Limits::defaults(),
1645 &compliant_downlevel()
1646 ));
1647 }
1648
1649 mod storage_resource_limits {
1650 use super::*;
1651
1652 #[track_caller]
1653 fn assert_normalized_eq(non_normalized: &wgt::Limits, expected: &wgt::Limits) {
1654 let mut normalized = non_normalized.clone();
1655 normalize_max_resource_per_shader_stage_limits(&mut normalized);
1656 assert_eq!(&normalized, expected);
1657 }
1658
1659 #[test]
1660 fn normalization_is_idempotent() {
1661 let original = wgt::Limits {
1662 max_storage_buffers_in_vertex_stage: 16,
1663 max_storage_textures_in_vertex_stage: 9,
1664 ..wgt::Limits::defaults()
1665 };
1666
1667 let mut first_normalization = original.clone();
1668 normalize_max_resource_per_shader_stage_limits(&mut first_normalization);
1669 assert_ne!(original, first_normalization);
1670
1671 let mut second_normalization = first_normalization.clone();
1672 normalize_max_resource_per_shader_stage_limits(&mut second_normalization);
1673 assert_eq!(first_normalization, second_normalization);
1674 }
1675
1676 #[test]
1677 fn limits_presets_already_normalized() {
1678 [
1679 wgt::Limits::defaults(),
1680 wgt::Limits::downlevel_defaults(),
1681 wgt::Limits::downlevel_webgl2_defaults(),
1682 wgt::Limits::unlimited(),
1683 ]
1684 .iter()
1685 .for_each(|l| assert_normalized_eq(l, l))
1686 }
1687
1688 #[test]
1689 fn in_stage_raises_per_shader_stage() {
1690 assert_normalized_eq(
1691 &wgt::Limits {
1692 max_storage_buffers_per_shader_stage: 8,
1693 max_storage_buffers_in_vertex_stage: 16,
1694 max_storage_buffers_in_fragment_stage: 16,
1695 ..wgt::Limits::defaults()
1696 },
1697 &wgt::Limits {
1698 max_storage_buffers_per_shader_stage: 16,
1699 max_storage_buffers_in_vertex_stage: 16,
1700 max_storage_buffers_in_fragment_stage: 16,
1701 ..wgt::Limits::defaults()
1702 },
1703 );
1704
1705 assert_normalized_eq(
1706 &wgt::Limits {
1707 max_storage_textures_per_shader_stage: 8,
1708 max_storage_textures_in_vertex_stage: 9,
1709 max_storage_textures_in_fragment_stage: 9,
1710 ..wgt::Limits::defaults()
1711 },
1712 &wgt::Limits {
1713 max_storage_textures_per_shader_stage: 9,
1714 max_storage_textures_in_vertex_stage: 9,
1715 max_storage_textures_in_fragment_stage: 9,
1716 ..wgt::Limits::defaults()
1717 },
1718 );
1719 }
1720
1721 #[test]
1722 fn per_shader_stage_raises_in_stage() {
1723 assert_normalized_eq(
1724 &wgt::Limits {
1725 max_storage_buffers_per_shader_stage: 16,
1726 max_storage_buffers_in_vertex_stage: 4,
1727 max_storage_buffers_in_fragment_stage: 4,
1728 max_storage_textures_per_shader_stage: 8,
1729 max_storage_textures_in_vertex_stage: 4,
1730 max_storage_textures_in_fragment_stage: 4,
1731 ..wgt::Limits::defaults()
1732 },
1733 &wgt::Limits {
1734 max_storage_buffers_per_shader_stage: 16,
1735 max_storage_buffers_in_vertex_stage: 16,
1736 max_storage_buffers_in_fragment_stage: 16,
1737 max_storage_textures_per_shader_stage: 8,
1738 max_storage_textures_in_vertex_stage: 8,
1739 max_storage_textures_in_fragment_stage: 8,
1740 ..wgt::Limits::defaults()
1741 },
1742 );
1743 }
1744
1745 #[test]
1746 fn lowering_per_shader_stage_noop() {
1747 assert_normalized_eq(
1748 &wgt::Limits {
1749 max_storage_buffers_per_shader_stage: 1,
1750 max_storage_textures_per_shader_stage: 1,
1751 ..wgt::Limits::defaults()
1752 },
1753 &wgt::Limits::defaults(),
1754 );
1755 }
1756 }
1757
1758 #[test]
1759 fn downlevel_default_limits_less_than_default_limits() {
1760 let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
1761 assert!(
1762 res.is_empty(),
1763 "Downlevel limits are greater than default limits",
1764 )
1765 }
1766}