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