From d51b8032256cfe6a3a42f33f7de0ce6f04bd5bff Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Thu, 1 Dec 2022 18:48:14 -0800 Subject: [PATCH] Evaluate gfx_select's #[cfg] conditions at the right time. See the comments in the code for details. --- CHANGELOG.md | 1 + wgpu-core/src/lib.rs | 120 +++++++++++++++++++++++++++++++++++-------- 2 files changed, 100 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb07c09bcde..dd841e7d15d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -118,6 +118,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non - Fix an integer overflow in `queue_write_texture` by @nical in (#3146)[https://github.com/gfx-rs/wgpu/pull/3146] - Make `RenderPassCompatibilityError` and `CreateShaderModuleError` not so huge. By @jimblandy in (#3226)[https://github.com/gfx-rs/wgpu/pull/3226] - Check for invalid bitflag bits in wgpu-core and allow them to be captured/replayed by @nical in (#3229)[https://github.com/gfx-rs/wgpu/pull/3229] +- Evaluate `gfx_select!`'s `#[cfg]` conditions at the right time. By @jimblandy in [#3253](https://github.com/gfx-rs/wgpu/pull/3253) #### WebGPU diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index ce0f7f50871..86f6c469aab 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -243,6 +243,100 @@ If you are running this program on native and not in a browser and wish to work Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \ platform supports."; +// #[cfg] attributes in exported macros are interesting! +// +// The #[cfg] conditions in a macro's expansion are evaluated using the +// configuration options (features, target architecture and os, etc.) in force +// where the macro is *used*, not where it is *defined*. That is, if crate A +// defines a macro like this: +// +// #[macro_export] +// macro_rules! if_bleep { +// { } => { +// #[cfg(feature = "bleep")] +// bleep(); +// } +// } +// +// and then crate B uses it like this: +// +// fn f() { +// if_bleep! { } +// } +// +// then it is crate B's `"bleep"` feature, not crate A's, that determines +// whether the macro expands to a function call or an empty statement. The +// entire configuration predicate is evaluated in the use's context, not the +// definition's. +// +// Since `wgpu-core` selects back ends using features, we need to make sure the +// arms of the `gfx_select!` macro are pruned according to `wgpu-core`'s +// features, not those of whatever crate happens to be using `gfx_select!`. This +// means we can't use `#[cfg]` attributes in `gfx_select!`s definition itself. +// Instead, for each backend, `gfx_select!` must use a macro whose definition is +// selected by `#[cfg]` in `wgpu-core`. The configuration predicate is still +// evaluated when the macro is used; we've just moved the `#[cfg]` into a macro +// used by `wgpu-core` itself. + +/// Define an exported macro named `$public` that expands to a call if the +/// configuration predicate `$condition` is true, or to a panic otherwise. +/// +/// Because of odd technical limitations on exporting macros expanded by other +/// macros, you must supply a both a public-facing name for the macro and a +/// private name, which is never used outside this macro. For details: +/// +macro_rules! define_backend_caller { + { $public:ident, $private:ident if $condition:meta } => { + #[cfg( $condition )] + #[macro_export] + macro_rules! $private { + ( $backend:literal, $call:expr ) => ( $call ) + } + + #[cfg(not( $condition ))] + #[macro_export] + macro_rules! $private { + ( $backend:literal, $call:expr ) => ( + panic!("Unexpected backend {:?}", $backend) + ) + } + + #[doc(hidden)] pub use $private as $public; + } +} + +define_backend_caller! { + gfx_if_vulkan, gfx_if_vulkan_hidden + if any( + all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")), + feature = "vulkan-portability" + ) +} + +define_backend_caller! { + gfx_if_metal, gfx_if_metal_hidden + if all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")) +} + +define_backend_caller! { + gfx_if_dx12, gfx_if_dx12_hidden + if all(not(target_arch = "wasm32"), windows) +} + +define_backend_caller! { + gfx_if_dx11, gfx_if_dx11_hidden + if all(not(target_arch = "wasm32"), windows) +} + +define_backend_caller! { + gfx_if_gles, gfx_if_gles_hidden + if any( + all(unix, not(target_os = "macos"), not(target_os = "ios")), + feature = "angle", + target_arch = "wasm32" + ) +} + /// Dispatch on an [`Id`]'s backend to a backend-generic method. /// /// Uses of this macro have the form: @@ -291,29 +385,13 @@ platform supports."; #[macro_export] macro_rules! gfx_select { ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => { - // Note: For some reason the cfg aliases defined in build.rs - // don't succesfully apply in this macro so we must specify - // their equivalents manually. match $id.backend() { - #[cfg(any( - all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")), - feature = "vulkan-portability" - ))] - wgt::Backend::Vulkan => $global.$method::<$crate::api::Vulkan>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))] - wgt::Backend::Metal => $global.$method::<$crate::api::Metal>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), windows))] - wgt::Backend::Dx12 => $global.$method::<$crate::api::Dx12>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), windows))] - wgt::Backend::Dx11 => $global.$method::<$crate::api::Dx11>( $($param),* ), - #[cfg(any( - all(unix, not(target_os = "macos"), not(target_os = "ios")), - feature = "angle", - target_arch = "wasm32" - ))] - wgt::Backend::Gl => $global.$method::<$crate::api::Gles>( $($param),+ ), + wgt::Backend::Vulkan => $crate::gfx_if_vulkan!("Vulkan", $global.$method::<$crate::api::Vulkan>( $($param),* )), + wgt::Backend::Metal => $crate::gfx_if_metal!("Metal", $global.$method::<$crate::api::Metal>( $($param),* )), + wgt::Backend::Dx12 => $crate::gfx_if_dx12!("Dx12", $global.$method::<$crate::api::Dx12>( $($param),* )), + wgt::Backend::Dx11 => $crate::gfx_if_dx11!("Dx11", $global.$method::<$crate::api::Dx11>( $($param),* )), + wgt::Backend::Gl => $crate::gfx_if_gles!("Gles", $global.$method::<$crate::api::Gles>( $($param),+ )), other => panic!("Unexpected backend {:?}", other), - } }; }