summaryrefslogtreecommitdiffstats
path: root/gfx/wgpu/wgpu-core/src/instance.rs
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wgpu/wgpu-core/src/instance.rs')
-rw-r--r--gfx/wgpu/wgpu-core/src/instance.rs840
1 files changed, 840 insertions, 0 deletions
diff --git a/gfx/wgpu/wgpu-core/src/instance.rs b/gfx/wgpu/wgpu-core/src/instance.rs
new file mode 100644
index 0000000000..8f156c9d3f
--- /dev/null
+++ b/gfx/wgpu/wgpu-core/src/instance.rs
@@ -0,0 +1,840 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use crate::{
+ backend,
+ device::{Device, DeviceDescriptor},
+ hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token},
+ id::{AdapterId, DeviceId, SurfaceId, Valid},
+ span, LabelHelpers, LifeGuard, PrivateFeatures, Stored, MAX_BIND_GROUPS,
+};
+
+use wgt::{Backend, BackendBit, PowerPreference, BIND_BUFFER_ALIGNMENT};
+
+use hal::{
+ adapter::{AdapterInfo as HalAdapterInfo, DeviceType as HalDeviceType, PhysicalDevice as _},
+ queue::QueueFamily as _,
+ window::Surface as _,
+ Instance as _,
+};
+use thiserror::Error;
+
+/// Size that is guaranteed to be available in push constants.
+///
+/// This is needed because non-vulkan backends might not
+/// provide a push-constant size limit.
+const MIN_PUSH_CONSTANT_SIZE: u32 = 128;
+
+pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
+
+#[derive(Debug)]
+pub struct Instance {
+ #[cfg(vulkan)]
+ pub vulkan: Option<gfx_backend_vulkan::Instance>,
+ #[cfg(metal)]
+ pub metal: Option<gfx_backend_metal::Instance>,
+ #[cfg(dx12)]
+ pub dx12: Option<gfx_backend_dx12::Instance>,
+ #[cfg(dx11)]
+ pub dx11: Option<gfx_backend_dx11::Instance>,
+ #[cfg(gl)]
+ pub gl: Option<gfx_backend_gl::Instance>,
+}
+
+impl Instance {
+ pub fn new(name: &str, version: u32, backends: BackendBit) -> Self {
+ backends_map! {
+ let map = |(backend, backend_create)| {
+ if backends.contains(backend.into()) {
+ backend_create(name, version).ok()
+ } else {
+ None
+ }
+ };
+ Self {
+ #[cfg(vulkan)]
+ vulkan: map((Backend::Vulkan, gfx_backend_vulkan::Instance::create)),
+ #[cfg(metal)]
+ metal: map((Backend::Metal, gfx_backend_metal::Instance::create)),
+ #[cfg(dx12)]
+ dx12: map((Backend::Dx12, gfx_backend_dx12::Instance::create)),
+ #[cfg(dx11)]
+ dx11: map((Backend::Dx11, gfx_backend_dx11::Instance::create)),
+ #[cfg(gl)]
+ gl: map((Backend::Gl, gfx_backend_gl::Instance::create)),
+ }
+ }
+ }
+
+ pub(crate) fn destroy_surface(&self, surface: Surface) {
+ backends_map! {
+ let map = |(surface_backend, self_backend)| {
+ unsafe {
+ if let Some(suf) = surface_backend {
+ self_backend.as_ref().unwrap().destroy_surface(suf);
+ }
+ }
+ };
+
+ #[cfg(vulkan)]
+ map((surface.vulkan, &self.vulkan)),
+ #[cfg(metal)]
+ map((surface.metal, &self.metal)),
+ #[cfg(dx12)]
+ map((surface.dx12, &self.dx12)),
+ #[cfg(dx11)]
+ map((surface.dx11, &self.dx11)),
+ #[cfg(gl)]
+ map((surface.gl, &self.gl)),
+ }
+ }
+}
+
+type GfxSurface<B> = <B as hal::Backend>::Surface;
+
+#[derive(Debug)]
+pub struct Surface {
+ #[cfg(vulkan)]
+ pub vulkan: Option<GfxSurface<backend::Vulkan>>,
+ #[cfg(metal)]
+ pub metal: Option<GfxSurface<backend::Metal>>,
+ #[cfg(dx12)]
+ pub dx12: Option<GfxSurface<backend::Dx12>>,
+ #[cfg(dx11)]
+ pub dx11: Option<GfxSurface<backend::Dx11>>,
+ #[cfg(gl)]
+ pub gl: Option<GfxSurface<backend::Gl>>,
+}
+
+impl crate::hub::Resource for Surface {
+ const TYPE: &'static str = "Surface";
+
+ fn life_guard(&self) -> &LifeGuard {
+ unreachable!()
+ }
+
+ fn label(&self) -> &str {
+ "<Surface>"
+ }
+}
+
+#[derive(Debug)]
+pub struct Adapter<B: hal::Backend> {
+ pub(crate) raw: hal::adapter::Adapter<B>,
+ features: wgt::Features,
+ limits: wgt::Limits,
+ life_guard: LifeGuard,
+}
+
+impl<B: GfxBackend> Adapter<B> {
+ fn new(raw: hal::adapter::Adapter<B>) -> Self {
+ span!(_guard, INFO, "Adapter::new");
+
+ let adapter_features = raw.physical_device.features();
+
+ let mut features = wgt::Features::default()
+ | wgt::Features::MAPPABLE_PRIMARY_BUFFERS
+ | wgt::Features::PUSH_CONSTANTS;
+ features.set(
+ wgt::Features::DEPTH_CLAMPING,
+ adapter_features.contains(hal::Features::DEPTH_CLAMP),
+ );
+ features.set(
+ wgt::Features::TEXTURE_COMPRESSION_BC,
+ adapter_features.contains(hal::Features::FORMAT_BC),
+ );
+ features.set(
+ wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY,
+ adapter_features.contains(hal::Features::TEXTURE_DESCRIPTOR_ARRAY),
+ );
+ features.set(
+ wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
+ adapter_features.contains(hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING),
+ );
+ features.set(
+ wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING,
+ adapter_features.contains(hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING),
+ );
+ features.set(
+ wgt::Features::UNSIZED_BINDING_ARRAY,
+ adapter_features.contains(hal::Features::UNSIZED_DESCRIPTOR_ARRAY),
+ );
+ features.set(
+ wgt::Features::MULTI_DRAW_INDIRECT,
+ adapter_features.contains(hal::Features::MULTI_DRAW_INDIRECT),
+ );
+ features.set(
+ wgt::Features::MULTI_DRAW_INDIRECT_COUNT,
+ adapter_features.contains(hal::Features::DRAW_INDIRECT_COUNT),
+ );
+ features.set(
+ wgt::Features::NON_FILL_POLYGON_MODE,
+ adapter_features.contains(hal::Features::NON_FILL_POLYGON_MODE),
+ );
+ #[cfg(not(target_os = "ios"))]
+ //TODO: https://github.com/gfx-rs/gfx/issues/3346
+ features.set(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true);
+
+ let adapter_limits = raw.physical_device.limits();
+
+ let default_limits = wgt::Limits::default();
+
+ // All these casts to u32 are safe as the underlying vulkan types are u32s.
+ // If another backend provides larger limits than u32, we need to clamp them to u32::MAX.
+ // TODO: fix all gfx-hal backends to produce limits we care about, and remove .max
+ let limits = wgt::Limits {
+ max_bind_groups: (adapter_limits.max_bound_descriptor_sets as u32)
+ .min(MAX_BIND_GROUPS as u32)
+ .max(default_limits.max_bind_groups),
+ max_dynamic_uniform_buffers_per_pipeline_layout: (adapter_limits
+ .max_descriptor_set_uniform_buffers_dynamic
+ as u32)
+ .max(default_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
+ max_dynamic_storage_buffers_per_pipeline_layout: (adapter_limits
+ .max_descriptor_set_storage_buffers_dynamic
+ as u32)
+ .max(default_limits.max_dynamic_storage_buffers_per_pipeline_layout),
+ max_sampled_textures_per_shader_stage: (adapter_limits
+ .max_per_stage_descriptor_sampled_images
+ as u32)
+ .max(default_limits.max_sampled_textures_per_shader_stage),
+ max_samplers_per_shader_stage: (adapter_limits.max_per_stage_descriptor_samplers
+ as u32)
+ .max(default_limits.max_samplers_per_shader_stage),
+ max_storage_buffers_per_shader_stage: (adapter_limits
+ .max_per_stage_descriptor_storage_buffers
+ as u32)
+ .max(default_limits.max_storage_buffers_per_shader_stage),
+ max_storage_textures_per_shader_stage: (adapter_limits
+ .max_per_stage_descriptor_storage_images
+ as u32)
+ .max(default_limits.max_storage_textures_per_shader_stage),
+ max_uniform_buffers_per_shader_stage: (adapter_limits
+ .max_per_stage_descriptor_uniform_buffers
+ as u32)
+ .max(default_limits.max_uniform_buffers_per_shader_stage),
+ max_uniform_buffer_binding_size: (adapter_limits.max_uniform_buffer_range as u32)
+ .max(default_limits.max_uniform_buffer_binding_size),
+ max_push_constant_size: (adapter_limits.max_push_constants_size as u32)
+ .max(MIN_PUSH_CONSTANT_SIZE), // As an extension, the default is always 0, so define a separate minimum.
+ };
+
+ Self {
+ raw,
+ features,
+ limits,
+ life_guard: LifeGuard::new("<Adapter>"),
+ }
+ }
+
+ fn create_device(
+ &self,
+ self_id: AdapterId,
+ desc: &DeviceDescriptor,
+ trace_path: Option<&std::path::Path>,
+ ) -> Result<Device<B>, RequestDeviceError> {
+ // Verify all features were exposed by the adapter
+ if !self.features.contains(desc.features) {
+ return Err(RequestDeviceError::UnsupportedFeature(
+ desc.features - self.features,
+ ));
+ }
+
+ // Verify feature preconditions
+ if desc
+ .features
+ .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
+ && self.raw.info.device_type == hal::adapter::DeviceType::DiscreteGpu
+ {
+ tracing::warn!("Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. This is a massive performance footgun and likely not what you wanted");
+ }
+
+ let phd = &self.raw.physical_device;
+ let available_features = phd.features();
+
+ // Check features that are always needed
+ let wishful_features = hal::Features::ROBUST_BUFFER_ACCESS
+ | hal::Features::VERTEX_STORES_AND_ATOMICS
+ | hal::Features::FRAGMENT_STORES_AND_ATOMICS
+ | hal::Features::NDC_Y_UP
+ | hal::Features::INDEPENDENT_BLENDING
+ | hal::Features::SAMPLER_ANISOTROPY
+ | hal::Features::IMAGE_CUBE_ARRAY;
+ let mut enabled_features = available_features & wishful_features;
+ if enabled_features != wishful_features {
+ tracing::warn!(
+ "Missing internal features: {:?}",
+ wishful_features - enabled_features
+ );
+ }
+
+ // Features
+ enabled_features.set(
+ hal::Features::TEXTURE_DESCRIPTOR_ARRAY,
+ desc.features
+ .contains(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY),
+ );
+ enabled_features.set(
+ hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING,
+ desc.features
+ .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING),
+ );
+ enabled_features.set(
+ hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING,
+ desc.features
+ .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING),
+ );
+ enabled_features.set(
+ hal::Features::UNSIZED_DESCRIPTOR_ARRAY,
+ desc.features.contains(wgt::Features::UNSIZED_BINDING_ARRAY),
+ );
+ enabled_features.set(
+ hal::Features::MULTI_DRAW_INDIRECT,
+ desc.features.contains(wgt::Features::MULTI_DRAW_INDIRECT),
+ );
+ enabled_features.set(
+ hal::Features::DRAW_INDIRECT_COUNT,
+ desc.features
+ .contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT),
+ );
+ enabled_features.set(
+ hal::Features::NON_FILL_POLYGON_MODE,
+ desc.features.contains(wgt::Features::NON_FILL_POLYGON_MODE),
+ );
+
+ let family = self
+ .raw
+ .queue_families
+ .iter()
+ .find(|family| family.queue_type().supports_graphics())
+ .ok_or(RequestDeviceError::NoGraphicsQueue)?;
+ let mut gpu =
+ unsafe { phd.open(&[(family, &[1.0])], enabled_features) }.map_err(|err| {
+ use hal::device::CreationError::*;
+ match err {
+ DeviceLost => RequestDeviceError::DeviceLost,
+ InitializationFailed => RequestDeviceError::Internal,
+ OutOfMemory(_) => RequestDeviceError::OutOfMemory,
+ _ => panic!("failed to create `gfx-hal` device: {}", err),
+ }
+ })?;
+
+ if let Some(_) = desc.label {
+ //TODO
+ }
+
+ let limits = phd.limits();
+ assert_eq!(
+ 0,
+ BIND_BUFFER_ALIGNMENT % limits.min_storage_buffer_offset_alignment,
+ "Adapter storage buffer offset alignment not compatible with WGPU"
+ );
+ assert_eq!(
+ 0,
+ BIND_BUFFER_ALIGNMENT % limits.min_uniform_buffer_offset_alignment,
+ "Adapter uniform buffer offset alignment not compatible with WGPU"
+ );
+ if self.limits < desc.limits {
+ return Err(RequestDeviceError::LimitsExceeded);
+ }
+
+ let mem_props = phd.memory_properties();
+ if !desc.shader_validation {
+ tracing::warn!("Shader validation is disabled");
+ }
+ let private_features = PrivateFeatures {
+ shader_validation: desc.shader_validation,
+ anisotropic_filtering: enabled_features.contains(hal::Features::SAMPLER_ANISOTROPY),
+ texture_d24: phd
+ .format_properties(Some(hal::format::Format::X8D24Unorm))
+ .optimal_tiling
+ .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
+ texture_d24_s8: phd
+ .format_properties(Some(hal::format::Format::D24UnormS8Uint))
+ .optimal_tiling
+ .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT),
+ };
+
+ Device::new(
+ gpu.device,
+ Stored {
+ value: Valid(self_id),
+ ref_count: self.life_guard.add_ref(),
+ },
+ gpu.queue_groups.swap_remove(0),
+ mem_props,
+ limits,
+ private_features,
+ desc,
+ trace_path,
+ )
+ .or(Err(RequestDeviceError::OutOfMemory))
+ }
+}
+
+impl<B: hal::Backend> crate::hub::Resource for Adapter<B> {
+ const TYPE: &'static str = "Adapter";
+
+ fn life_guard(&self) -> &LifeGuard {
+ &self.life_guard
+ }
+}
+
+/// Metadata about a backend adapter.
+#[derive(Clone, Debug, PartialEq)]
+#[cfg_attr(feature = "trace", derive(serde::Serialize))]
+#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
+pub struct AdapterInfo {
+ /// Adapter name
+ pub name: String,
+ /// Vendor PCI id of the adapter
+ pub vendor: usize,
+ /// PCI id of the adapter
+ pub device: usize,
+ /// Type of device
+ pub device_type: DeviceType,
+ /// Backend used for device
+ pub backend: Backend,
+}
+
+impl AdapterInfo {
+ fn from_gfx(adapter_info: HalAdapterInfo, backend: Backend) -> Self {
+ let HalAdapterInfo {
+ name,
+ vendor,
+ device,
+ device_type,
+ } = adapter_info;
+
+ Self {
+ name,
+ vendor,
+ device,
+ device_type: device_type.into(),
+ backend,
+ }
+ }
+}
+
+#[derive(Clone, Debug, Error)]
+/// Error when requesting a device from the adaptor
+pub enum RequestDeviceError {
+ #[error("parent adapter is invalid")]
+ InvalidAdapter,
+ #[error("connection to device was lost during initialization")]
+ DeviceLost,
+ #[error("device initialization failed due to implementation specific errors")]
+ Internal,
+ #[error("some of the requested device limits are not supported")]
+ LimitsExceeded,
+ #[error("device has no queue supporting graphics")]
+ NoGraphicsQueue,
+ #[error("not enough memory left")]
+ OutOfMemory,
+ #[error("unsupported features were requested: {0:?}")]
+ UnsupportedFeature(wgt::Features),
+}
+
+/// Supported physical device types.
+#[repr(u8)]
+#[derive(Clone, Debug, PartialEq)]
+#[cfg_attr(feature = "trace", derive(serde::Serialize))]
+#[cfg_attr(feature = "replay", derive(serde::Deserialize))]
+pub enum DeviceType {
+ /// Other.
+ Other,
+ /// Integrated GPU with shared CPU/GPU memory.
+ IntegratedGpu,
+ /// Discrete GPU with separate CPU/GPU memory.
+ DiscreteGpu,
+ /// Virtual / Hosted.
+ VirtualGpu,
+ /// Cpu / Software Rendering.
+ Cpu,
+}
+
+impl From<HalDeviceType> for DeviceType {
+ fn from(device_type: HalDeviceType) -> Self {
+ match device_type {
+ HalDeviceType::Other => Self::Other,
+ HalDeviceType::IntegratedGpu => Self::IntegratedGpu,
+ HalDeviceType::DiscreteGpu => Self::DiscreteGpu,
+ HalDeviceType::VirtualGpu => Self::VirtualGpu,
+ HalDeviceType::Cpu => Self::Cpu,
+ }
+ }
+}
+
+pub enum AdapterInputs<'a, I> {
+ IdSet(&'a [I], fn(&I) -> Backend),
+ Mask(BackendBit, fn(Backend) -> I),
+}
+
+impl<I: Clone> AdapterInputs<'_, I> {
+ fn find(&self, b: Backend) -> Option<I> {
+ match *self {
+ Self::IdSet(ids, ref fun) => ids.iter().find(|id| fun(id) == b).cloned(),
+ Self::Mask(bits, ref fun) => {
+ if bits.contains(b.into()) {
+ Some(fun(b))
+ } else {
+ None
+ }
+ }
+ }
+ }
+}
+
+#[error("adapter is invalid")]
+#[derive(Clone, Debug, Error)]
+pub struct InvalidAdapter;
+
+#[derive(Clone, Debug, Error)]
+pub enum RequestAdapterError {
+ #[error("no suitable adapter found")]
+ NotFound,
+ #[error("surface {0:?} is invalid")]
+ InvalidSurface(SurfaceId),
+}
+
+impl<G: GlobalIdentityHandlerFactory> Global<G> {
+ #[cfg(feature = "raw-window-handle")]
+ pub fn instance_create_surface(
+ &self,
+ handle: &impl raw_window_handle::HasRawWindowHandle,
+ id_in: Input<G, SurfaceId>,
+ ) -> SurfaceId {
+ span!(_guard, INFO, "Instance::create_surface");
+
+ let surface = unsafe {
+ backends_map! {
+ let map = |inst| {
+ inst
+ .as_ref()
+ .and_then(|inst| inst.create_surface(handle).map_err(|e| {
+ tracing::warn!("Error: {:?}", e);
+ }).ok())
+ };
+
+ Surface {
+ #[cfg(vulkan)]
+ vulkan: map(&self.instance.vulkan),
+ #[cfg(metal)]
+ metal: map(&self.instance.metal),
+ #[cfg(dx12)]
+ dx12: map(&self.instance.dx12),
+ #[cfg(dx11)]
+ dx11: map(&self.instance.dx11),
+ #[cfg(gl)]
+ gl: map(&self.instance.gl),
+ }
+ }
+ };
+
+ let mut token = Token::root();
+ let id = self.surfaces.register_identity(id_in, surface, &mut token);
+ id.0
+ }
+
+ pub fn surface_drop(&self, id: SurfaceId) {
+ span!(_guard, INFO, "Surface::drop");
+ let mut token = Token::root();
+ let (surface, _) = self.surfaces.unregister(id, &mut token);
+ self.instance.destroy_surface(surface.unwrap());
+ }
+
+ pub fn enumerate_adapters(&self, inputs: AdapterInputs<Input<G, AdapterId>>) -> Vec<AdapterId> {
+ span!(_guard, INFO, "Instance::enumerate_adapters");
+
+ let instance = &self.instance;
+ let mut token = Token::root();
+ let mut adapters = Vec::new();
+
+ backends_map! {
+ let map = |(instance_field, backend, backend_info, backend_hub)| {
+ if let Some(inst) = instance_field {
+ let hub = backend_hub(self);
+ if let Some(id_backend) = inputs.find(backend) {
+ for raw in inst.enumerate_adapters() {
+ let adapter = Adapter::new(raw);
+ tracing::info!("Adapter {} {:?}", backend_info, adapter.raw.info);
+ let id = hub.adapters.register_identity(
+ id_backend.clone(),
+ adapter,
+ &mut token,
+ );
+ adapters.push(id.0);
+ }
+ }
+ }
+ };
+
+ #[cfg(vulkan)]
+ map((&instance.vulkan, Backend::Vulkan, "Vulkan", backend::Vulkan::hub)),
+ #[cfg(metal)]
+ map((&instance.metal, Backend::Metal, "Metal", backend::Metal::hub)),
+ #[cfg(dx12)]
+ map((&instance.dx12, Backend::Dx12, "Dx12", backend::Dx12::hub)),
+ #[cfg(dx11)]
+ map((&instance.dx11, Backend::Dx11, "Dx11", backend::Dx11::hub)),
+ #[cfg(gl)]
+ map((&instance.gl, Backend::Gl, "GL", backend::Gl::hub)),
+ }
+
+ adapters
+ }
+
+ pub fn request_adapter(
+ &self,
+ desc: &RequestAdapterOptions,
+ inputs: AdapterInputs<Input<G, AdapterId>>,
+ ) -> Result<AdapterId, RequestAdapterError> {
+ span!(_guard, INFO, "Instance::pick_adapter");
+
+ let instance = &self.instance;
+ let mut token = Token::root();
+ let (surface_guard, mut token) = self.surfaces.read(&mut token);
+ let compatible_surface = desc
+ .compatible_surface
+ .map(|id| {
+ surface_guard
+ .get(id)
+ .map_err(|_| RequestAdapterError::InvalidSurface(id))
+ })
+ .transpose()?;
+ let mut device_types = Vec::new();
+
+ let mut id_vulkan = inputs.find(Backend::Vulkan);
+ let mut id_metal = inputs.find(Backend::Metal);
+ let mut id_dx12 = inputs.find(Backend::Dx12);
+ let mut id_dx11 = inputs.find(Backend::Dx11);
+ let mut id_gl = inputs.find(Backend::Gl);
+
+ backends_map! {
+ let map = |(instance_backend, id_backend, surface_backend)| {
+ match instance_backend {
+ Some(ref inst) if id_backend.is_some() => {
+ let mut adapters = inst.enumerate_adapters();
+ if let Some(surface_backend) = compatible_surface.and_then(surface_backend) {
+ adapters.retain(|a| {
+ a.queue_families
+ .iter()
+ .find(|qf| qf.queue_type().supports_graphics())
+ .map_or(false, |qf| surface_backend.supports_queue_family(qf))
+ });
+ }
+ device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone()));
+ adapters
+ }
+ _ => Vec::new(),
+ }
+ };
+
+ // NB: The internal function definitions are a workaround for Rust
+ // being weird with lifetimes for closure literals...
+ #[cfg(vulkan)]
+ let adapters_vk = map((&instance.vulkan, &id_vulkan, {
+ fn surface_vulkan(surf: &Surface) -> Option<&GfxSurface<backend::Vulkan>> {
+ surf.vulkan.as_ref()
+ }
+ surface_vulkan
+ }));
+ #[cfg(metal)]
+ let adapters_mtl = map((&instance.metal, &id_metal, {
+ fn surface_metal(surf: &Surface) -> Option<&GfxSurface<backend::Metal>> {
+ surf.metal.as_ref()
+ }
+ surface_metal
+ }));
+ #[cfg(dx12)]
+ let adapters_dx12 = map((&instance.dx12, &id_dx12, {
+ fn surface_dx12(surf: &Surface) -> Option<&GfxSurface<backend::Dx12>> {
+ surf.dx12.as_ref()
+ }
+ surface_dx12
+ }));
+ #[cfg(dx11)]
+ let adapters_dx11 = map((&instance.dx11, &id_dx11, {
+ fn surface_dx11(surf: &Surface) -> Option<&GfxSurface<backend::Dx11>> {
+ surf.dx11.as_ref()
+ }
+ surface_dx11
+ }));
+ #[cfg(gl)]
+ let adapters_gl = map((&instance.gl, &id_gl, {
+ fn surface_gl(surf: &Surface) -> Option<&GfxSurface<backend::Gl>> {
+ surf.gl.as_ref()
+ }
+ surface_gl
+ }));
+ }
+
+ if device_types.is_empty() {
+ return Err(RequestAdapterError::NotFound);
+ }
+
+ let (mut integrated, mut discrete, mut virt, mut other) = (None, None, None, None);
+
+ for (i, ty) in device_types.into_iter().enumerate() {
+ match ty {
+ hal::adapter::DeviceType::IntegratedGpu => {
+ integrated = integrated.or(Some(i));
+ }
+ hal::adapter::DeviceType::DiscreteGpu => {
+ discrete = discrete.or(Some(i));
+ }
+ hal::adapter::DeviceType::VirtualGpu => {
+ virt = virt.or(Some(i));
+ }
+ _ => {
+ other = other.or(Some(i));
+ }
+ }
+ }
+
+ let preferred_gpu = match desc.power_preference {
+ PowerPreference::LowPower => integrated.or(other).or(discrete).or(virt),
+ PowerPreference::HighPerformance => discrete.or(other).or(integrated).or(virt),
+ };
+
+ let mut selected = preferred_gpu.unwrap_or(0);
+
+ backends_map! {
+ let map = |(info_adapter, id_backend, mut adapters_backend, backend_hub)| {
+ if selected < adapters_backend.len() {
+ let adapter = Adapter::new(adapters_backend.swap_remove(selected));
+ tracing::info!("Adapter {} {:?}", info_adapter, adapter.raw.info);
+ let id = backend_hub(self).adapters.register_identity(
+ id_backend.take().unwrap(),
+ adapter,
+ &mut token,
+ );
+ return Ok(id.0);
+ }
+ selected -= adapters_backend.len();
+ };
+
+ #[cfg(vulkan)]
+ map(("Vulkan", &mut id_vulkan, adapters_vk, backend::Vulkan::hub)),
+ #[cfg(metal)]
+ map(("Metal", &mut id_metal, adapters_mtl, backend::Metal::hub)),
+ #[cfg(dx12)]
+ map(("Dx12", &mut id_dx12, adapters_dx12, backend::Dx12::hub)),
+ #[cfg(dx11)]
+ map(("Dx11", &mut id_dx11, adapters_dx11, backend::Dx11::hub)),
+ #[cfg(gl)]
+ map(("GL", &mut id_dx11, adapters_gl, backend::Gl::hub)),
+ }
+
+ let _ = (
+ selected,
+ id_vulkan.take(),
+ id_metal.take(),
+ id_dx12.take(),
+ id_dx11.take(),
+ id_gl.take(),
+ );
+ tracing::warn!("Some adapters are present, but enumerating them failed!");
+ Err(RequestAdapterError::NotFound)
+ }
+
+ pub fn adapter_get_info<B: GfxBackend>(
+ &self,
+ adapter_id: AdapterId,
+ ) -> Result<AdapterInfo, InvalidAdapter> {
+ span!(_guard, INFO, "Adapter::get_info");
+
+ let hub = B::hub(self);
+ let mut token = Token::root();
+ let (adapter_guard, _) = hub.adapters.read(&mut token);
+ adapter_guard
+ .get(adapter_id)
+ .map(|adapter| AdapterInfo::from_gfx(adapter.raw.info.clone(), adapter_id.backend()))
+ .map_err(|_| InvalidAdapter)
+ }
+
+ pub fn adapter_features<B: GfxBackend>(
+ &self,
+ adapter_id: AdapterId,
+ ) -> Result<wgt::Features, InvalidAdapter> {
+ span!(_guard, INFO, "Adapter::features");
+
+ let hub = B::hub(self);
+ let mut token = Token::root();
+ let (adapter_guard, _) = hub.adapters.read(&mut token);
+ adapter_guard
+ .get(adapter_id)
+ .map(|adapter| adapter.features)
+ .map_err(|_| InvalidAdapter)
+ }
+
+ pub fn adapter_limits<B: GfxBackend>(
+ &self,
+ adapter_id: AdapterId,
+ ) -> Result<wgt::Limits, InvalidAdapter> {
+ span!(_guard, INFO, "Adapter::limits");
+
+ let hub = B::hub(self);
+ let mut token = Token::root();
+ let (adapter_guard, _) = hub.adapters.read(&mut token);
+ adapter_guard
+ .get(adapter_id)
+ .map(|adapter| adapter.limits.clone())
+ .map_err(|_| InvalidAdapter)
+ }
+
+ pub fn adapter_drop<B: GfxBackend>(&self, adapter_id: AdapterId) {
+ span!(_guard, INFO, "Adapter::drop");
+
+ let hub = B::hub(self);
+ let mut token = Token::root();
+ let (mut adapter_guard, _) = hub.adapters.write(&mut token);
+
+ match adapter_guard.get_mut(adapter_id) {
+ Ok(adapter) => {
+ if adapter.life_guard.ref_count.take().unwrap().load() == 1 {
+ hub.adapters
+ .unregister_locked(adapter_id, &mut *adapter_guard);
+ }
+ }
+ Err(_) => {
+ hub.adapters.free_id(adapter_id);
+ }
+ }
+ }
+}
+
+impl<G: GlobalIdentityHandlerFactory> Global<G> {
+ pub fn 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>) {
+ span!(_guard, INFO, "Adapter::request_device");
+
+ let hub = B::hub(self);
+ let mut token = Token::root();
+
+ let error = loop {
+ let (adapter_guard, mut token) = hub.adapters.read(&mut token);
+ let adapter = match adapter_guard.get(adapter_id) {
+ Ok(adapter) => adapter,
+ Err(_) => break RequestDeviceError::InvalidAdapter,
+ };
+ let device = match adapter.create_device(adapter_id, desc, trace_path) {
+ Ok(device) => device,
+ Err(e) => break e,
+ };
+ let id = hub.devices.register_identity(id_in, device, &mut token);
+ return (id.0, None);
+ };
+
+ let id = hub
+ .devices
+ .register_error(id_in, desc.label.borrow_or_default(), &mut token);
+ (id, Some(error))
+ }
+}