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