1use std::collections::HashMap;
2use std::sync::Arc;
3
4use crate::{
5 api_log,
6 device::{queue::Queue, resource::Device, DeviceDescriptor},
7 global::Global,
8 hal_api::HalApi,
9 id::markers,
10 id::{AdapterId, DeviceId, Id, Marker, QueueId, SurfaceId},
11 lock::{rank, Mutex},
12 present::Presentation,
13 resource::{Resource, ResourceInfo, ResourceType},
14 resource_log, LabelHelpers, DOWNLEVEL_WARNING_MESSAGE,
15};
16
17use wgt::{Backend, Backends, PowerPreference};
18
19use hal::{Adapter as _, Instance as _, OpenDevice};
20use thiserror::Error;
21
22pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
23type HalInstance<A> = <A as hal::Api>::Instance;
24type HalSurface<A> = <A as hal::Api>::Surface;
25
26#[derive(Clone, Debug, Error)]
27#[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
28pub struct FailedLimit {
29 name: &'static str,
30 requested: u64,
31 allowed: u64,
32}
33
34fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLimit> {
35 let mut failed = Vec::new();
36
37 requested.check_limits_with_fail_fn(allowed, false, |name, requested, allowed| {
38 failed.push(FailedLimit {
39 name,
40 requested,
41 allowed,
42 })
43 });
44
45 failed
46}
47
48#[test]
49fn downlevel_default_limits_less_than_default_limits() {
50 let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
51 assert!(
52 res.is_empty(),
53 "Downlevel limits are greater than default limits",
54 )
55}
56
57#[derive(Default)]
58pub struct Instance {
59 #[allow(dead_code)]
60 pub name: String,
61 #[cfg(vulkan)]
62 pub vulkan: Option<HalInstance<hal::api::Vulkan>>,
63 #[cfg(metal)]
64 pub metal: Option<HalInstance<hal::api::Metal>>,
65 #[cfg(dx12)]
66 pub dx12: Option<HalInstance<hal::api::Dx12>>,
67 #[cfg(gles)]
68 pub gl: Option<HalInstance<hal::api::Gles>>,
69 pub flags: wgt::InstanceFlags,
70}
71
72impl Instance {
73 pub fn new(name: &str, instance_desc: wgt::InstanceDescriptor) -> Self {
74 fn init<A: HalApi>(_: A, instance_desc: &wgt::InstanceDescriptor) -> Option<A::Instance> {
75 if instance_desc.backends.contains(A::VARIANT.into()) {
76 let hal_desc = hal::InstanceDescriptor {
77 name: "wgpu",
78 flags: instance_desc.flags,
79 dx12_shader_compiler: instance_desc.dx12_shader_compiler.clone(),
80 gles_minor_version: instance_desc.gles_minor_version,
81 };
82 match unsafe { hal::Instance::init(&hal_desc) } {
83 Ok(instance) => {
84 log::debug!("Instance::new: created {:?} backend", A::VARIANT);
85 Some(instance)
86 }
87 Err(err) => {
88 log::debug!(
89 "Instance::new: failed to create {:?} backend: {:?}",
90 A::VARIANT,
91 err
92 );
93 None
94 }
95 }
96 } else {
97 log::trace!("Instance::new: backend {:?} not requested", A::VARIANT);
98 None
99 }
100 }
101
102 Self {
103 name: name.to_string(),
104 #[cfg(vulkan)]
105 vulkan: init(hal::api::Vulkan, &instance_desc),
106 #[cfg(metal)]
107 metal: init(hal::api::Metal, &instance_desc),
108 #[cfg(dx12)]
109 dx12: init(hal::api::Dx12, &instance_desc),
110 #[cfg(gles)]
111 gl: init(hal::api::Gles, &instance_desc),
112 flags: instance_desc.flags,
113 }
114 }
115
116 pub(crate) fn destroy_surface(&self, surface: Surface) {
117 fn destroy<A: HalApi>(instance: &Option<A::Instance>, mut surface: Option<HalSurface<A>>) {
118 if let Some(surface) = surface.take() {
119 unsafe {
120 instance.as_ref().unwrap().destroy_surface(surface);
121 }
122 }
123 }
124 #[cfg(vulkan)]
125 destroy::<hal::api::Vulkan>(&self.vulkan, surface.vulkan);
126 #[cfg(metal)]
127 destroy::<hal::api::Metal>(&self.metal, surface.metal);
128 #[cfg(dx12)]
129 destroy::<hal::api::Dx12>(&self.dx12, surface.dx12);
130 #[cfg(gles)]
131 destroy::<hal::api::Gles>(&self.gl, surface.gl);
132 }
133}
134
135pub struct Surface {
136 pub(crate) presentation: Mutex<Option<Presentation>>,
137 pub(crate) info: ResourceInfo<Surface>,
138
139 #[cfg(vulkan)]
140 pub vulkan: Option<HalSurface<hal::api::Vulkan>>,
141 #[cfg(metal)]
142 pub metal: Option<HalSurface<hal::api::Metal>>,
143 #[cfg(dx12)]
144 pub dx12: Option<HalSurface<hal::api::Dx12>>,
145 #[cfg(gles)]
146 pub gl: Option<HalSurface<hal::api::Gles>>,
147}
148
149impl Resource for Surface {
150 const TYPE: ResourceType = "Surface";
151
152 type Marker = markers::Surface;
153
154 fn as_info(&self) -> &ResourceInfo<Self> {
155 &self.info
156 }
157
158 fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
159 &mut self.info
160 }
161
162 fn label(&self) -> String {
163 String::from("<Surface>")
164 }
165}
166
167impl Surface {
168 pub fn get_capabilities<A: HalApi>(
169 &self,
170 adapter: &Adapter<A>,
171 ) -> Result<hal::SurfaceCapabilities, GetSurfaceSupportError> {
172 let suf = A::surface_as_hal(self).ok_or(GetSurfaceSupportError::Unsupported)?;
173 profiling::scope!("surface_capabilities");
174 let caps = unsafe {
175 adapter
176 .raw
177 .adapter
178 .surface_capabilities(suf)
179 .ok_or(GetSurfaceSupportError::Unsupported)?
180 };
181
182 Ok(caps)
183 }
184}
185
186pub struct Adapter<A: HalApi> {
187 pub(crate) raw: hal::ExposedAdapter<A>,
188 pub(crate) info: ResourceInfo<Adapter<A>>,
189}
190
191impl<A: HalApi> Adapter<A> {
192 fn new(mut raw: hal::ExposedAdapter<A>) -> Self {
193 const MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND: u32 = 32;
195
196 let limits = &mut raw.capabilities.limits;
197
198 limits.min_uniform_buffer_offset_alignment = limits
199 .min_uniform_buffer_offset_alignment
200 .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
201 limits.min_storage_buffer_offset_alignment = limits
202 .min_storage_buffer_offset_alignment
203 .max(MIN_BUFFER_OFFSET_ALIGNMENT_LOWER_BOUND);
204
205 Self {
206 raw,
207 info: ResourceInfo::new("<Adapter>", None),
208 }
209 }
210
211 pub fn is_surface_supported(&self, surface: &Surface) -> bool {
212 let suf = A::surface_as_hal(surface);
213
214 match suf {
219 Some(suf) => unsafe { self.raw.adapter.surface_capabilities(suf) }.is_some(),
220 None => false,
221 }
222 }
223
224 pub(crate) fn get_texture_format_features(
225 &self,
226 format: wgt::TextureFormat,
227 ) -> wgt::TextureFormatFeatures {
228 use hal::TextureFormatCapabilities as Tfc;
229
230 let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) };
231 let mut allowed_usages = wgt::TextureUsages::empty();
232
233 allowed_usages.set(wgt::TextureUsages::COPY_SRC, caps.contains(Tfc::COPY_SRC));
234 allowed_usages.set(wgt::TextureUsages::COPY_DST, caps.contains(Tfc::COPY_DST));
235 allowed_usages.set(
236 wgt::TextureUsages::TEXTURE_BINDING,
237 caps.contains(Tfc::SAMPLED),
238 );
239 allowed_usages.set(
240 wgt::TextureUsages::STORAGE_BINDING,
241 caps.contains(Tfc::STORAGE),
242 );
243 allowed_usages.set(
244 wgt::TextureUsages::RENDER_ATTACHMENT,
245 caps.intersects(Tfc::COLOR_ATTACHMENT | Tfc::DEPTH_STENCIL_ATTACHMENT),
246 );
247
248 let mut flags = wgt::TextureFormatFeatureFlags::empty();
249 flags.set(
250 wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE,
251 caps.contains(Tfc::STORAGE_READ_WRITE),
252 );
253
254 flags.set(
255 wgt::TextureFormatFeatureFlags::FILTERABLE,
256 caps.contains(Tfc::SAMPLED_LINEAR),
257 );
258
259 flags.set(
260 wgt::TextureFormatFeatureFlags::BLENDABLE,
261 caps.contains(Tfc::COLOR_ATTACHMENT_BLEND),
262 );
263
264 flags.set(
265 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X2,
266 caps.contains(Tfc::MULTISAMPLE_X2),
267 );
268 flags.set(
269 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X4,
270 caps.contains(Tfc::MULTISAMPLE_X4),
271 );
272 flags.set(
273 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X8,
274 caps.contains(Tfc::MULTISAMPLE_X8),
275 );
276 flags.set(
277 wgt::TextureFormatFeatureFlags::MULTISAMPLE_X16,
278 caps.contains(Tfc::MULTISAMPLE_X16),
279 );
280
281 flags.set(
282 wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE,
283 caps.contains(Tfc::MULTISAMPLE_RESOLVE),
284 );
285
286 wgt::TextureFormatFeatures {
287 allowed_usages,
288 flags,
289 }
290 }
291
292 fn create_device_and_queue_from_hal(
293 self: &Arc<Self>,
294 hal_device: OpenDevice<A>,
295 desc: &DeviceDescriptor,
296 instance_flags: wgt::InstanceFlags,
297 trace_path: Option<&std::path::Path>,
298 ) -> Result<(Device<A>, Queue<A>), RequestDeviceError> {
299 api_log!("Adapter::create_device");
300
301 if let Ok(device) = Device::new(
302 hal_device.device,
303 &hal_device.queue,
304 self,
305 desc,
306 trace_path,
307 instance_flags,
308 ) {
309 let queue = Queue {
310 device: None,
311 raw: Some(hal_device.queue),
312 info: ResourceInfo::new("<Queue>", None),
313 };
314 return Ok((device, queue));
315 }
316 Err(RequestDeviceError::OutOfMemory)
317 }
318
319 fn create_device_and_queue(
320 self: &Arc<Self>,
321 desc: &DeviceDescriptor,
322 instance_flags: wgt::InstanceFlags,
323 trace_path: Option<&std::path::Path>,
324 ) -> Result<(Device<A>, Queue<A>), RequestDeviceError> {
325 if !self.raw.features.contains(desc.required_features) {
327 return Err(RequestDeviceError::UnsupportedFeature(
328 desc.required_features - self.raw.features,
329 ));
330 }
331
332 let caps = &self.raw.capabilities;
333 if wgt::Backends::PRIMARY.contains(wgt::Backends::from(A::VARIANT))
334 && !caps.downlevel.is_webgpu_compliant()
335 {
336 let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
337 log::warn!(
338 "Missing downlevel flags: {:?}\n{}",
339 missing_flags,
340 DOWNLEVEL_WARNING_MESSAGE
341 );
342 log::warn!("{:#?}", caps.downlevel);
343 }
344
345 if desc
347 .required_features
348 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
349 && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
350 {
351 log::warn!(
352 "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
353 This is a massive performance footgun and likely not what you wanted"
354 );
355 }
356
357 if let Some(_) = desc.label {
358 }
360
361 if let Some(failed) = check_limits(&desc.required_limits, &caps.limits).pop() {
362 return Err(RequestDeviceError::LimitsExceeded(failed));
363 }
364
365 let open = unsafe {
366 self.raw
367 .adapter
368 .open(desc.required_features, &desc.required_limits)
369 }
370 .map_err(|err| match err {
371 hal::DeviceError::Lost => RequestDeviceError::DeviceLost,
372 hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory,
373 hal::DeviceError::ResourceCreationFailed => RequestDeviceError::Internal,
374 })?;
375
376 self.create_device_and_queue_from_hal(open, desc, instance_flags, trace_path)
377 }
378}
379
380impl<A: HalApi> Resource for Adapter<A> {
381 const TYPE: ResourceType = "Adapter";
382
383 type Marker = markers::Adapter;
384
385 fn as_info(&self) -> &ResourceInfo<Self> {
386 &self.info
387 }
388
389 fn as_info_mut(&mut self) -> &mut ResourceInfo<Self> {
390 &mut self.info
391 }
392}
393
394#[derive(Clone, Debug, Error)]
395#[non_exhaustive]
396pub enum IsSurfaceSupportedError {
397 #[error("Invalid adapter")]
398 InvalidAdapter,
399 #[error("Invalid surface")]
400 InvalidSurface,
401}
402
403#[derive(Clone, Debug, Error)]
404#[non_exhaustive]
405pub enum GetSurfaceSupportError {
406 #[error("Invalid adapter")]
407 InvalidAdapter,
408 #[error("Invalid surface")]
409 InvalidSurface,
410 #[error("Surface is not supported by the adapter")]
411 Unsupported,
412}
413
414#[derive(Clone, Debug, Error)]
415#[non_exhaustive]
417pub enum RequestDeviceError {
418 #[error("Parent adapter is invalid")]
419 InvalidAdapter,
420 #[error("Connection to device was lost during initialization")]
421 DeviceLost,
422 #[error("Device initialization failed due to implementation specific errors")]
423 Internal,
424 #[error(transparent)]
425 LimitsExceeded(#[from] FailedLimit),
426 #[error("Device has no queue supporting graphics")]
427 NoGraphicsQueue,
428 #[error("Not enough memory left to request device")]
429 OutOfMemory,
430 #[error("Unsupported features were requested: {0:?}")]
431 UnsupportedFeature(wgt::Features),
432}
433
434pub enum AdapterInputs<'a, M: Marker> {
435 IdSet(&'a [Id<M>]),
436 Mask(Backends, fn(Backend) -> Option<Id<M>>),
437}
438
439impl<M: Marker> AdapterInputs<'_, M> {
440 fn find(&self, b: Backend) -> Option<Option<Id<M>>> {
441 match *self {
442 Self::IdSet(ids) => Some(Some(ids.iter().find(|id| id.backend() == b).copied()?)),
443 Self::Mask(bits, ref fun) => {
444 if bits.contains(b.into()) {
445 Some(fun(b))
446 } else {
447 None
448 }
449 }
450 }
451 }
452}
453
454#[derive(Clone, Debug, Error)]
455#[error("Adapter is invalid")]
456pub struct InvalidAdapter;
457
458#[derive(Clone, Debug, Error)]
459#[non_exhaustive]
460pub enum RequestAdapterError {
461 #[error("No suitable adapter found")]
462 NotFound,
463 #[error("Surface {0:?} is invalid")]
464 InvalidSurface(SurfaceId),
465}
466
467#[derive(Clone, Debug, Error)]
468#[non_exhaustive]
469pub enum CreateSurfaceError {
470 #[error("The backend {0} was not enabled on the instance.")]
471 BackendNotEnabled(Backend),
472 #[error("Failed to create surface for any enabled backend: {0:?}")]
473 FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>),
474}
475
476impl Global {
477 #[cfg(feature = "raw-window-handle")]
495 pub unsafe fn instance_create_surface(
496 &self,
497 display_handle: raw_window_handle::RawDisplayHandle,
498 window_handle: raw_window_handle::RawWindowHandle,
499 id_in: Option<SurfaceId>,
500 ) -> Result<SurfaceId, CreateSurfaceError> {
501 profiling::scope!("Instance::create_surface");
502
503 fn init<A: HalApi>(
504 errors: &mut HashMap<Backend, hal::InstanceError>,
505 any_created: &mut bool,
506 backend: Backend,
507 inst: &Option<A::Instance>,
508 display_handle: raw_window_handle::RawDisplayHandle,
509 window_handle: raw_window_handle::RawWindowHandle,
510 ) -> Option<HalSurface<A>> {
511 inst.as_ref().and_then(|inst| {
512 match unsafe { inst.create_surface(display_handle, window_handle) } {
513 Ok(raw) => {
514 *any_created = true;
515 Some(raw)
516 }
517 Err(err) => {
518 log::debug!(
519 "Instance::create_surface: failed to create surface for {:?}: {:?}",
520 backend,
521 err
522 );
523 errors.insert(backend, err);
524 None
525 }
526 }
527 })
528 }
529
530 let mut errors = HashMap::default();
531 let mut any_created = false;
532
533 let surface = Surface {
534 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
535 info: ResourceInfo::new("<Surface>", None),
536
537 #[cfg(vulkan)]
538 vulkan: init::<hal::api::Vulkan>(
539 &mut errors,
540 &mut any_created,
541 Backend::Vulkan,
542 &self.instance.vulkan,
543 display_handle,
544 window_handle,
545 ),
546 #[cfg(metal)]
547 metal: init::<hal::api::Metal>(
548 &mut errors,
549 &mut any_created,
550 Backend::Metal,
551 &self.instance.metal,
552 display_handle,
553 window_handle,
554 ),
555 #[cfg(dx12)]
556 dx12: init::<hal::api::Dx12>(
557 &mut errors,
558 &mut any_created,
559 Backend::Dx12,
560 &self.instance.dx12,
561 display_handle,
562 window_handle,
563 ),
564 #[cfg(gles)]
565 gl: init::<hal::api::Gles>(
566 &mut errors,
567 &mut any_created,
568 Backend::Gl,
569 &self.instance.gl,
570 display_handle,
571 window_handle,
572 ),
573 };
574
575 if any_created {
576 #[allow(clippy::arc_with_non_send_sync)]
577 let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
578 Ok(id)
579 } else {
580 Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
581 errors,
582 ))
583 }
584 }
585
586 #[cfg(metal)]
590 pub unsafe fn instance_create_surface_metal(
591 &self,
592 layer: *mut std::ffi::c_void,
593 id_in: Option<SurfaceId>,
594 ) -> Result<SurfaceId, CreateSurfaceError> {
595 profiling::scope!("Instance::create_surface_metal");
596
597 let surface = Surface {
598 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
599 info: ResourceInfo::new("<Surface>", None),
600 metal: Some(self.instance.metal.as_ref().map_or(
601 Err(CreateSurfaceError::BackendNotEnabled(Backend::Metal)),
602 |inst| {
603 #[allow(clippy::transmute_ptr_to_ref)]
605 Ok(inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) }))
606 },
607 )?),
608 #[cfg(dx12)]
609 dx12: None,
610 #[cfg(vulkan)]
611 vulkan: None,
612 #[cfg(gles)]
613 gl: None,
614 };
615
616 let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
617 Ok(id)
618 }
619
620 #[cfg(dx12)]
621 fn instance_create_surface_dx12(
622 &self,
623 id_in: Option<SurfaceId>,
624 create_surface_func: impl FnOnce(&HalInstance<hal::api::Dx12>) -> HalSurface<hal::api::Dx12>,
625 ) -> Result<SurfaceId, CreateSurfaceError> {
626 let surface = Surface {
627 presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
628 info: ResourceInfo::new("<Surface>", None),
629 dx12: Some(create_surface_func(
630 self.instance
631 .dx12
632 .as_ref()
633 .ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Dx12))?,
634 )),
635 #[cfg(metal)]
636 metal: None,
637 #[cfg(vulkan)]
638 vulkan: None,
639 #[cfg(gles)]
640 gl: None,
641 };
642
643 let (id, _) = self.surfaces.prepare(id_in).assign(Arc::new(surface));
644 Ok(id)
645 }
646
647 #[cfg(dx12)]
648 pub unsafe fn instance_create_surface_from_visual(
652 &self,
653 visual: *mut std::ffi::c_void,
654 id_in: Option<SurfaceId>,
655 ) -> Result<SurfaceId, CreateSurfaceError> {
656 profiling::scope!("Instance::instance_create_surface_from_visual");
657 self.instance_create_surface_dx12(id_in, |inst| unsafe {
658 inst.create_surface_from_visual(visual as _)
659 })
660 }
661
662 #[cfg(dx12)]
663 pub unsafe fn instance_create_surface_from_surface_handle(
667 &self,
668 surface_handle: *mut std::ffi::c_void,
669 id_in: Option<SurfaceId>,
670 ) -> Result<SurfaceId, CreateSurfaceError> {
671 profiling::scope!("Instance::instance_create_surface_from_surface_handle");
672 self.instance_create_surface_dx12(id_in, |inst| unsafe {
673 inst.create_surface_from_surface_handle(surface_handle)
674 })
675 }
676
677 #[cfg(dx12)]
678 pub unsafe fn instance_create_surface_from_swap_chain_panel(
682 &self,
683 swap_chain_panel: *mut std::ffi::c_void,
684 id_in: Option<SurfaceId>,
685 ) -> Result<SurfaceId, CreateSurfaceError> {
686 profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel");
687 self.instance_create_surface_dx12(id_in, |inst| unsafe {
688 inst.create_surface_from_swap_chain_panel(swap_chain_panel as _)
689 })
690 }
691
692 pub fn surface_drop(&self, id: SurfaceId) {
693 profiling::scope!("Surface::drop");
694
695 api_log!("Surface::drop {id:?}");
696
697 fn unconfigure<A: HalApi>(
698 global: &Global,
699 surface: &Option<HalSurface<A>>,
700 present: &Presentation,
701 ) {
702 if let Some(surface) = surface {
703 let hub = HalApi::hub(global);
704 if let Some(device) = present.device.downcast_ref::<A>() {
705 hub.surface_unconfigure(device, surface);
706 }
707 }
708 }
709
710 let surface = self.surfaces.unregister(id);
711 let surface = Arc::into_inner(surface.unwrap())
712 .expect("Surface cannot be destroyed because is still in use");
713
714 if let Some(present) = surface.presentation.lock().take() {
715 #[cfg(vulkan)]
716 unconfigure::<hal::api::Vulkan>(self, &surface.vulkan, &present);
717 #[cfg(metal)]
718 unconfigure::<hal::api::Metal>(self, &surface.metal, &present);
719 #[cfg(dx12)]
720 unconfigure::<hal::api::Dx12>(self, &surface.dx12, &present);
721 #[cfg(gles)]
722 unconfigure::<hal::api::Gles>(self, &surface.gl, &present);
723 }
724 self.instance.destroy_surface(surface);
725 }
726
727 fn enumerate<A: HalApi>(
728 &self,
729 _: A,
730 instance: &Option<A::Instance>,
731 inputs: &AdapterInputs<markers::Adapter>,
732 list: &mut Vec<AdapterId>,
733 ) {
734 let inst = match *instance {
735 Some(ref inst) => inst,
736 None => return,
737 };
738 let id_backend = match inputs.find(A::VARIANT) {
739 Some(id) => id,
740 None => return,
741 };
742
743 profiling::scope!("enumerating", &*format!("{:?}", A::VARIANT));
744 let hub = HalApi::hub(self);
745
746 let hal_adapters = unsafe { inst.enumerate_adapters() };
747 for raw in hal_adapters {
748 let adapter = Adapter::new(raw);
749 log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info);
750 let (id, _) = hub.adapters.prepare(id_backend).assign(Arc::new(adapter));
751 list.push(id);
752 }
753 }
754
755 pub fn enumerate_adapters(&self, inputs: AdapterInputs<markers::Adapter>) -> Vec<AdapterId> {
756 profiling::scope!("Instance::enumerate_adapters");
757 api_log!("Instance::enumerate_adapters");
758
759 let mut adapters = Vec::new();
760
761 #[cfg(vulkan)]
762 self.enumerate(
763 hal::api::Vulkan,
764 &self.instance.vulkan,
765 &inputs,
766 &mut adapters,
767 );
768 #[cfg(metal)]
769 self.enumerate(
770 hal::api::Metal,
771 &self.instance.metal,
772 &inputs,
773 &mut adapters,
774 );
775 #[cfg(dx12)]
776 self.enumerate(hal::api::Dx12, &self.instance.dx12, &inputs, &mut adapters);
777 #[cfg(gles)]
778 self.enumerate(hal::api::Gles, &self.instance.gl, &inputs, &mut adapters);
779
780 adapters
781 }
782
783 fn select<A: HalApi>(
784 &self,
785 selected: &mut usize,
786 new_id: Option<AdapterId>,
787 mut list: Vec<hal::ExposedAdapter<A>>,
788 ) -> Option<AdapterId> {
789 match selected.checked_sub(list.len()) {
790 Some(left) => {
791 *selected = left;
792 None
793 }
794 None => {
795 let adapter = Adapter::new(list.swap_remove(*selected));
796 log::info!("Adapter {:?} {:?}", A::VARIANT, adapter.raw.info);
797 let (id, _) = HalApi::hub(self)
798 .adapters
799 .prepare(new_id)
800 .assign(Arc::new(adapter));
801 Some(id)
802 }
803 }
804 }
805
806 pub fn request_adapter(
807 &self,
808 desc: &RequestAdapterOptions,
809 inputs: AdapterInputs<markers::Adapter>,
810 ) -> Result<AdapterId, RequestAdapterError> {
811 profiling::scope!("Instance::request_adapter");
812 api_log!("Instance::request_adapter");
813
814 fn gather<A: HalApi>(
815 _: A,
816 instance: Option<&A::Instance>,
817 inputs: &AdapterInputs<markers::Adapter>,
818 compatible_surface: Option<&Surface>,
819 force_software: bool,
820 device_types: &mut Vec<wgt::DeviceType>,
821 ) -> (Option<Id<markers::Adapter>>, Vec<hal::ExposedAdapter<A>>) {
822 let id = inputs.find(A::VARIANT);
823 match (id, instance) {
824 (Some(id), Some(inst)) => {
825 let mut adapters = unsafe { inst.enumerate_adapters() };
826 if force_software {
827 adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu);
828 }
829 if let Some(surface) = compatible_surface {
830 let surface = &A::surface_as_hal(surface);
831 adapters.retain(|exposed| unsafe {
832 surface.is_some()
835 && exposed
836 .adapter
837 .surface_capabilities(surface.unwrap())
838 .is_some()
839 });
840 }
841 device_types.extend(adapters.iter().map(|ad| ad.info.device_type));
842 (id, adapters)
843 }
844 _ => (None, Vec::new()),
845 }
846 }
847
848 let compatible_surface = desc
849 .compatible_surface
850 .map(|id| {
851 self.surfaces
852 .get(id)
853 .map_err(|_| RequestAdapterError::InvalidSurface(id))
854 })
855 .transpose()?;
856 let compatible_surface = compatible_surface.as_ref().map(|surface| surface.as_ref());
857 let mut device_types = Vec::new();
858
859 #[cfg(vulkan)]
860 let (id_vulkan, adapters_vk) = gather(
861 hal::api::Vulkan,
862 self.instance.vulkan.as_ref(),
863 &inputs,
864 compatible_surface,
865 desc.force_fallback_adapter,
866 &mut device_types,
867 );
868 #[cfg(metal)]
869 let (id_metal, adapters_metal) = gather(
870 hal::api::Metal,
871 self.instance.metal.as_ref(),
872 &inputs,
873 compatible_surface,
874 desc.force_fallback_adapter,
875 &mut device_types,
876 );
877 #[cfg(dx12)]
878 let (id_dx12, adapters_dx12) = gather(
879 hal::api::Dx12,
880 self.instance.dx12.as_ref(),
881 &inputs,
882 compatible_surface,
883 desc.force_fallback_adapter,
884 &mut device_types,
885 );
886 #[cfg(gles)]
887 let (id_gl, adapters_gl) = gather(
888 hal::api::Gles,
889 self.instance.gl.as_ref(),
890 &inputs,
891 compatible_surface,
892 desc.force_fallback_adapter,
893 &mut device_types,
894 );
895
896 if device_types.is_empty() {
897 return Err(RequestAdapterError::NotFound);
898 }
899
900 let (mut integrated, mut discrete, mut virt, mut cpu, mut other) =
901 (None, None, None, None, None);
902
903 for (i, ty) in device_types.into_iter().enumerate() {
904 match ty {
905 wgt::DeviceType::IntegratedGpu => {
906 integrated = integrated.or(Some(i));
907 }
908 wgt::DeviceType::DiscreteGpu => {
909 discrete = discrete.or(Some(i));
910 }
911 wgt::DeviceType::VirtualGpu => {
912 virt = virt.or(Some(i));
913 }
914 wgt::DeviceType::Cpu => {
915 cpu = cpu.or(Some(i));
916 }
917 wgt::DeviceType::Other => {
918 other = other.or(Some(i));
919 }
920 }
921 }
922
923 let preferred_gpu = match desc.power_preference {
924 PowerPreference::LowPower => integrated.or(discrete).or(other).or(virt).or(cpu),
932 PowerPreference::HighPerformance => discrete.or(integrated).or(other).or(virt).or(cpu),
933 PowerPreference::None => {
934 let option_min = |a: Option<usize>, b: Option<usize>| {
935 if let (Some(a), Some(b)) = (a, b) {
936 Some(a.min(b))
937 } else {
938 a.or(b)
939 }
940 };
941 option_min(option_min(discrete, integrated), other)
943 }
944 };
945
946 let mut selected = preferred_gpu.unwrap_or(0);
947 #[cfg(vulkan)]
948 if let Some(id) = self.select(&mut selected, id_vulkan, adapters_vk) {
949 return Ok(id);
950 }
951 #[cfg(metal)]
952 if let Some(id) = self.select(&mut selected, id_metal, adapters_metal) {
953 return Ok(id);
954 }
955 #[cfg(dx12)]
956 if let Some(id) = self.select(&mut selected, id_dx12, adapters_dx12) {
957 return Ok(id);
958 }
959 #[cfg(gles)]
960 if let Some(id) = self.select(&mut selected, id_gl, adapters_gl) {
961 return Ok(id);
962 }
963 let _ = selected;
964
965 log::warn!("Some adapters are present, but enumerating them failed!");
966 Err(RequestAdapterError::NotFound)
967 }
968
969 pub unsafe fn create_adapter_from_hal<A: HalApi>(
973 &self,
974 hal_adapter: hal::ExposedAdapter<A>,
975 input: Option<AdapterId>,
976 ) -> AdapterId {
977 profiling::scope!("Instance::create_adapter_from_hal");
978
979 let fid = A::hub(self).adapters.prepare(input);
980
981 let (id, _adapter): (_, Arc<Adapter<A>>) = match A::VARIANT {
982 #[cfg(vulkan)]
983 Backend::Vulkan => fid.assign(Arc::new(Adapter::new(hal_adapter))),
984 #[cfg(metal)]
985 Backend::Metal => fid.assign(Arc::new(Adapter::new(hal_adapter))),
986 #[cfg(dx12)]
987 Backend::Dx12 => fid.assign(Arc::new(Adapter::new(hal_adapter))),
988 #[cfg(gles)]
989 Backend::Gl => fid.assign(Arc::new(Adapter::new(hal_adapter))),
990 _ => unreachable!(),
991 };
992 resource_log!("Created Adapter {:?}", id);
993 id
994 }
995
996 pub fn adapter_get_info<A: HalApi>(
997 &self,
998 adapter_id: AdapterId,
999 ) -> Result<wgt::AdapterInfo, InvalidAdapter> {
1000 let hub = A::hub(self);
1001
1002 hub.adapters
1003 .get(adapter_id)
1004 .map(|adapter| adapter.raw.info.clone())
1005 .map_err(|_| InvalidAdapter)
1006 }
1007
1008 pub fn adapter_get_texture_format_features<A: HalApi>(
1009 &self,
1010 adapter_id: AdapterId,
1011 format: wgt::TextureFormat,
1012 ) -> Result<wgt::TextureFormatFeatures, InvalidAdapter> {
1013 let hub = A::hub(self);
1014
1015 hub.adapters
1016 .get(adapter_id)
1017 .map(|adapter| adapter.get_texture_format_features(format))
1018 .map_err(|_| InvalidAdapter)
1019 }
1020
1021 pub fn adapter_features<A: HalApi>(
1022 &self,
1023 adapter_id: AdapterId,
1024 ) -> Result<wgt::Features, InvalidAdapter> {
1025 let hub = A::hub(self);
1026
1027 hub.adapters
1028 .get(adapter_id)
1029 .map(|adapter| adapter.raw.features)
1030 .map_err(|_| InvalidAdapter)
1031 }
1032
1033 pub fn adapter_limits<A: HalApi>(
1034 &self,
1035 adapter_id: AdapterId,
1036 ) -> Result<wgt::Limits, InvalidAdapter> {
1037 let hub = A::hub(self);
1038
1039 hub.adapters
1040 .get(adapter_id)
1041 .map(|adapter| adapter.raw.capabilities.limits.clone())
1042 .map_err(|_| InvalidAdapter)
1043 }
1044
1045 pub fn adapter_downlevel_capabilities<A: HalApi>(
1046 &self,
1047 adapter_id: AdapterId,
1048 ) -> Result<wgt::DownlevelCapabilities, InvalidAdapter> {
1049 let hub = A::hub(self);
1050
1051 hub.adapters
1052 .get(adapter_id)
1053 .map(|adapter| adapter.raw.capabilities.downlevel.clone())
1054 .map_err(|_| InvalidAdapter)
1055 }
1056
1057 pub fn adapter_get_presentation_timestamp<A: HalApi>(
1058 &self,
1059 adapter_id: AdapterId,
1060 ) -> Result<wgt::PresentationTimestamp, InvalidAdapter> {
1061 let hub = A::hub(self);
1062
1063 let adapter = hub.adapters.get(adapter_id).map_err(|_| InvalidAdapter)?;
1064
1065 Ok(unsafe { adapter.raw.adapter.get_presentation_timestamp() })
1066 }
1067
1068 pub fn adapter_drop<A: HalApi>(&self, adapter_id: AdapterId) {
1069 profiling::scope!("Adapter::drop");
1070 api_log!("Adapter::drop {adapter_id:?}");
1071
1072 let hub = A::hub(self);
1073 let mut adapters_locked = hub.adapters.write();
1074
1075 let free = match adapters_locked.get(adapter_id) {
1076 Ok(adapter) => Arc::strong_count(adapter) == 1,
1077 Err(_) => true,
1078 };
1079 if free {
1080 hub.adapters
1081 .unregister_locked(adapter_id, &mut *adapters_locked);
1082 }
1083 }
1084}
1085
1086impl Global {
1087 pub fn adapter_request_device<A: HalApi>(
1088 &self,
1089 adapter_id: AdapterId,
1090 desc: &DeviceDescriptor,
1091 trace_path: Option<&std::path::Path>,
1092 device_id_in: Option<DeviceId>,
1093 queue_id_in: Option<QueueId>,
1094 ) -> (DeviceId, QueueId, Option<RequestDeviceError>) {
1095 profiling::scope!("Adapter::request_device");
1096 api_log!("Adapter::request_device");
1097
1098 let hub = A::hub(self);
1099 let device_fid = hub.devices.prepare(device_id_in);
1100 let queue_fid = hub.queues.prepare(queue_id_in);
1101
1102 let error = loop {
1103 let adapter = match hub.adapters.get(adapter_id) {
1104 Ok(adapter) => adapter,
1105 Err(_) => break RequestDeviceError::InvalidAdapter,
1106 };
1107 let (device, mut queue) =
1108 match adapter.create_device_and_queue(desc, self.instance.flags, trace_path) {
1109 Ok((device, queue)) => (device, queue),
1110 Err(e) => break e,
1111 };
1112 let (device_id, _) = device_fid.assign(Arc::new(device));
1113 resource_log!("Created Device {:?}", device_id);
1114
1115 let device = hub.devices.get(device_id).unwrap();
1116 queue.device = Some(device.clone());
1117
1118 let (queue_id, queue) = queue_fid.assign(Arc::new(queue));
1119 resource_log!("Created Queue {:?}", queue_id);
1120
1121 device.set_queue(queue);
1122
1123 return (device_id, queue_id, None);
1124 };
1125
1126 let device_id = device_fid.assign_error(desc.label.borrow_or_default());
1127 let queue_id = queue_fid.assign_error(desc.label.borrow_or_default());
1128 (device_id, queue_id, Some(error))
1129 }
1130
1131 pub unsafe fn create_device_from_hal<A: HalApi>(
1136 &self,
1137 adapter_id: AdapterId,
1138 hal_device: OpenDevice<A>,
1139 desc: &DeviceDescriptor,
1140 trace_path: Option<&std::path::Path>,
1141 device_id_in: Option<DeviceId>,
1142 queue_id_in: Option<QueueId>,
1143 ) -> (DeviceId, QueueId, Option<RequestDeviceError>) {
1144 profiling::scope!("Global::create_device_from_hal");
1145
1146 let hub = A::hub(self);
1147 let devices_fid = hub.devices.prepare(device_id_in);
1148 let queues_fid = hub.queues.prepare(queue_id_in);
1149
1150 let error = loop {
1151 let adapter = match hub.adapters.get(adapter_id) {
1152 Ok(adapter) => adapter,
1153 Err(_) => break RequestDeviceError::InvalidAdapter,
1154 };
1155 let (device, mut queue) = match adapter.create_device_and_queue_from_hal(
1156 hal_device,
1157 desc,
1158 self.instance.flags,
1159 trace_path,
1160 ) {
1161 Ok(device) => device,
1162 Err(e) => break e,
1163 };
1164 let (device_id, _) = devices_fid.assign(Arc::new(device));
1165 resource_log!("Created Device {:?}", device_id);
1166
1167 let device = hub.devices.get(device_id).unwrap();
1168 queue.device = Some(device.clone());
1169
1170 let (queue_id, queue) = queues_fid.assign(Arc::new(queue));
1171 resource_log!("Created Queue {:?}", queue_id);
1172
1173 device.set_queue(queue);
1174
1175 return (device_id, queue_id, None);
1176 };
1177
1178 let device_id = devices_fid.assign_error(desc.label.borrow_or_default());
1179 let queue_id = queues_fid.assign_error(desc.label.borrow_or_default());
1180 (device_id, queue_id, Some(error))
1181 }
1182}
1183
1184pub fn parse_backends_from_comma_list(string: &str) -> Backends {
1197 let mut backends = Backends::empty();
1198 for backend in string.to_lowercase().split(',') {
1199 backends |= match backend.trim() {
1200 "vulkan" | "vk" => Backends::VULKAN,
1201 "dx12" | "d3d12" => Backends::DX12,
1202 "metal" | "mtl" => Backends::METAL,
1203 "opengl" | "gles" | "gl" => Backends::GL,
1204 "webgpu" => Backends::BROWSER_WEBGPU,
1205 b => {
1206 log::warn!("unknown backend string '{}'", b);
1207 continue;
1208 }
1209 }
1210 }
1211
1212 if backends.is_empty() {
1213 log::warn!("no valid backend strings found!");
1214 }
1215
1216 backends
1217}