From 2053358d894a2a91b1bf443c9760f6732ef3c6a0 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Sat, 16 Dec 2023 10:39:53 +0100 Subject: [PATCH] Add `wgpu` crate features for backends (#4815) * Introduce `dx12` and `metal` crate features to `wgpu` * Implement dummy `Context` to allow compilation with `--no-default-features` * Address review * Remove `dummy::Context` in favor of `hal::api::Empty` * Add changelog entry * Panic early in `Instance::new()` if no backend is enabled Co-Authored-By: Andreas Reich <1220815+Wumpf@users.noreply.github.com> --------- Co-authored-by: Andreas Reich <1220815+Wumpf@users.noreply.github.com> --- CHANGELOG.md | 4 +++ wgpu-core/src/lib.rs | 5 ++++ wgpu-hal/src/vulkan/instance.rs | 6 ++-- wgpu/Cargo.toml | 24 ++++++++++------ wgpu/src/backend/direct.rs | 8 +++--- wgpu/src/lib.rs | 51 ++++++++++++++++++++++++++++++--- 6 files changed, 78 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 909ff8ad4..39ad54048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,10 @@ Previously, `DeviceExt::create_texture_with_data` only allowed data to be provid This feature allowed you to call `global_id` on any wgpu opaque handle to get a unique hashable identity for the given resource. This is now available without the feature flag. By @cwfitzgerald in [#4841](https://github.com/gfx-rs/wgpu/pull/4841) +### `dx12` and `metal` backend crate features + +Wgpu now exposes backend feature for the Direct3D 12 (`dx12`) and Metal (`metal`) backend. These are enabled by default, but don't do anything when not targetting the corresponding OS. By @daxpedda in [#4815](https://github.com/gfx-rs/wgpu/pull/4815) + ### New Features #### General diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index a4bb0e71b..01fd3419a 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -222,6 +222,10 @@ define_backend_caller! { gfx_if_vulkan, gfx_if_vulkan_hidden, "vulkan" if all(fe define_backend_caller! { gfx_if_metal, gfx_if_metal_hidden, "metal" if all(feature = "metal", any(target_os = "macos", target_os = "ios")) } define_backend_caller! { gfx_if_dx12, gfx_if_dx12_hidden, "dx12" if all(feature = "dx12", windows) } define_backend_caller! { gfx_if_gles, gfx_if_gles_hidden, "gles" if feature = "gles" } +define_backend_caller! { gfx_if_empty, gfx_if_empty_hidden, "empty" if all( + not(any(feature = "metal", feature = "vulkan", feature = "gles")), + any(target_os = "macos", target_os = "ios"), +) } /// Dispatch on an [`Id`]'s backend to a backend-generic method. /// @@ -276,6 +280,7 @@ macro_rules! gfx_select { wgt::Backend::Metal => $crate::gfx_if_metal!($global.$method::<$crate::api::Metal>( $($param),* )), wgt::Backend::Dx12 => $crate::gfx_if_dx12!($global.$method::<$crate::api::Dx12>( $($param),* )), wgt::Backend::Gl => $crate::gfx_if_gles!($global.$method::<$crate::api::Gles>( $($param),+ )), + wgt::Backend::Empty => $crate::gfx_if_empty!($global.$method::<$crate::api::Empty>( $($param),+ )), other => panic!("Unexpected backend {:?}", other), } }; diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs index 8eb136b2c..7a5277743 100644 --- a/wgpu-hal/src/vulkan/instance.rs +++ b/wgpu-hal/src/vulkan/instance.rs @@ -507,7 +507,7 @@ impl super::Instance { Ok(self.create_surface_from_vk_surface_khr(surface)) } - #[cfg(any(target_os = "macos", target_os = "ios"))] + #[cfg(all(any(target_os = "macos", target_os = "ios"), feature = "metal"))] fn create_surface_from_view( &self, view: *mut c_void, @@ -805,13 +805,13 @@ impl crate::Instance for super::Instance { let hinstance = unsafe { GetModuleHandleW(std::ptr::null()) }; self.create_surface_from_hwnd(hinstance as *mut _, handle.hwnd.get() as *mut _) } - #[cfg(target_os = "macos")] + #[cfg(all(target_os = "macos", feature = "metal"))] (Rwh::AppKit(handle), _) if self.shared.extensions.contains(&ext::MetalSurface::name()) => { self.create_surface_from_view(handle.ns_view.as_ptr()) } - #[cfg(target_os = "ios")] + #[cfg(all(target_os = "ios", feature = "metal"))] (Rwh::UiKit(handle), _) if self.shared.extensions.contains(&ext::MetalSurface::name()) => { diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index f10192838..12466d89d 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -24,7 +24,7 @@ targets = [ [lib] [features] -default = ["wgsl"] +default = ["wgsl", "dx12", "metal"] # Apply run-time checks, even in release builds. These are in addition # to the validation carried out at public APIs in all builds. strict_asserts = ["wgc?/strict_asserts", "wgt/strict_asserts"] @@ -33,11 +33,6 @@ glsl = ["naga/glsl-in"] wgsl = ["wgc?/wgsl"] trace = ["serde", "wgc/trace"] replay = ["serde", "wgc/replay"] -# Enables the GLES backend on Windows & macOS -angle = ["wgc/gles"] -webgl = ["hal", "wgc/gles"] -# Enables the Vulkan backend on macOS & iOS -vulkan-portability = ["wgc/vulkan"] # Implement `Send` and `Sync` on Wasm. fragile-send-sync-non-atomic-wasm = [ "hal/fragile-send-sync-non-atomic-wasm", @@ -46,6 +41,17 @@ fragile-send-sync-non-atomic-wasm = [ ] # Log all API entry points at info instead of trace level. api_log_info = ["wgc/api_log_info"] +# Backends +# Enables the DX12 backend on Windows +dx12 = ["wgc?/dx12"] +# Enables the Metal backend on macOS & iOS +metal = ["wgc?/metal"] +# Enables the GLES backend on Windows & macOS +angle = ["wgc?/gles"] +# Enables the Vulkan backend on macOS & iOS +vulkan-portability = ["wgc?/vulkan"] +# Enables the GLES backend on Wasm (currently will also enable GLES dependencies on any other target) +webgl = ["hal", "wgc/gles"] # wgpu-core is always available as an optional dependency, "wgc". # Whenever wgpu-core is selected, we want raw window handle support. @@ -60,15 +66,15 @@ features = ["raw-window-handle"] workspace = true features = ["raw-window-handle"] -# We want the wgpu-core Metal backend on macOS and iOS. +# Enable `wgc` by default on macOS and iOS to allow the `metal` crate feature to +# enable the Metal backend while being no-op on other targets. [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc] workspace = true -features = ["metal"] # We want the wgpu-core Direct3D backend and OpenGL (via WGL) on Windows. [target.'cfg(windows)'.dependencies.wgc] workspace = true -features = ["dx12", "gles"] +features = ["gles"] # We want the wgpu-core Vulkan backend on Unix (but not emscripten, macOS, iOS) and Windows. [target.'cfg(any(windows, all(unix, not(target_os = "emscripten"), not(target_os = "ios"), not(target_os = "macos"))))'.dependencies.wgc] diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index cf1fd7291..75643dc79 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -229,7 +229,7 @@ impl Context { self.0.generate_report() } - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(all(any(target_os = "ios", target_os = "macos"), feature = "metal"))] pub unsafe fn create_surface_from_core_animation_layer( &self, layer: *mut std::ffi::c_void, @@ -265,7 +265,7 @@ impl Context { }) } - #[cfg(target_os = "windows")] + #[cfg(all(target_os = "windows", feature = "dx12"))] pub unsafe fn create_surface_from_visual(&self, visual: *mut std::ffi::c_void) -> Surface { let id = unsafe { self.0.instance_create_surface_from_visual(visual, ()) }; Surface { @@ -274,7 +274,7 @@ impl Context { } } - #[cfg(target_os = "windows")] + #[cfg(all(target_os = "windows", feature = "dx12"))] pub unsafe fn create_surface_from_surface_handle( &self, surface_handle: *mut std::ffi::c_void, @@ -289,7 +289,7 @@ impl Context { } } - #[cfg(target_os = "windows")] + #[cfg(all(target_os = "windows", feature = "dx12"))] pub unsafe fn create_surface_from_swap_chain_panel( &self, swap_chain_panel: *mut std::ffi::c_void, diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 48e7262bc..b3c665c3f 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -1786,19 +1786,62 @@ impl Default for Instance { /// Creates a new instance of wgpu with default options. /// /// Backends are set to `Backends::all()`, and FXC is chosen as the `dx12_shader_compiler`. + /// + /// # Panics + /// + /// If no backend feature for the active target platform is enabled, + /// this method will panic, see [`Instance::any_backend_feature_enabled()`]. fn default() -> Self { Self::new(InstanceDescriptor::default()) } } impl Instance { + /// Returns `true` if any backend feature is enabled for the current build configuration. + /// + /// Which feature makes this method return true depends on the target platform: + /// * MacOS/iOS: `metal`, `vulkan-portability` or `angle` + /// * All other: Always returns true + /// + /// TODO: Right now it's otherwise not possible yet to opt-out of all features on most platforms. + /// See + /// * Windows: always enables Vulkan and GLES with no way to opt out + /// * Linux: always enables Vulkan and GLES with no way to opt out + /// * Web: either targets WebGPU backend or, if `webgl` enabled, WebGL + /// * TODO: Support both WebGPU and WebGL at the same time, see + pub const fn any_backend_feature_enabled() -> bool { + // Method intentionally kept verbose to keep it a bit easier to follow! + + // On macOS and iOS, at least one of Metal, Vulkan or GLES backend must be enabled. + let is_mac_or_ios = cfg!(target_os = "macos") || cfg!(target_os = "ios"); + if is_mac_or_ios { + cfg!(feature = "metal") + || cfg!(feature = "vulkan-portability") + || cfg!(feature = "angle") + } else { + true + } + } + /// Create an new instance of wgpu. /// /// # Arguments /// /// - `instance_desc` - Has fields for which [backends][Backends] wgpu will choose /// during instantiation, and which [DX12 shader compiler][Dx12Compiler] wgpu will use. + /// + /// # Panics + /// + /// If no backend feature for the active target platform is enabled, + /// this method will panic, see [`Instance::any_backend_feature_enabled()`]. pub fn new(instance_desc: InstanceDescriptor) -> Self { + if !Self::any_backend_feature_enabled() { + panic!( + "No wgpu backend feature that is implemented for the target platform was enabled. \ + See `wgpu::Instance::any_backend_feature_enabled()` for more information." + ); + } + Self { context: Arc::from(crate::backend::Context::init(instance_desc)), } @@ -2029,7 +2072,7 @@ impl Instance { /// # Safety /// /// - layer must be a valid object to create a surface upon. - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(all(any(target_os = "ios", target_os = "macos"), feature = "metal"))] pub unsafe fn create_surface_from_core_animation_layer( &self, layer: *mut std::ffi::c_void, @@ -2055,7 +2098,7 @@ impl Instance { /// # Safety /// /// - visual must be a valid IDCompositionVisual to create a surface upon. - #[cfg(target_os = "windows")] + #[cfg(all(target_os = "windows", feature = "dx12"))] pub unsafe fn create_surface_from_visual( &self, visual: *mut std::ffi::c_void, @@ -2081,7 +2124,7 @@ impl Instance { /// # Safety /// /// - surface_handle must be a valid SurfaceHandle to create a surface upon. - #[cfg(target_os = "windows")] + #[cfg(all(target_os = "windows", feature = "dx12"))] pub unsafe fn create_surface_from_surface_handle( &self, surface_handle: *mut std::ffi::c_void, @@ -2107,7 +2150,7 @@ impl Instance { /// # Safety /// /// - visual must be a valid SwapChainPanel to create a surface upon. - #[cfg(target_os = "windows")] + #[cfg(all(target_os = "windows", feature = "dx12"))] pub unsafe fn create_surface_from_swap_chain_panel( &self, swap_chain_panel: *mut std::ffi::c_void,