1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use crate::{
6     backend, conv,
7     device::{Device, DeviceDescriptor},
8     hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token},
9     id::{AdapterId, DeviceId, SurfaceId, Valid},
10     LabelHelpers, LifeGuard, PrivateFeatures, Stored, DOWNLEVEL_WARNING_MESSAGE, MAX_BIND_GROUPS,
11 };
12 
13 use wgt::{Backend, BackendBit, PowerPreference, BIND_BUFFER_ALIGNMENT};
14 
15 use hal::{
16     adapter::PhysicalDevice as _, queue::QueueFamily as _, window::Surface as _, Instance as _,
17 };
18 use thiserror::Error;
19 
20 /// Size that is guaranteed to be available in push constants.
21 ///
22 /// This is needed because non-vulkan backends might not
23 /// provide a push-constant size limit.
24 const MIN_PUSH_CONSTANT_SIZE: u32 = 128;
25 
26 pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
27 
28 #[derive(Debug)]
29 pub struct Instance {
30     #[cfg(vulkan)]
31     pub vulkan: Option<gfx_backend_vulkan::Instance>,
32     #[cfg(metal)]
33     pub metal: Option<gfx_backend_metal::Instance>,
34     #[cfg(dx12)]
35     pub dx12: Option<gfx_backend_dx12::Instance>,
36     #[cfg(dx11)]
37     pub dx11: Option<gfx_backend_dx11::Instance>,
38     #[cfg(gl)]
39     pub gl: Option<gfx_backend_gl::Instance>,
40 }
41 
42 impl Instance {
new(name: &str, version: u32, backends: BackendBit) -> Self43     pub fn new(name: &str, version: u32, backends: BackendBit) -> Self {
44         backends_map! {
45             let map = |(backend, backend_create)| {
46                 if backends.contains(backend.into()) {
47                     backend_create(name, version).ok()
48                 } else {
49                     None
50                 }
51             };
52             Self {
53                 #[cfg(vulkan)]
54                 vulkan: map((Backend::Vulkan, gfx_backend_vulkan::Instance::create)),
55                 #[cfg(metal)]
56                 metal: map((Backend::Metal, gfx_backend_metal::Instance::create)),
57                 #[cfg(dx12)]
58                 dx12: map((Backend::Dx12, gfx_backend_dx12::Instance::create)),
59                 #[cfg(dx11)]
60                 dx11: map((Backend::Dx11, gfx_backend_dx11::Instance::create)),
61                 #[cfg(gl)]
62                 gl: map((Backend::Gl, gfx_backend_gl::Instance::create)),
63             }
64         }
65     }
66 
destroy_surface(&self, surface: Surface)67     pub(crate) fn destroy_surface(&self, surface: Surface) {
68         backends_map! {
69             let map = |(surface_backend, self_backend)| {
70                 unsafe {
71                     if let Some(suf) = surface_backend {
72                         self_backend.as_ref().unwrap().destroy_surface(suf);
73                     }
74                 }
75             };
76 
77             #[cfg(vulkan)]
78             map((surface.vulkan, &self.vulkan)),
79             #[cfg(metal)]
80             map((surface.metal, &self.metal)),
81             #[cfg(dx12)]
82             map((surface.dx12, &self.dx12)),
83             #[cfg(dx11)]
84             map((surface.dx11, &self.dx11)),
85             #[cfg(gl)]
86             map((surface.gl, &self.gl)),
87         }
88     }
89 }
90 
91 type GfxSurface<B> = <B as hal::Backend>::Surface;
92 
93 #[derive(Debug)]
94 pub struct Surface {
95     #[cfg(vulkan)]
96     pub vulkan: Option<GfxSurface<backend::Vulkan>>,
97     #[cfg(metal)]
98     pub metal: Option<GfxSurface<backend::Metal>>,
99     #[cfg(dx12)]
100     pub dx12: Option<GfxSurface<backend::Dx12>>,
101     #[cfg(dx11)]
102     pub dx11: Option<GfxSurface<backend::Dx11>>,
103     #[cfg(gl)]
104     pub gl: Option<GfxSurface<backend::Gl>>,
105 }
106 
107 impl crate::hub::Resource for Surface {
108     const TYPE: &'static str = "Surface";
109 
life_guard(&self) -> &LifeGuard110     fn life_guard(&self) -> &LifeGuard {
111         unreachable!()
112     }
113 
label(&self) -> &str114     fn label(&self) -> &str {
115         "<Surface>"
116     }
117 }
118 
119 const FEATURE_MAP: &[(wgt::Features, hal::Features)] = &[
120     (wgt::Features::DEPTH_CLAMPING, hal::Features::DEPTH_CLAMP),
121     (
122         wgt::Features::TEXTURE_COMPRESSION_BC,
123         hal::Features::FORMAT_BC,
124     ),
125     (
126         wgt::Features::TEXTURE_COMPRESSION_ETC2,
127         hal::Features::FORMAT_ETC2,
128     ),
129     (
130         wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR,
131         hal::Features::FORMAT_ASTC_LDR,
132     ),
133     (
134         wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY,
135         hal::Features::TEXTURE_DESCRIPTOR_ARRAY,
136     ),
137     (
138         wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
139         hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING,
140     ),
141     (
142         wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
143         hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING,
144     ),
145     (
146         wgt::Features::UNSIZED_BINDING_ARRAY,
147         hal::Features::UNSIZED_DESCRIPTOR_ARRAY,
148     ),
149     (
150         wgt::Features::MULTI_DRAW_INDIRECT,
151         hal::Features::MULTI_DRAW_INDIRECT,
152     ),
153     (
154         wgt::Features::MULTI_DRAW_INDIRECT_COUNT,
155         hal::Features::DRAW_INDIRECT_COUNT,
156     ),
157     (
158         wgt::Features::NON_FILL_POLYGON_MODE,
159         hal::Features::NON_FILL_POLYGON_MODE,
160     ),
161     (
162         wgt::Features::PIPELINE_STATISTICS_QUERY,
163         hal::Features::PIPELINE_STATISTICS_QUERY,
164     ),
165     (wgt::Features::SHADER_FLOAT64, hal::Features::SHADER_FLOAT64),
166     (
167         wgt::Features::CONSERVATIVE_RASTERIZATION,
168         hal::Features::CONSERVATIVE_RASTERIZATION,
169     ),
170     (
171         wgt::Features::BUFFER_BINDING_ARRAY,
172         hal::Features::BUFFER_DESCRIPTOR_ARRAY,
173     ),
174     (
175         wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING,
176         hal::Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING,
177     ),
178     (
179         wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
180         hal::Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING,
181     ),
182     (
183         wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING,
184         hal::Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING,
185     ),
186     (
187         wgt::Features::STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
188         hal::Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING,
189     ),
190     (
191         wgt::Features::VERTEX_WRITABLE_STORAGE,
192         hal::Features::VERTEX_STORES_AND_ATOMICS,
193     ),
194     (
195         wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER,
196         hal::Features::SAMPLER_BORDER_COLOR,
197     ),
198 ];
199 
200 #[derive(Debug)]
201 pub struct Adapter<B: hal::Backend> {
202     pub(crate) raw: hal::adapter::Adapter<B>,
203     features: wgt::Features,
204     pub(crate) private_features: PrivateFeatures,
205     limits: wgt::Limits,
206     downlevel: wgt::DownlevelProperties,
207     life_guard: LifeGuard,
208 }
209 
210 impl<B: GfxBackend> Adapter<B> {
new(raw: hal::adapter::Adapter<B>) -> Self211     fn new(raw: hal::adapter::Adapter<B>) -> Self {
212         profiling::scope!("new", "Adapter");
213 
214         let adapter_features = raw.physical_device.features();
215         let properties = raw.physical_device.properties();
216 
217         let mut features = wgt::Features::default()
218             | wgt::Features::MAPPABLE_PRIMARY_BUFFERS
219             | wgt::Features::PUSH_CONSTANTS
220             | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
221             | wgt::Features::CLEAR_COMMANDS;
222         for &(hi, lo) in FEATURE_MAP.iter() {
223             features.set(hi, adapter_features.contains(lo));
224         }
225         features.set(
226             wgt::Features::TIMESTAMP_QUERY,
227             properties.limits.timestamp_compute_and_graphics,
228         );
229 
230         let private_features = PrivateFeatures {
231             anisotropic_filtering: adapter_features.contains(hal::Features::SAMPLER_ANISOTROPY),
232             texture_d24: raw
233                 .physical_device
234                 .format_properties(Some(hal::format::Format::X8D24Unorm))
235                 .optimal_tiling
236                 .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
237             texture_d24_s8: raw
238                 .physical_device
239                 .format_properties(Some(hal::format::Format::D24UnormS8Uint))
240                 .optimal_tiling
241                 .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
242         };
243 
244         let default_limits = wgt::Limits::default();
245 
246         // All these casts to u32 are safe as the underlying vulkan types are u32s.
247         // If another backend provides larger limits than u32, we need to clamp them to u32::MAX.
248         // TODO: fix all gfx-hal backends to produce limits we care about, and remove .max
249         let desc_limits = &properties.limits.descriptor_limits;
250         let limits = wgt::Limits {
251             max_texture_dimension_1d: properties
252                 .limits
253                 .max_image_1d_size
254                 .max(default_limits.max_texture_dimension_1d),
255             max_texture_dimension_2d: properties
256                 .limits
257                 .max_image_2d_size
258                 .max(default_limits.max_texture_dimension_1d),
259             max_texture_dimension_3d: properties
260                 .limits
261                 .max_image_3d_size
262                 .max(default_limits.max_texture_dimension_1d),
263             max_texture_array_layers: (properties.limits.max_image_array_layers as u32)
264                 .max(default_limits.max_texture_array_layers),
265             max_bind_groups: (properties.limits.max_bound_descriptor_sets as u32)
266                 .min(MAX_BIND_GROUPS as u32)
267                 .max(default_limits.max_bind_groups),
268             max_dynamic_uniform_buffers_per_pipeline_layout: desc_limits
269                 .max_descriptor_set_uniform_buffers_dynamic
270                 .max(default_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
271             max_dynamic_storage_buffers_per_pipeline_layout: desc_limits
272                 .max_descriptor_set_storage_buffers_dynamic
273                 .max(default_limits.max_dynamic_storage_buffers_per_pipeline_layout),
274             max_sampled_textures_per_shader_stage: desc_limits
275                 .max_per_stage_descriptor_sampled_images
276                 .max(default_limits.max_sampled_textures_per_shader_stage),
277             max_samplers_per_shader_stage: desc_limits
278                 .max_per_stage_descriptor_samplers
279                 .max(default_limits.max_samplers_per_shader_stage),
280             max_storage_buffers_per_shader_stage: desc_limits
281                 .max_per_stage_descriptor_storage_buffers
282                 .max(default_limits.max_storage_buffers_per_shader_stage),
283             max_storage_textures_per_shader_stage: desc_limits
284                 .max_per_stage_descriptor_storage_images
285                 .max(default_limits.max_storage_textures_per_shader_stage),
286             max_uniform_buffers_per_shader_stage: desc_limits
287                 .max_per_stage_descriptor_uniform_buffers
288                 .max(default_limits.max_uniform_buffers_per_shader_stage),
289             max_uniform_buffer_binding_size: (properties.limits.max_uniform_buffer_range as u32)
290                 .max(default_limits.max_uniform_buffer_binding_size),
291             max_storage_buffer_binding_size: (properties.limits.max_storage_buffer_range as u32)
292                 .max(default_limits.max_storage_buffer_binding_size),
293             max_vertex_buffers: (properties.limits.max_vertex_input_bindings as u32)
294                 .max(default_limits.max_vertex_buffers),
295             max_vertex_attributes: (properties.limits.max_vertex_input_attributes as u32)
296                 .max(default_limits.max_vertex_attributes),
297             max_vertex_buffer_array_stride: (properties.limits.max_vertex_input_binding_stride
298                 as u32)
299                 .max(default_limits.max_vertex_buffer_array_stride),
300             max_push_constant_size: (properties.limits.max_push_constants_size as u32)
301                 .max(MIN_PUSH_CONSTANT_SIZE), // As an extension, the default is always 0, so define a separate minimum.
302         };
303 
304         let mut downlevel_flags = wgt::DownlevelFlags::empty();
305         downlevel_flags.set(
306             wgt::DownlevelFlags::COMPUTE_SHADERS,
307             properties.downlevel.compute_shaders,
308         );
309         downlevel_flags.set(
310             wgt::DownlevelFlags::STORAGE_IMAGES,
311             properties.downlevel.storage_images,
312         );
313         downlevel_flags.set(
314             wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL,
315             properties.downlevel.read_only_depth_stencil,
316         );
317         downlevel_flags.set(
318             wgt::DownlevelFlags::DEVICE_LOCAL_IMAGE_COPIES,
319             properties.downlevel.device_local_image_copies,
320         );
321         downlevel_flags.set(
322             wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES,
323             properties.downlevel.non_power_of_two_mipmapped_textures,
324         );
325         downlevel_flags.set(
326             wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES,
327             adapter_features.contains(hal::Features::IMAGE_CUBE_ARRAY),
328         );
329         downlevel_flags.set(
330             wgt::DownlevelFlags::ANISOTROPIC_FILTERING,
331             private_features.anisotropic_filtering,
332         );
333 
334         let downlevel = wgt::DownlevelProperties {
335             flags: downlevel_flags,
336             shader_model: match properties.downlevel.shader_model {
337                 hal::DownlevelShaderModel::ShaderModel2 => wgt::ShaderModel::Sm2,
338                 hal::DownlevelShaderModel::ShaderModel4 => wgt::ShaderModel::Sm4,
339                 hal::DownlevelShaderModel::ShaderModel5 => wgt::ShaderModel::Sm5,
340             },
341         };
342 
343         Self {
344             raw,
345             features,
346             private_features,
347             limits,
348             downlevel,
349             life_guard: LifeGuard::new("<Adapter>"),
350         }
351     }
352 
get_swap_chain_preferred_format( &self, surface: &mut Surface, ) -> Result<wgt::TextureFormat, GetSwapChainPreferredFormatError>353     pub fn get_swap_chain_preferred_format(
354         &self,
355         surface: &mut Surface,
356     ) -> Result<wgt::TextureFormat, GetSwapChainPreferredFormatError> {
357         let formats = {
358             let surface = B::get_surface_mut(surface);
359             let queue_family = &self.raw.queue_families[0];
360             if !surface.supports_queue_family(queue_family) {
361                 return Err(GetSwapChainPreferredFormatError::UnsupportedQueueFamily);
362             }
363             surface.supported_formats(&self.raw.physical_device)
364         };
365         if let Some(formats) = formats {
366             // Check the four formats mentioned in the WebGPU spec:
367             // Bgra8UnormSrgb, Rgba8UnormSrgb, Bgra8Unorm, Rgba8Unorm
368             // Also, prefer sRGB over linear as it is better in
369             // representing perceived colors.
370             if formats.contains(&hal::format::Format::Bgra8Srgb) {
371                 return Ok(wgt::TextureFormat::Bgra8UnormSrgb);
372             }
373             if formats.contains(&hal::format::Format::Rgba8Srgb) {
374                 return Ok(wgt::TextureFormat::Rgba8UnormSrgb);
375             }
376             if formats.contains(&hal::format::Format::Bgra8Unorm) {
377                 return Ok(wgt::TextureFormat::Bgra8Unorm);
378             }
379             if formats.contains(&hal::format::Format::Rgba8Unorm) {
380                 return Ok(wgt::TextureFormat::Rgba8Unorm);
381             }
382             return Err(GetSwapChainPreferredFormatError::NotFound);
383         }
384 
385         // If no formats were returned, use Bgra8UnormSrgb
386         Ok(wgt::TextureFormat::Bgra8UnormSrgb)
387     }
388 
get_texture_format_features( &self, format: wgt::TextureFormat, ) -> wgt::TextureFormatFeatures389     pub(crate) fn get_texture_format_features(
390         &self,
391         format: wgt::TextureFormat,
392     ) -> wgt::TextureFormatFeatures {
393         let texture_format_properties = self
394             .raw
395             .physical_device
396             .format_properties(Some(conv::map_texture_format(
397                 format,
398                 self.private_features,
399             )))
400             .optimal_tiling;
401 
402         let mut allowed_usages = format.describe().guaranteed_format_features.allowed_usages;
403         if texture_format_properties.contains(hal::format::ImageFeature::SAMPLED) {
404             allowed_usages |= wgt::TextureUsage::SAMPLED;
405         }
406         if texture_format_properties.contains(hal::format::ImageFeature::STORAGE) {
407             allowed_usages |= wgt::TextureUsage::STORAGE;
408         }
409         if texture_format_properties.contains(hal::format::ImageFeature::COLOR_ATTACHMENT) {
410             allowed_usages |= wgt::TextureUsage::RENDER_ATTACHMENT;
411         }
412         if texture_format_properties.contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT) {
413             allowed_usages |= wgt::TextureUsage::RENDER_ATTACHMENT;
414         }
415 
416         let mut flags = wgt::TextureFormatFeatureFlags::empty();
417         if texture_format_properties.contains(hal::format::ImageFeature::STORAGE_ATOMIC) {
418             flags |= wgt::TextureFormatFeatureFlags::STORAGE_ATOMICS;
419         }
420         if texture_format_properties.contains(hal::format::ImageFeature::STORAGE_READ_WRITE) {
421             flags |= wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE;
422         }
423 
424         let filterable =
425             texture_format_properties.contains(hal::format::ImageFeature::SAMPLED_LINEAR);
426 
427         wgt::TextureFormatFeatures {
428             allowed_usages,
429             flags,
430             filterable,
431         }
432     }
433 
create_device( &self, self_id: AdapterId, desc: &DeviceDescriptor, trace_path: Option<&std::path::Path>, ) -> Result<Device<B>, RequestDeviceError>434     fn create_device(
435         &self,
436         self_id: AdapterId,
437         desc: &DeviceDescriptor,
438         trace_path: Option<&std::path::Path>,
439     ) -> Result<Device<B>, RequestDeviceError> {
440         // Verify all features were exposed by the adapter
441         if !self.features.contains(desc.features) {
442             return Err(RequestDeviceError::UnsupportedFeature(
443                 desc.features - self.features,
444             ));
445         }
446 
447         if !self.downlevel.is_webgpu_compliant() {
448             log::warn!("{}", DOWNLEVEL_WARNING_MESSAGE);
449         }
450 
451         // Verify feature preconditions
452         if desc
453             .features
454             .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
455             && self.raw.info.device_type == hal::adapter::DeviceType::DiscreteGpu
456         {
457             log::warn!("Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. This is a massive performance footgun and likely not what you wanted");
458         }
459 
460         let phd = &self.raw.physical_device;
461         let available_features = phd.features();
462 
463         // Check features that are always needed
464         let wishful_features = hal::Features::ROBUST_BUFFER_ACCESS
465             | hal::Features::FRAGMENT_STORES_AND_ATOMICS
466             | hal::Features::NDC_Y_UP
467             | hal::Features::INDEPENDENT_BLENDING
468             | hal::Features::SAMPLER_ANISOTROPY
469             | hal::Features::IMAGE_CUBE_ARRAY
470             | hal::Features::SAMPLE_RATE_SHADING;
471         let mut enabled_features = available_features & wishful_features;
472         if enabled_features != wishful_features {
473             log::warn!(
474                 "Missing internal features: {:?}",
475                 wishful_features - enabled_features
476             );
477         }
478 
479         // Enable low-level features
480         for &(hi, lo) in FEATURE_MAP.iter() {
481             enabled_features.set(lo, desc.features.contains(hi));
482         }
483 
484         let family = self
485             .raw
486             .queue_families
487             .iter()
488             .find(|family| family.queue_type().supports_graphics())
489             .ok_or(RequestDeviceError::NoGraphicsQueue)?;
490 
491         let mut gpu =
492             unsafe { phd.open(&[(family, &[1.0])], enabled_features) }.map_err(|err| {
493                 use hal::device::CreationError::*;
494                 match err {
495                     DeviceLost => RequestDeviceError::DeviceLost,
496                     InitializationFailed => RequestDeviceError::Internal,
497                     OutOfMemory(_) => RequestDeviceError::OutOfMemory,
498                     _ => panic!("failed to create `gfx-hal` device: {}", err),
499                 }
500             })?;
501 
502         if let Some(_) = desc.label {
503             //TODO
504         }
505 
506         let limits = phd.properties().limits;
507         assert_eq!(
508             0,
509             BIND_BUFFER_ALIGNMENT % limits.min_storage_buffer_offset_alignment,
510             "Adapter storage buffer offset alignment not compatible with WGPU"
511         );
512         assert_eq!(
513             0,
514             BIND_BUFFER_ALIGNMENT % limits.min_uniform_buffer_offset_alignment,
515             "Adapter uniform buffer offset alignment not compatible with WGPU"
516         );
517         if self.limits < desc.limits {
518             return Err(RequestDeviceError::LimitsExceeded);
519         }
520 
521         let mem_props = phd.memory_properties();
522 
523         Device::new(
524             gpu.device,
525             Stored {
526                 value: Valid(self_id),
527                 ref_count: self.life_guard.add_ref(),
528             },
529             gpu.queue_groups.swap_remove(0),
530             mem_props,
531             limits,
532             self.private_features,
533             self.downlevel,
534             desc,
535             trace_path,
536         )
537         .or(Err(RequestDeviceError::OutOfMemory))
538     }
539 }
540 
541 impl<B: hal::Backend> crate::hub::Resource for Adapter<B> {
542     const TYPE: &'static str = "Adapter";
543 
life_guard(&self) -> &LifeGuard544     fn life_guard(&self) -> &LifeGuard {
545         &self.life_guard
546     }
547 }
548 
549 #[derive(Clone, Debug, Error)]
550 pub enum GetSwapChainPreferredFormatError {
551     #[error("no suitable format found")]
552     NotFound,
553     #[error("invalid adapter")]
554     InvalidAdapter,
555     #[error("invalid surface")]
556     InvalidSurface,
557     #[error("surface does not support the adapter's queue family")]
558     UnsupportedQueueFamily,
559 }
560 
561 #[derive(Clone, Debug, Error)]
562 /// Error when requesting a device from the adaptor
563 pub enum RequestDeviceError {
564     #[error("parent adapter is invalid")]
565     InvalidAdapter,
566     #[error("connection to device was lost during initialization")]
567     DeviceLost,
568     #[error("device initialization failed due to implementation specific errors")]
569     Internal,
570     #[error("some of the requested device limits are not supported")]
571     LimitsExceeded,
572     #[error("device has no queue supporting graphics")]
573     NoGraphicsQueue,
574     #[error("not enough memory left")]
575     OutOfMemory,
576     #[error("unsupported features were requested: {0:?}")]
577     UnsupportedFeature(wgt::Features),
578 }
579 
580 pub enum AdapterInputs<'a, I> {
581     IdSet(&'a [I], fn(&I) -> Backend),
582     Mask(BackendBit, fn(Backend) -> I),
583 }
584 
585 impl<I: Clone> AdapterInputs<'_, I> {
find(&self, b: Backend) -> Option<I>586     fn find(&self, b: Backend) -> Option<I> {
587         match *self {
588             Self::IdSet(ids, ref fun) => ids.iter().find(|id| fun(id) == b).cloned(),
589             Self::Mask(bits, ref fun) => {
590                 if bits.contains(b.into()) {
591                     Some(fun(b))
592                 } else {
593                     None
594                 }
595             }
596         }
597     }
598 }
599 
600 #[derive(Clone, Debug, Error)]
601 #[error("adapter is invalid")]
602 pub struct InvalidAdapter;
603 
604 #[derive(Clone, Debug, Error)]
605 pub enum RequestAdapterError {
606     #[error("no suitable adapter found")]
607     NotFound,
608     #[error("surface {0:?} is invalid")]
609     InvalidSurface(SurfaceId),
610 }
611 
612 impl<G: GlobalIdentityHandlerFactory> Global<G> {
613     #[cfg(feature = "raw-window-handle")]
instance_create_surface( &self, handle: &impl raw_window_handle::HasRawWindowHandle, id_in: Input<G, SurfaceId>, ) -> SurfaceId614     pub fn instance_create_surface(
615         &self,
616         handle: &impl raw_window_handle::HasRawWindowHandle,
617         id_in: Input<G, SurfaceId>,
618     ) -> SurfaceId {
619         profiling::scope!("create_surface", "Instance");
620 
621         let surface = unsafe {
622             backends_map! {
623                 let map = |inst| {
624                     inst
625                     .as_ref()
626                     .and_then(|inst| inst.create_surface(handle).map_err(|e| {
627                         log::warn!("Error: {:?}", e);
628                     }).ok())
629                 };
630 
631                 Surface {
632                     #[cfg(vulkan)]
633                     vulkan: map(&self.instance.vulkan),
634                     #[cfg(metal)]
635                     metal: map(&self.instance.metal),
636                     #[cfg(dx12)]
637                     dx12: map(&self.instance.dx12),
638                     #[cfg(dx11)]
639                     dx11: map(&self.instance.dx11),
640                     #[cfg(gl)]
641                     gl: map(&self.instance.gl),
642                 }
643             }
644         };
645 
646         let mut token = Token::root();
647         let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
648         id.0
649     }
650 
651     #[cfg(metal)]
instance_create_surface_metal( &self, layer: *mut std::ffi::c_void, id_in: Input<G, SurfaceId>, ) -> SurfaceId652     pub fn instance_create_surface_metal(
653         &self,
654         layer: *mut std::ffi::c_void,
655         id_in: Input<G, SurfaceId>,
656     ) -> SurfaceId {
657         profiling::scope!("create_surface_metal", "Instance");
658 
659         let surface = Surface {
660             metal: self.instance.metal.as_ref().map(|inst| {
661                 // we don't want to link to metal-rs for this
662                 #[allow(clippy::transmute_ptr_to_ref)]
663                 inst.create_surface_from_layer(unsafe { std::mem::transmute(layer) })
664             }),
665         };
666 
667         let mut token = Token::root();
668         let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
669         id.0
670     }
671 
surface_drop(&self, id: SurfaceId)672     pub fn surface_drop(&self, id: SurfaceId) {
673         profiling::scope!("drop", "Surface");
674         let mut token = Token::root();
675         let (surface, _) = self.surfaces.unregister(id, &mut token);
676         self.instance.destroy_surface(surface.unwrap());
677     }
678 
enumerate_adapters(&self, inputs: AdapterInputs<Input<G, AdapterId>>) -> Vec<AdapterId>679     pub fn enumerate_adapters(&self, inputs: AdapterInputs<Input<G, AdapterId>>) -> Vec<AdapterId> {
680         profiling::scope!("enumerate_adapters", "Instance");
681 
682         let instance = &self.instance;
683         let mut token = Token::root();
684         let mut adapters = Vec::new();
685 
686         backends_map! {
687             let map = |(instance_field, backend, backend_info, backend_hub)| {
688                 if let Some(ref inst) = *instance_field {
689                     let hub = backend_hub(self);
690                     if let Some(id_backend) = inputs.find(backend) {
691                         for raw in inst.enumerate_adapters() {
692                             let adapter = Adapter::new(raw);
693                             log::info!("Adapter {} {:?}", backend_info, adapter.raw.info);
694                             let id = hub.adapters
695                                 .prepare(id_backend.clone())
696                                 .assign(adapter, &mut token);
697                             adapters.push(id.0);
698                         }
699                     }
700                 }
701             };
702 
703             #[cfg(vulkan)]
704             map((&instance.vulkan, Backend::Vulkan, "Vulkan", backend::Vulkan::hub)),
705             #[cfg(metal)]
706             map((&instance.metal, Backend::Metal, "Metal", backend::Metal::hub)),
707             #[cfg(dx12)]
708             map((&instance.dx12, Backend::Dx12, "Dx12", backend::Dx12::hub)),
709             #[cfg(dx11)]
710             map((&instance.dx11, Backend::Dx11, "Dx11", backend::Dx11::hub)),
711             #[cfg(gl)]
712             map((&instance.gl, Backend::Gl, "GL", backend::Gl::hub)),
713         }
714 
715         adapters
716     }
717 
request_adapter( &self, desc: &RequestAdapterOptions, inputs: AdapterInputs<Input<G, AdapterId>>, ) -> Result<AdapterId, RequestAdapterError>718     pub fn request_adapter(
719         &self,
720         desc: &RequestAdapterOptions,
721         inputs: AdapterInputs<Input<G, AdapterId>>,
722     ) -> Result<AdapterId, RequestAdapterError> {
723         profiling::scope!("pick_adapter", "Instance");
724 
725         let instance = &self.instance;
726         let mut token = Token::root();
727         let (surface_guard, mut token) = self.surfaces.read(&mut token);
728         let compatible_surface = desc
729             .compatible_surface
730             .map(|id| {
731                 surface_guard
732                     .get(id)
733                     .map_err(|_| RequestAdapterError::InvalidSurface(id))
734             })
735             .transpose()?;
736         let mut device_types = Vec::new();
737 
738         let mut id_vulkan = inputs.find(Backend::Vulkan);
739         let mut id_metal = inputs.find(Backend::Metal);
740         let mut id_dx12 = inputs.find(Backend::Dx12);
741         let mut id_dx11 = inputs.find(Backend::Dx11);
742         let mut id_gl = inputs.find(Backend::Gl);
743 
744         backends_map! {
745             let map = |(instance_backend, id_backend, surface_backend)| {
746                 match *instance_backend {
747                     Some(ref inst) if id_backend.is_some() => {
748                         let mut adapters = inst.enumerate_adapters();
749                         if let Some(surface_backend) = compatible_surface.and_then(surface_backend) {
750                             adapters.retain(|a| {
751                                 a.queue_families
752                                     .iter()
753                                     .find(|qf| qf.queue_type().supports_graphics())
754                                     .map_or(false, |qf| surface_backend.supports_queue_family(qf))
755                             });
756                         }
757                         device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone()));
758                         adapters
759                     }
760                     _ => Vec::new(),
761                 }
762             };
763 
764             // NB: The internal function definitions are a workaround for Rust
765             // being weird with lifetimes for closure literals...
766             #[cfg(vulkan)]
767             let adapters_vk = map((&instance.vulkan, &id_vulkan, {
768                 fn surface_vulkan(surf: &Surface) -> Option<&GfxSurface<backend::Vulkan>> {
769                     surf.vulkan.as_ref()
770                 }
771                 surface_vulkan
772             }));
773             #[cfg(metal)]
774             let adapters_mtl = map((&instance.metal, &id_metal, {
775                 fn surface_metal(surf: &Surface) -> Option<&GfxSurface<backend::Metal>> {
776                     surf.metal.as_ref()
777                 }
778                 surface_metal
779             }));
780             #[cfg(dx12)]
781             let adapters_dx12 = map((&instance.dx12, &id_dx12, {
782                 fn surface_dx12(surf: &Surface) -> Option<&GfxSurface<backend::Dx12>> {
783                     surf.dx12.as_ref()
784                 }
785                 surface_dx12
786             }));
787             #[cfg(dx11)]
788             let adapters_dx11 = map((&instance.dx11, &id_dx11, {
789                 fn surface_dx11(surf: &Surface) -> Option<&GfxSurface<backend::Dx11>> {
790                     surf.dx11.as_ref()
791                 }
792                 surface_dx11
793             }));
794             #[cfg(gl)]
795             let adapters_gl = map((&instance.gl, &id_gl, {
796                 fn surface_gl(surf: &Surface) -> Option<&GfxSurface<backend::Gl>> {
797                     surf.gl.as_ref()
798                 }
799                 surface_gl
800             }));
801         }
802 
803         if device_types.is_empty() {
804             return Err(RequestAdapterError::NotFound);
805         }
806 
807         let (mut integrated, mut discrete, mut virt, mut cpu, mut other) =
808             (None, None, None, None, None);
809 
810         for (i, ty) in device_types.into_iter().enumerate() {
811             match ty {
812                 hal::adapter::DeviceType::IntegratedGpu => {
813                     integrated = integrated.or(Some(i));
814                 }
815                 hal::adapter::DeviceType::DiscreteGpu => {
816                     discrete = discrete.or(Some(i));
817                 }
818                 hal::adapter::DeviceType::VirtualGpu => {
819                     virt = virt.or(Some(i));
820                 }
821                 hal::adapter::DeviceType::Cpu => {
822                     cpu = cpu.or(Some(i));
823                 }
824                 hal::adapter::DeviceType::Other => {
825                     other = other.or(Some(i));
826                 }
827             }
828         }
829 
830         let preferred_gpu = match desc.power_preference {
831             PowerPreference::LowPower => integrated.or(other).or(discrete).or(virt).or(cpu),
832             PowerPreference::HighPerformance => discrete.or(other).or(integrated).or(virt).or(cpu),
833         };
834 
835         let mut selected = preferred_gpu.unwrap_or(0);
836 
837         backends_map! {
838             let map = |(info_adapter, id_backend, mut adapters_backend, backend_hub)| {
839                 if selected < adapters_backend.len() {
840                     let adapter = Adapter::new(adapters_backend.swap_remove(selected));
841                     log::info!("Adapter {} {:?}", info_adapter, adapter.raw.info);
842                     let id = backend_hub(self).adapters
843                         .prepare(id_backend.take().unwrap())
844                         .assign(adapter, &mut token);
845                     return Ok(id.0);
846                 }
847                 selected -= adapters_backend.len();
848             };
849 
850             #[cfg(vulkan)]
851             map(("Vulkan", &mut id_vulkan, adapters_vk, backend::Vulkan::hub)),
852             #[cfg(metal)]
853             map(("Metal", &mut id_metal, adapters_mtl, backend::Metal::hub)),
854             #[cfg(dx12)]
855             map(("Dx12", &mut id_dx12, adapters_dx12, backend::Dx12::hub)),
856             #[cfg(dx11)]
857             map(("Dx11", &mut id_dx11, adapters_dx11, backend::Dx11::hub)),
858             #[cfg(gl)]
859             map(("GL", &mut id_gl, adapters_gl, backend::Gl::hub)),
860         }
861 
862         let _ = (
863             selected,
864             id_vulkan.take(),
865             id_metal.take(),
866             id_dx12.take(),
867             id_dx11.take(),
868             id_gl.take(),
869         );
870         log::warn!("Some adapters are present, but enumerating them failed!");
871         Err(RequestAdapterError::NotFound)
872     }
873 
adapter_get_info<B: GfxBackend>( &self, adapter_id: AdapterId, ) -> Result<wgt::AdapterInfo, InvalidAdapter>874     pub fn adapter_get_info<B: GfxBackend>(
875         &self,
876         adapter_id: AdapterId,
877     ) -> Result<wgt::AdapterInfo, InvalidAdapter> {
878         let hub = B::hub(self);
879         let mut token = Token::root();
880         let (adapter_guard, _) = hub.adapters.read(&mut token);
881         adapter_guard
882             .get(adapter_id)
883             .map(|adapter| conv::map_adapter_info(adapter.raw.info.clone(), adapter_id.backend()))
884             .map_err(|_| InvalidAdapter)
885     }
886 
adapter_get_texture_format_features<B: GfxBackend>( &self, adapter_id: AdapterId, format: wgt::TextureFormat, ) -> Result<wgt::TextureFormatFeatures, InvalidAdapter>887     pub fn adapter_get_texture_format_features<B: GfxBackend>(
888         &self,
889         adapter_id: AdapterId,
890         format: wgt::TextureFormat,
891     ) -> Result<wgt::TextureFormatFeatures, InvalidAdapter> {
892         let hub = B::hub(self);
893         let mut token = Token::root();
894         let (adapter_guard, _) = hub.adapters.read(&mut token);
895         adapter_guard
896             .get(adapter_id)
897             .map(|adapter| adapter.get_texture_format_features(format))
898             .map_err(|_| InvalidAdapter)
899     }
900 
adapter_features<B: GfxBackend>( &self, adapter_id: AdapterId, ) -> Result<wgt::Features, InvalidAdapter>901     pub fn adapter_features<B: GfxBackend>(
902         &self,
903         adapter_id: AdapterId,
904     ) -> Result<wgt::Features, InvalidAdapter> {
905         let hub = B::hub(self);
906         let mut token = Token::root();
907         let (adapter_guard, _) = hub.adapters.read(&mut token);
908         adapter_guard
909             .get(adapter_id)
910             .map(|adapter| adapter.features)
911             .map_err(|_| InvalidAdapter)
912     }
913 
adapter_limits<B: GfxBackend>( &self, adapter_id: AdapterId, ) -> Result<wgt::Limits, InvalidAdapter>914     pub fn adapter_limits<B: GfxBackend>(
915         &self,
916         adapter_id: AdapterId,
917     ) -> Result<wgt::Limits, InvalidAdapter> {
918         let hub = B::hub(self);
919         let mut token = Token::root();
920         let (adapter_guard, _) = hub.adapters.read(&mut token);
921         adapter_guard
922             .get(adapter_id)
923             .map(|adapter| adapter.limits.clone())
924             .map_err(|_| InvalidAdapter)
925     }
926 
adapter_downlevel_properties<B: GfxBackend>( &self, adapter_id: AdapterId, ) -> Result<wgt::DownlevelProperties, InvalidAdapter>927     pub fn adapter_downlevel_properties<B: GfxBackend>(
928         &self,
929         adapter_id: AdapterId,
930     ) -> Result<wgt::DownlevelProperties, InvalidAdapter> {
931         let hub = B::hub(self);
932         let mut token = Token::root();
933         let (adapter_guard, _) = hub.adapters.read(&mut token);
934         adapter_guard
935             .get(adapter_id)
936             .map(|adapter| adapter.downlevel)
937             .map_err(|_| InvalidAdapter)
938     }
939 
adapter_drop<B: GfxBackend>(&self, adapter_id: AdapterId)940     pub fn adapter_drop<B: GfxBackend>(&self, adapter_id: AdapterId) {
941         profiling::scope!("drop", "Adapter");
942 
943         let hub = B::hub(self);
944         let mut token = Token::root();
945         let (mut adapter_guard, _) = hub.adapters.write(&mut token);
946 
947         let free = match adapter_guard.get_mut(adapter_id) {
948             Ok(adapter) => adapter.life_guard.ref_count.take().unwrap().load() == 1,
949             Err(_) => true,
950         };
951         if free {
952             hub.adapters
953                 .unregister_locked(adapter_id, &mut *adapter_guard);
954         }
955     }
956 }
957 
958 impl<G: GlobalIdentityHandlerFactory> Global<G> {
adapter_request_device<B: GfxBackend>( &self, adapter_id: AdapterId, desc: &DeviceDescriptor, trace_path: Option<&std::path::Path>, id_in: Input<G, DeviceId>, ) -> (DeviceId, Option<RequestDeviceError>)959     pub fn adapter_request_device<B: GfxBackend>(
960         &self,
961         adapter_id: AdapterId,
962         desc: &DeviceDescriptor,
963         trace_path: Option<&std::path::Path>,
964         id_in: Input<G, DeviceId>,
965     ) -> (DeviceId, Option<RequestDeviceError>) {
966         profiling::scope!("request_device", "Adapter");
967 
968         let hub = B::hub(self);
969         let mut token = Token::root();
970         let fid = hub.devices.prepare(id_in);
971 
972         let error = loop {
973             let (adapter_guard, mut token) = hub.adapters.read(&mut token);
974             let adapter = match adapter_guard.get(adapter_id) {
975                 Ok(adapter) => adapter,
976                 Err(_) => break RequestDeviceError::InvalidAdapter,
977             };
978             let device = match adapter.create_device(adapter_id, desc, trace_path) {
979                 Ok(device) => device,
980                 Err(e) => break e,
981             };
982             let id = fid.assign(device, &mut token);
983             return (id.0, None);
984         };
985 
986         let id = fid.assign_error(desc.label.borrow_or_default(), &mut token);
987         (id, Some(error))
988     }
989 }
990