From 1ee6036f440210e038098bcc372ddd738437918f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 1 Oct 2021 08:06:03 -0400 Subject: [PATCH] Implement software adapter selection --- deno_webgpu/src/lib.rs | 3 +- player/src/bin/play.rs | 1 + player/tests/test.rs | 1 + wgpu-core/src/instance.rs | 146 ++++++++++++++------------- wgpu-types/src/lib.rs | 4 + wgpu/examples/hello-triangle/main.rs | 1 + wgpu/examples/hello-windows/main.rs | 2 +- wgpu/src/backend/direct.rs | 1 + wgpu/src/util/init.rs | 1 + wgpu/tests/instance.rs | 1 + 10 files changed, 88 insertions(+), 73 deletions(-) diff --git a/deno_webgpu/src/lib.rs b/deno_webgpu/src/lib.rs index d8c8b67e9..66b514cd3 100644 --- a/deno_webgpu/src/lib.rs +++ b/deno_webgpu/src/lib.rs @@ -216,6 +216,7 @@ fn deserialize_features(features: &wgpu_types::Features) -> Vec<&'static str> { #[serde(rename_all = "camelCase")] pub struct RequestAdapterArgs { power_preference: Option, + force_fallback_adapter: bool, } #[derive(Serialize)] @@ -259,7 +260,7 @@ pub async fn op_webgpu_request_adapter( Some(power_preference) => power_preference.into(), None => PowerPreference::default(), }, - // TODO(lucacasonato): respect forceFallbackAdapter + force_fallback_adapter: args.force_fallback_adapter, compatible_surface: None, // windowless }; let res = instance.request_adapter( diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs index 2eac675d1..ad239115f 100644 --- a/player/src/bin/play.rs +++ b/player/src/bin/play.rs @@ -55,6 +55,7 @@ fn main() { .request_adapter( &wgc::instance::RequestAdapterOptions { power_preference: wgt::PowerPreference::LowPower, + force_fallback_adapter: false, #[cfg(feature = "winit")] compatible_surface: Some(surface), #[cfg(not(feature = "winit"))] diff --git a/player/tests/test.rs b/player/tests/test.rs index 0c5532bf2..d5d6642f3 100644 --- a/player/tests/test.rs +++ b/player/tests/test.rs @@ -186,6 +186,7 @@ impl Corpus { let adapter = match global.request_adapter( &wgc::instance::RequestAdapterOptions { power_preference: wgt::PowerPreference::LowPower, + force_fallback_adapter: false, compatible_surface: None, }, wgc::instance::AdapterInputs::IdSet( diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index d73461c6f..e5a7437dc 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -551,7 +551,34 @@ impl Global { ) -> Result { profiling::scope!("pick_adapter", "Instance"); - let instance = &self.instance; + fn gather( + _: A, + instance: Option<&A::Instance>, + inputs: &AdapterInputs, + compatible_surface: Option<&Surface>, + force_software: bool, + device_types: &mut Vec, + ) -> (Option, Vec>) { + let id = inputs.find(A::VARIANT); + match instance { + Some(inst) if id.is_some() => { + let mut adapters = unsafe { inst.enumerate_adapters() }; + if force_software { + adapters.retain(|exposed| exposed.info.device_type == wgt::DeviceType::Cpu); + } + if let Some(surface) = compatible_surface { + let suf_raw = &A::get_surface(surface).raw; + adapters.retain(|exposed| unsafe { + exposed.adapter.surface_capabilities(suf_raw).is_some() + }); + } + device_types.extend(adapters.iter().map(|ad| ad.info.device_type)); + (id, adapters) + } + _ => (id, Vec::new()), + } + } + let mut token = Token::root(); let (surface_guard, mut token) = self.surfaces.read(&mut token); let compatible_surface = desc @@ -564,67 +591,51 @@ impl Global { .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 = unsafe { inst.enumerate_adapters() }; - if let Some(surface_backend) = compatible_surface.and_then(surface_backend) { - adapters.retain(|exposed| unsafe { - exposed.adapter.surface_capabilities(&surface_backend.raw).is_some() - }); - } - device_types.extend(adapters.iter().map(|ad| ad.info.device_type)); - 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<&HalSurface> { - surf.vulkan.as_ref() - } - surface_vulkan - })); - #[cfg(metal)] - let adapters_mtl = map((&instance.metal, &id_metal, { - fn surface_metal(surf: &Surface) -> Option<&HalSurface> { - surf.metal.as_ref() - } - surface_metal - })); - #[cfg(dx12)] - let adapters_dx12 = map((&instance.dx12, &id_dx12, { - fn surface_dx12(surf: &Surface) -> Option<&HalSurface> { - surf.dx12.as_ref() - } - surface_dx12 - })); - #[cfg(dx11)] - let adapters_dx11 = map((&instance.dx11, &id_dx11, { - fn surface_dx11(surf: &Surface) -> Option<&HalSurface> { - surf.dx11.as_ref() - } - surface_dx11 - })); - #[cfg(gl)] - let adapters_gl = map((&instance.gl, &id_gl, { - fn surface_gl(surf: &Surface) -> Option<&HalSurface> { - surf.gl.as_ref() - } - surface_gl - })); - } + #[cfg(vulkan)] + let (mut id_vulkan, adapters_vk) = gather( + hal::api::Vulkan, + self.instance.vulkan.as_ref(), + &inputs, + compatible_surface, + desc.force_fallback_adapter, + &mut device_types, + ); + #[cfg(metal)] + let (mut id_metal, adapters_metal) = gather( + hal::api::Metal, + self.instance.metal.as_ref(), + &inputs, + compatible_surface, + desc.force_fallback_adapter, + &mut device_types, + ); + #[cfg(dx12)] + let (mut id_dx12, adapters_dx12) = gather( + hal::api::Dx12, + self.instance.dx12.as_ref(), + &inputs, + compatible_surface, + desc.force_fallback_adapter, + &mut device_types, + ); + #[cfg(dx11)] + let (mut id_dx11, adapters_dx11) = gather( + hal::api::Dx11, + self.instance.dx11.as_ref(), + &inputs, + compatible_surface, + desc.force_fallback_adapter, + &mut device_types, + ); + #[cfg(gl)] + let (mut id_gl, adapters_gl) = gather( + hal::api::Gles, + self.instance.gl.as_ref(), + &inputs, + compatible_surface, + desc.force_fallback_adapter, + &mut device_types, + ); if device_types.is_empty() { return Err(RequestAdapterError::NotFound); @@ -676,7 +687,7 @@ impl Global { #[cfg(vulkan)] map(("Vulkan", &mut id_vulkan, adapters_vk)), #[cfg(metal)] - map(("Metal", &mut id_metal, adapters_mtl)), + map(("Metal", &mut id_metal, adapters_metal)), #[cfg(dx12)] map(("Dx12", &mut id_dx12, adapters_dx12)), #[cfg(dx11)] @@ -685,14 +696,7 @@ impl Global { map(("GL", &mut id_gl, adapters_gl)), } - let _ = ( - selected, - id_vulkan.take(), - id_metal.take(), - id_dx12.take(), - id_dx11.take(), - id_gl.take(), - ); + let _ = selected; log::warn!("Some adapters are present, but enumerating them failed!"); Err(RequestAdapterError::NotFound) } diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 73f0c8808..f5cc0664d 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -131,6 +131,9 @@ impl From for Backends { pub struct RequestAdapterOptions { /// Power preference for the adapter. pub power_preference: PowerPreference, + /// Indicates that only a fallback adapter can be returned. This is generally a "software" + /// implementation on the system. + pub force_fallback_adapter: bool, /// Surface that is required to be presentable with the requested adapter. This does not /// create the surface, only guarantees that the adapter can present to said surface. pub compatible_surface: Option, @@ -140,6 +143,7 @@ impl Default for RequestAdapterOptions { fn default() -> Self { Self { power_preference: PowerPreference::default(), + force_fallback_adapter: false, compatible_surface: None, } } diff --git a/wgpu/examples/hello-triangle/main.rs b/wgpu/examples/hello-triangle/main.rs index 9bbfb8984..938bc87c0 100644 --- a/wgpu/examples/hello-triangle/main.rs +++ b/wgpu/examples/hello-triangle/main.rs @@ -12,6 +12,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), + force_fallback_adapter: false, // Request an adapter which can render to our surface compatible_surface: Some(&surface), }) diff --git a/wgpu/examples/hello-windows/main.rs b/wgpu/examples/hello-windows/main.rs index 531b66d39..47ec00838 100644 --- a/wgpu/examples/hello-windows/main.rs +++ b/wgpu/examples/hello-windows/main.rs @@ -66,9 +66,9 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Window, wgpu::Color)>) { .collect(); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), // Request an adapter which can render to our surface compatible_surface: viewports.first().map(|desc| &desc.surface), + ..Default::default() }) .await .expect("Failed to find an appropriate adapter"); diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 836ddd020..5fa6632b1 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -777,6 +777,7 @@ impl crate::Context for Context { let id = self.0.request_adapter( &wgc::instance::RequestAdapterOptions { power_preference: options.power_preference, + force_fallback_adapter: options.force_fallback_adapter, compatible_surface: options.compatible_surface.map(|surface| surface.id.id), }, wgc::instance::AdapterInputs::Mask(wgt::Backends::all(), |_| PhantomData), diff --git a/wgpu/src/util/init.rs b/wgpu/src/util/init.rs index 32a0dd312..7f442332d 100644 --- a/wgpu/src/util/init.rs +++ b/wgpu/src/util/init.rs @@ -79,6 +79,7 @@ pub async fn initialize_adapter_from_env_or_default( instance .request_adapter(&RequestAdapterOptions { power_preference: power_preference_from_env().unwrap_or_default(), + force_fallback_adapter: false, compatible_surface, }) .await diff --git a/wgpu/tests/instance.rs b/wgpu/tests/instance.rs index 7758f6df0..c5de0480b 100644 --- a/wgpu/tests/instance.rs +++ b/wgpu/tests/instance.rs @@ -12,6 +12,7 @@ fn request_adapter_inner(power: wgt::PowerPreference) { let _adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { power_preference: power, + force_fallback_adapter: false, compatible_surface: None, })) .unwrap();