1use crate::{
6 backend,
7 device::Device,
8 hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token},
9 id::{AdapterId, DeviceId, SurfaceId},
10 power, span, LifeGuard, PrivateFeatures, Stored, MAX_BIND_GROUPS,
11};
12
13use wgt::{Backend, BackendBit, DeviceDescriptor, PowerPreference, BIND_BUFFER_ALIGNMENT};
14
15#[cfg(feature = "replay")]
16use serde::Deserialize;
17#[cfg(feature = "trace")]
18use serde::Serialize;
19
20use hal::{
21 adapter::{AdapterInfo as HalAdapterInfo, DeviceType as HalDeviceType, PhysicalDevice as _},
22 queue::QueueFamily as _,
23 window::Surface as _,
24 Instance as _,
25};
26use std::fmt::Display;
27
28#[repr(C)]
29#[derive(Clone, Debug, PartialEq, Eq, Hash)]
30#[cfg_attr(feature = "trace", derive(Serialize))]
31#[cfg_attr(feature = "replay", derive(Deserialize))]
32pub struct RequestAdapterOptions {
33 pub power_preference: PowerPreference,
34 pub compatible_surface: Option<SurfaceId>,
35}
36
37impl Default for RequestAdapterOptions {
38 fn default() -> Self {
39 RequestAdapterOptions {
40 power_preference: PowerPreference::Default,
41 compatible_surface: None,
42 }
43 }
44}
45
46#[derive(Debug)]
47pub struct Instance {
48 #[cfg(any(
49 not(any(target_os = "ios", target_os = "macos")),
50 feature = "gfx-backend-vulkan"
51 ))]
52 pub vulkan: Option<gfx_backend_vulkan::Instance>,
53 #[cfg(any(target_os = "ios", target_os = "macos"))]
54 pub metal: Option<gfx_backend_metal::Instance>,
55 #[cfg(windows)]
56 pub dx12: Option<gfx_backend_dx12::Instance>,
57 #[cfg(windows)]
58 pub dx11: Option<gfx_backend_dx11::Instance>,
59}
60
61impl Instance {
62 pub fn new(name: &str, version: u32, backends: BackendBit) -> Self {
63 backends_map! {
64 let map = |(backend, backend_create)| {
65 if backends.contains(backend.into()) {
66 backend_create(name, version).ok()
67 } else {
68 None
69 }
70 };
71 Instance {
72 #[vulkan]
73 vulkan: map((Backend::Vulkan, gfx_backend_vulkan::Instance::create)),
74 #[metal]
75 metal: map((Backend::Metal, gfx_backend_metal::Instance::create)),
76 #[dx12]
77 dx12: map((Backend::Dx12, gfx_backend_dx12::Instance::create)),
78 #[dx11]
79 dx11: map((Backend::Dx11, gfx_backend_dx11::Instance::create)),
80 }
81 }
82 }
83
84 pub(crate) fn destroy_surface(&mut self, surface: Surface) {
85 backends_map! {
86 let map = |(surface_backend, self_backend)| {
87 unsafe {
88 if let Some(suf) = surface_backend {
89 self_backend.as_mut().unwrap().destroy_surface(suf);
90 }
91 }
92 };
93
94 #[vulkan]
95 map((surface.vulkan, &mut self.vulkan)),
96 #[metal]
97 map((surface.metal, &mut self.metal)),
98 #[dx12]
99 map((surface.dx12, &mut self.dx12)),
100 #[dx11]
101 map((surface.dx11, &mut self.dx11)),
102 }
103 }
104}
105
106type GfxSurface<B> = <B as hal::Backend>::Surface;
107
108#[derive(Debug)]
109pub struct Surface {
110 #[cfg(any(
111 not(any(target_os = "ios", target_os = "macos")),
112 feature = "gfx-backend-vulkan"
113 ))]
114 pub vulkan: Option<GfxSurface<backend::Vulkan>>,
115 #[cfg(any(target_os = "ios", target_os = "macos"))]
116 pub metal: Option<GfxSurface<backend::Metal>>,
117 #[cfg(windows)]
118 pub dx12: Option<GfxSurface<backend::Dx12>>,
119 #[cfg(windows)]
120 pub dx11: Option<GfxSurface<backend::Dx11>>,
121}
122
123#[derive(Debug)]
124pub struct Adapter<B: hal::Backend> {
125 pub(crate) raw: hal::adapter::Adapter<B>,
126 features: wgt::Features,
127 limits: wgt::Limits,
128 life_guard: LifeGuard,
129}
130
131impl<B: hal::Backend> Adapter<B> {
132 fn new(raw: hal::adapter::Adapter<B>) -> Self {
133 span!(_guard, INFO, "Adapter::new");
134
135 let adapter_features = raw.physical_device.features();
136
137 let mut features = wgt::Features::default() | wgt::Features::MAPPABLE_PRIMARY_BUFFERS;
138 features.set(
139 wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY,
140 adapter_features.contains(hal::Features::TEXTURE_DESCRIPTOR_ARRAY),
141 );
142 features.set(
143 wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
144 adapter_features.contains(hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING),
145 );
146 features.set(
147 wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
148 adapter_features.contains(hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING),
149 );
150 features.set(
151 wgt::Features::UNSIZED_BINDING_ARRAY,
152 adapter_features.contains(hal::Features::UNSIZED_DESCRIPTOR_ARRAY),
153 );
154 features.set(
155 wgt::Features::MULTI_DRAW_INDIRECT,
156 adapter_features.contains(hal::Features::MULTI_DRAW_INDIRECT),
157 );
158 features.set(
159 wgt::Features::MULTI_DRAW_INDIRECT_COUNT,
160 adapter_features.contains(hal::Features::DRAW_INDIRECT_COUNT),
161 );
162
163 let adapter_limits = raw.physical_device.limits();
164
165 let limits = wgt::Limits {
166 max_bind_groups: (adapter_limits.max_bound_descriptor_sets as u32)
167 .min(MAX_BIND_GROUPS as u32),
168 _non_exhaustive: unsafe { wgt::NonExhaustive::new() },
169 };
170
171 Adapter {
172 raw,
173 features,
174 limits,
175 life_guard: LifeGuard::new(),
176 }
177 }
178}
179
180#[derive(Clone, Debug, PartialEq)]
182#[cfg_attr(feature = "trace", derive(Serialize))]
183#[cfg_attr(feature = "replay", derive(Deserialize))]
184pub struct AdapterInfo {
185 pub name: String,
187 pub vendor: usize,
189 pub device: usize,
191 pub device_type: DeviceType,
193 pub backend: Backend,
195}
196
197impl AdapterInfo {
198 fn from_gfx(adapter_info: HalAdapterInfo, backend: Backend) -> Self {
199 let HalAdapterInfo {
200 name,
201 vendor,
202 device,
203 device_type,
204 } = adapter_info;
205
206 AdapterInfo {
207 name,
208 vendor,
209 device,
210 device_type: device_type.into(),
211 backend,
212 }
213 }
214}
215
216#[derive(Clone, Debug, PartialEq)]
217pub enum RequestDeviceError {
219 UnsupportedFeature(wgt::Features),
221 LimitsExceeded,
223}
224
225impl Display for RequestDeviceError {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 match &self {
228 RequestDeviceError::UnsupportedFeature(features) => write!(
229 f,
230 "Cannot enable features that adapter doesn't support. Unsupported extensions: {:?}",
231 features
232 ),
233 RequestDeviceError::LimitsExceeded => {
234 write!(f, "Some of the requested limits are not supported",)
235 }
236 }
237 }
238}
239
240#[derive(Clone, Debug, PartialEq)]
242#[cfg_attr(feature = "trace", derive(Serialize))]
243#[cfg_attr(feature = "replay", derive(Deserialize))]
244pub enum DeviceType {
245 Other,
247 IntegratedGpu,
249 DiscreteGpu,
251 VirtualGpu,
253 Cpu,
255}
256
257impl From<HalDeviceType> for DeviceType {
258 fn from(device_type: HalDeviceType) -> Self {
259 match device_type {
260 HalDeviceType::Other => Self::Other,
261 HalDeviceType::IntegratedGpu => Self::IntegratedGpu,
262 HalDeviceType::DiscreteGpu => Self::DiscreteGpu,
263 HalDeviceType::VirtualGpu => Self::VirtualGpu,
264 HalDeviceType::Cpu => Self::Cpu,
265 }
266 }
267}
268
269pub enum AdapterInputs<'a, I> {
270 IdSet(&'a [I], fn(&I) -> Backend),
271 Mask(BackendBit, fn(Backend) -> I),
272}
273
274impl<I: Clone> AdapterInputs<'_, I> {
275 fn find(&self, b: Backend) -> Option<I> {
276 match *self {
277 AdapterInputs::IdSet(ids, ref fun) => ids.iter().find(|id| fun(id) == b).cloned(),
278 AdapterInputs::Mask(bits, ref fun) => {
279 if bits.contains(b.into()) {
280 Some(fun(b))
281 } else {
282 None
283 }
284 }
285 }
286 }
287}
288
289impl<G: GlobalIdentityHandlerFactory> Global<G> {
290 #[cfg(feature = "raw-window-handle")]
291 pub fn instance_create_surface(
292 &self,
293 handle: &impl raw_window_handle::HasRawWindowHandle,
294 id_in: Input<G, SurfaceId>,
295 ) -> SurfaceId {
296 span!(_guard, INFO, "Instance::create_surface");
297
298 let surface = unsafe {
299 backends_map! {
300 let map = |inst| {
301 inst
302 .as_ref()
303 .and_then(|inst| inst.create_surface(handle).ok())
304 };
305
306 Surface {
307 #[vulkan]
308 vulkan: map(&self.instance.vulkan),
309 #[metal]
310 metal: map(&self.instance.metal),
311 #[dx12]
312 dx12: map(&self.instance.dx12),
313 #[dx11]
314 dx11: map(&self.instance.dx11),
315 }
316 }
317 };
318
319 let mut token = Token::root();
320 self.surfaces.register_identity(id_in, surface, &mut token)
321 }
322
323 pub fn enumerate_adapters(&self, inputs: AdapterInputs<Input<G, AdapterId>>) -> Vec<AdapterId> {
324 span!(_guard, INFO, "Instance::enumerate_adapters");
325
326 let instance = &self.instance;
327 let mut token = Token::root();
328 let mut adapters = Vec::new();
329
330 backends_map! {
331 let map = |(instance_field, backend, backend_info, backend_hub)| {
332 if let Some(inst) = instance_field {
333 if let Some(id_backend) = inputs.find(backend) {
334 for raw in inst.enumerate_adapters() {
335 let adapter = Adapter::new(raw);
336 log::info!("Adapter {} {:?}", backend_info, adapter.raw.info);
337 adapters.push(backend_hub(self).adapters.register_identity(
338 id_backend.clone(),
339 adapter,
340 &mut token,
341 ));
342 }
343 }
344 }
345 };
346
347 #[vulkan]
348 map((&instance.vulkan, Backend::Vulkan, "Vulkan", backend::Vulkan::hub)),
349 #[metal]
350 map((&instance.metal, Backend::Metal, "Metal", backend::Metal::hub)),
351 #[dx12]
352 map((&instance.dx12, Backend::Dx12, "Dx12", backend::Dx12::hub)),
353 #[dx11]
354 map((&instance.dx11, Backend::Dx11, "Dx11", backend::Dx11::hub)),
355 }
356
357 adapters
358 }
359
360 pub fn pick_adapter(
361 &self,
362 desc: &RequestAdapterOptions,
363 inputs: AdapterInputs<Input<G, AdapterId>>,
364 ) -> Option<AdapterId> {
365 span!(_guard, INFO, "Instance::pick_adapter");
366
367 let instance = &self.instance;
368 let mut token = Token::root();
369 let (surface_guard, mut token) = self.surfaces.read(&mut token);
370 let compatible_surface = desc.compatible_surface.map(|id| &surface_guard[id]);
371 let mut device_types = Vec::new();
372
373 let mut id_vulkan = inputs.find(Backend::Vulkan);
374 let mut id_metal = inputs.find(Backend::Metal);
375 let mut id_dx12 = inputs.find(Backend::Dx12);
376 let mut id_dx11 = inputs.find(Backend::Dx11);
377
378 backends_map! {
379 let map = |(instance_backend, id_backend, surface_backend)| {
380 match instance_backend {
381 Some(ref inst) if id_backend.is_some() => {
382 let mut adapters = inst.enumerate_adapters();
383 if let Some(surface_backend) = compatible_surface.and_then(surface_backend) {
384 adapters.retain(|a| {
385 a.queue_families
386 .iter()
387 .find(|qf| qf.queue_type().supports_graphics())
388 .map_or(false, |qf| surface_backend.supports_queue_family(qf))
389 });
390 }
391 device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone()));
392 adapters
393 }
394 _ => Vec::new(),
395 }
396 };
397
398 #[vulkan]
401 let adapters_vk = map((&instance.vulkan, &id_vulkan, {
402 fn surface_vulkan(surf: &Surface) -> Option<&GfxSurface<backend::Vulkan>> {
403 surf.vulkan.as_ref()
404 }
405 surface_vulkan
406 }));
407 #[metal]
408 let adapters_mtl = map((&instance.metal, &id_metal, {
409 fn surface_metal(surf: &Surface) -> Option<&GfxSurface<backend::Metal>> {
410 surf.metal.as_ref()
411 }
412 surface_metal
413 }));
414 #[dx12]
415 let adapters_dx12 = map((&instance.dx12, &id_dx12, {
416 fn surface_dx12(surf: &Surface) -> Option<&GfxSurface<backend::Dx12>> {
417 surf.dx12.as_ref()
418 }
419 surface_dx12
420 }));
421 #[dx11]
422 let adapters_dx11 = map((&instance.dx11, &id_dx11, {
423 fn surface_dx11(surf: &Surface) -> Option<&GfxSurface<backend::Dx11>> {
424 surf.dx11.as_ref()
425 }
426 surface_dx11
427 }));
428 }
429
430 if device_types.is_empty() {
431 log::warn!("No adapters are available!");
432 return None;
433 }
434
435 let (mut integrated, mut discrete, mut virt, mut other) = (None, None, None, None);
436
437 for (i, ty) in device_types.into_iter().enumerate() {
438 match ty {
439 hal::adapter::DeviceType::IntegratedGpu => {
440 integrated = integrated.or(Some(i));
441 }
442 hal::adapter::DeviceType::DiscreteGpu => {
443 discrete = discrete.or(Some(i));
444 }
445 hal::adapter::DeviceType::VirtualGpu => {
446 virt = virt.or(Some(i));
447 }
448 _ => {
449 other = other.or(Some(i));
450 }
451 }
452 }
453
454 let preferred_gpu = match desc.power_preference {
455 PowerPreference::Default => match power::is_battery_discharging() {
456 Ok(false) => discrete.or(integrated).or(other).or(virt),
457 Ok(true) => integrated.or(discrete).or(other).or(virt),
458 Err(err) => {
459 log::debug!(
460 "Power info unavailable, preferring integrated gpu ({})",
461 err
462 );
463 integrated.or(discrete).or(other).or(virt)
464 }
465 },
466 PowerPreference::LowPower => integrated.or(other).or(discrete).or(virt),
467 PowerPreference::HighPerformance => discrete.or(other).or(integrated).or(virt),
468 };
469
470 let mut selected = preferred_gpu.unwrap_or(0);
471
472 backends_map! {
473 let map = |(info_adapter, id_backend, mut adapters_backend, backend_hub)| {
474 if selected < adapters_backend.len() {
475 let adapter = Adapter::new(adapters_backend.swap_remove(selected));
476 log::info!("Adapter {} {:?}", info_adapter, adapter.raw.info);
477 let id = backend_hub(self).adapters.register_identity(
478 id_backend.take().unwrap(),
479 adapter,
480 &mut token,
481 );
482 return Some(id);
483 }
484 selected -= adapters_backend.len();
485 };
486
487 #[vulkan]
488 map(("Vulkan", &mut id_vulkan, adapters_vk, backend::Vulkan::hub)),
489 #[metal]
490 map(("Metal", &mut id_metal, adapters_mtl, backend::Metal::hub)),
491 #[dx12]
492 map(("Dx12", &mut id_dx12, adapters_dx12, backend::Dx12::hub)),
493 #[dx11]
494 map(("Dx11", &mut id_dx11, adapters_dx11, backend::Dx11::hub)),
495 }
496
497 let _ = (
498 selected,
499 id_vulkan.take(),
500 id_metal.take(),
501 id_dx12.take(),
502 id_dx11.take(),
503 );
504 log::warn!("Some adapters are present, but enumerating them failed!");
505 None
506 }
507
508 pub fn adapter_get_info<B: GfxBackend>(&self, adapter_id: AdapterId) -> AdapterInfo {
509 span!(_guard, INFO, "Adapter::get_info");
510
511 let hub = B::hub(self);
512 let mut token = Token::root();
513 let (adapter_guard, _) = hub.adapters.read(&mut token);
514 let adapter = &adapter_guard[adapter_id];
515 AdapterInfo::from_gfx(adapter.raw.info.clone(), adapter_id.backend())
516 }
517
518 pub fn adapter_features<B: GfxBackend>(&self, adapter_id: AdapterId) -> wgt::Features {
519 span!(_guard, INFO, "Adapter::features");
520
521 let hub = B::hub(self);
522 let mut token = Token::root();
523 let (adapter_guard, _) = hub.adapters.read(&mut token);
524 let adapter = &adapter_guard[adapter_id];
525
526 adapter.features
527 }
528
529 pub fn adapter_limits<B: GfxBackend>(&self, adapter_id: AdapterId) -> wgt::Limits {
530 span!(_guard, INFO, "Adapter::limits");
531
532 let hub = B::hub(self);
533 let mut token = Token::root();
534 let (adapter_guard, _) = hub.adapters.read(&mut token);
535 let adapter = &adapter_guard[adapter_id];
536
537 adapter.limits.clone()
538 }
539
540 pub fn adapter_destroy<B: GfxBackend>(&self, adapter_id: AdapterId) {
541 span!(_guard, INFO, "Adapter::drop");
542
543 let hub = B::hub(self);
544 let mut token = Token::root();
545 let (mut guard, _) = hub.adapters.write(&mut token);
546
547 if guard[adapter_id]
548 .life_guard
549 .ref_count
550 .take()
551 .unwrap()
552 .load()
553 == 1
554 {
555 hub.adapters.free_id(adapter_id);
556 let _adapter = guard.remove(adapter_id).unwrap();
557 }
558 }
559}
560
561impl<G: GlobalIdentityHandlerFactory> Global<G> {
562 pub fn adapter_request_device<B: GfxBackend>(
563 &self,
564 adapter_id: AdapterId,
565 desc: &DeviceDescriptor,
566 trace_path: Option<&std::path::Path>,
567 id_in: Input<G, DeviceId>,
568 ) -> Result<DeviceId, RequestDeviceError> {
569 span!(_guard, INFO, "Adapter::request_device");
570
571 let hub = B::hub(self);
572 let mut token = Token::root();
573 let device = {
574 let (adapter_guard, _) = hub.adapters.read(&mut token);
575 let adapter = &adapter_guard[adapter_id];
576 let phd = &adapter.raw.physical_device;
577
578 if !adapter.features.contains(desc.features) {
580 return Err(RequestDeviceError::UnsupportedFeature(
581 desc.features - adapter.features,
582 ));
583 }
584
585 if desc
587 .features
588 .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
589 && adapter.raw.info.device_type == hal::adapter::DeviceType::DiscreteGpu
590 {
591 log::warn!("Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. This is a massive performance footgun and likely not what you wanted");
592 }
593
594 let available_features = adapter.raw.physical_device.features();
595
596 let wishful_features = hal::Features::VERTEX_STORES_AND_ATOMICS
598 | hal::Features::FRAGMENT_STORES_AND_ATOMICS
599 | hal::Features::NDC_Y_UP
600 | hal::Features::INDEPENDENT_BLENDING
601 | hal::Features::SAMPLER_ANISOTROPY;
602 let mut enabled_features = available_features & wishful_features;
603 if enabled_features != wishful_features {
604 log::warn!(
605 "Missing features: {:?}",
606 wishful_features - enabled_features
607 );
608 }
609
610 enabled_features.set(
612 hal::Features::TEXTURE_DESCRIPTOR_ARRAY,
613 adapter
614 .features
615 .contains(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY),
616 );
617 enabled_features.set(
618 hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING,
619 adapter
620 .features
621 .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING),
622 );
623 enabled_features.set(
624 hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING,
625 adapter
626 .features
627 .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING),
628 );
629 enabled_features.set(
630 hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING,
631 adapter
632 .features
633 .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING),
634 );
635 enabled_features.set(
636 hal::Features::UNSIZED_DESCRIPTOR_ARRAY,
637 adapter
638 .features
639 .contains(wgt::Features::UNSIZED_BINDING_ARRAY),
640 );
641 enabled_features.set(
642 hal::Features::MULTI_DRAW_INDIRECT,
643 adapter
644 .features
645 .contains(wgt::Features::MULTI_DRAW_INDIRECT),
646 );
647 enabled_features.set(
648 hal::Features::DRAW_INDIRECT_COUNT,
649 adapter
650 .features
651 .contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT),
652 );
653
654 let family = adapter
655 .raw
656 .queue_families
657 .iter()
658 .find(|family| family.queue_type().supports_graphics())
659 .unwrap();
660 let mut gpu = unsafe { phd.open(&[(family, &[1.0])], enabled_features).unwrap() };
661
662 let limits = phd.limits();
663 assert_eq!(
664 0,
665 BIND_BUFFER_ALIGNMENT % limits.min_storage_buffer_offset_alignment,
666 "Adapter storage buffer offset alignment not compatible with WGPU"
667 );
668 assert_eq!(
669 0,
670 BIND_BUFFER_ALIGNMENT % limits.min_uniform_buffer_offset_alignment,
671 "Adapter uniform buffer offset alignment not compatible with WGPU"
672 );
673 if limits.max_bound_descriptor_sets == 0 {
674 log::warn!("max_bind_groups limit is missing");
675 } else {
676 if adapter.limits.max_bind_groups < desc.limits.max_bind_groups {
677 return Err(RequestDeviceError::LimitsExceeded);
678 }
679 }
680
681 let mem_props = phd.memory_properties();
682 if !desc.shader_validation {
683 log::warn!("Shader validation is disabled");
684 }
685 let private_features = PrivateFeatures {
686 shader_validation: desc.shader_validation,
687 anisotropic_filtering: enabled_features.contains(hal::Features::SAMPLER_ANISOTROPY),
688 texture_d24_s8: phd
689 .format_properties(Some(hal::format::Format::D24UnormS8Uint))
690 .optimal_tiling
691 .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
692 };
693
694 Device::new(
695 gpu.device,
696 Stored {
697 value: adapter_id,
698 ref_count: adapter.life_guard.add_ref(),
699 },
700 gpu.queue_groups.swap_remove(0),
701 mem_props,
702 limits,
703 private_features,
704 desc,
705 trace_path,
706 )
707 };
708
709 Ok(hub.devices.register_identity(id_in, device, &mut token))
710 }
711}