Skip to content

Commit

Permalink
Evaluate gfx_select's #[cfg] conditions at the right time.
Browse files Browse the repository at this point in the history
See the comments in the code for details.
  • Loading branch information
jimblandy committed Dec 2, 2022
1 parent 88acaf7 commit d51b803
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
120 changes: 99 additions & 21 deletions wgpu-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:
/// <https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997>
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:
Expand Down Expand Up @@ -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),

}
};
}
Expand Down

0 comments on commit d51b803

Please sign in to comment.