//! This library safely implements WebGPU on native platforms. //! It is designed for integration into browsers, as well as wrapping //! into other language-specific user-friendly libraries. //! //! ## Feature flags #![doc = document_features::document_features!()] //! // When we have no backends, we end up with a lot of dead or otherwise unreachable code. #![cfg_attr( all( not(all(feature = "vulkan", not(target_arch = "wasm32"))), not(all(feature = "metal", any(target_os = "macos", target_os = "ios"))), not(all(feature = "dx12", windows)), not(feature = "gles"), ), allow(unused, clippy::let_and_return) )] #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #![allow( // It is much clearer to assert negative conditions with eq! false clippy::bool_assert_comparison, // We use loops for getting early-out of scope without closures. clippy::never_loop, // We don't use syntax sugar where it's not necessary. clippy::match_like_matches_macro, // Redundant matching is more explicit. clippy::redundant_pattern_matching, // Explicit lifetimes are often easier to reason about. clippy::needless_lifetimes, // No need for defaults in the internal types. clippy::new_without_default, // Needless updates are more scalable, easier to play with features. clippy::needless_update, // Need many arguments for some core functions to be able to re-use code in many situations. clippy::too_many_arguments, // For some reason `rustc` can warn about these in const generics even // though they are required. unused_braces, // It gets in the way a lot and does not prevent bugs in practice. clippy::pattern_type_mismatch, )] #![warn( trivial_casts, trivial_numeric_casts, unsafe_op_in_unsafe_fn, unused_extern_crates, unused_qualifications )] pub mod any_surface; pub mod binding_model; pub mod command; mod conv; pub mod device; pub mod error; pub mod global; pub mod hal_api; mod hash_utils; pub mod hub; pub mod id; pub mod identity; mod init_tracker; pub mod instance; pub mod pipeline; mod pool; pub mod present; pub mod registry; pub mod resource; mod snatch; pub mod storage; mod track; // This is public for users who pre-compile shaders while still wanting to // preserve all run-time checks that `wgpu-core` does. // See , after which this can be // made private again. pub mod validation; pub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_ATTACHMENTS, MAX_VERTEX_BUFFERS}; pub use naga; use std::{borrow::Cow, os::raw::c_char}; pub(crate) use hash_utils::*; /// The index of a queue submission. /// /// These are the values stored in `Device::fence`. type SubmissionIndex = hal::FenceValue; type Index = u32; type Epoch = u32; pub type RawString = *const c_char; pub type Label<'a> = Option>; trait LabelHelpers<'a> { fn borrow_option(&'a self) -> Option<&'a str>; fn to_hal(&'a self, flags: wgt::InstanceFlags) -> Option<&'a str>; fn borrow_or_default(&'a self) -> &'a str; } impl<'a> LabelHelpers<'a> for Label<'a> { fn borrow_option(&'a self) -> Option<&'a str> { self.as_ref().map(|cow| cow.as_ref()) } fn to_hal(&'a self, flags: wgt::InstanceFlags) -> Option<&'a str> { if flags.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS) { return None; } self.as_ref().map(|cow| cow.as_ref()) } fn borrow_or_default(&'a self) -> &'a str { self.borrow_option().unwrap_or_default() } } pub fn hal_label(opt: Option<&str>, flags: wgt::InstanceFlags) -> Option<&str> { if flags.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS) { return None; } opt } const DOWNLEVEL_WARNING_MESSAGE: &str = "The underlying API or device in use does not \ support enough features to be a fully compliant implementation of WebGPU. A subset of the features can still be used. \ If you are running this program on native and not in a browser and wish to limit the features you use to the supported subset, \ call Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \ platform supports."; const DOWNLEVEL_ERROR_MESSAGE: &str = "This is not an invalid use of WebGPU: the underlying API or device does not \ support enough features to be a fully compliant implementation. A subset of the features can still be used. \ If you are running this program on native and not in a browser and wish to work around this issue, call \ 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 an expression if /// the feature `$feature` is enabled, or to a panic otherwise. /// /// This is used in the definition of `gfx_select!`, to dispatch the /// call to the appropriate backend, but panic if that backend was not /// compiled in. /// /// For a call like this: /// /// ```ignore /// define_backend_caller! { name, private, "feature" if cfg_condition } /// ``` /// /// define a macro `name`, used like this: /// /// ```ignore /// name!(expr) /// ``` /// /// that expands to `expr` if `#[cfg(cfg_condition)]` is enabled, or a /// panic otherwise. The panic message complains that `"feature"` is /// not enabled. /// /// Because of odd technical limitations on exporting macros expanded /// by other macros, you must supply both a public-facing name for the /// macro and a private name, `$private`, which is never used /// outside this macro. For details: /// macro_rules! define_backend_caller { { $public:ident, $private:ident, $feature:literal if $cfg:meta } => { #[cfg($cfg)] #[macro_export] macro_rules! $private { ( $call:expr ) => ( $call ) } #[cfg(not($cfg))] #[macro_export] macro_rules! $private { ( $call:expr ) => ( panic!("Identifier refers to disabled backend feature {:?}", $feature) ) } // See note about rust-lang#52234 above. #[doc(hidden)] pub use $private as $public; } } // Define a macro for each `gfx_select!` match arm. For example, // // gfx_if_vulkan!(expr) // // expands to `expr` if the `"vulkan"` feature is enabled, or to a panic // otherwise. define_backend_caller! { gfx_if_vulkan, gfx_if_vulkan_hidden, "vulkan" if all(feature = "vulkan", not(target_arch = "wasm32")) } 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. /// /// Uses of this macro have the form: /// /// ```ignore /// /// gfx_select!(id => value.method(args...)) /// /// ``` /// /// This expands to an expression that calls `value.method::(args...)` for /// the backend `A` selected by `id`. The expansion matches on `id.backend()`, /// with an arm for each backend type in [`wgpu_types::Backend`] which calls the /// specialization of `method` for the given backend. This allows resource /// identifiers to select backends dynamically, even though many `wgpu_core` /// methods are compiled and optimized for a specific back end. /// /// This macro is typically used to call methods on [`wgpu_core::global::Global`], /// many of which take a single `hal::Api` type parameter. For example, to /// create a new buffer on the device indicated by `device_id`, one would say: /// /// ```ignore /// gfx_select!(device_id => global.device_create_buffer(device_id, ...)) /// ``` /// /// where the `device_create_buffer` method is defined like this: /// /// ```ignore /// impl Global { /// pub fn device_create_buffer(&self, ...) -> ... /// { ... } /// } /// ``` /// /// That `gfx_select!` call uses `device_id`'s backend to select the right /// backend type `A` for a call to `Global::device_create_buffer`. /// /// However, there's nothing about this macro that is specific to `hub::Global`. /// For example, Firefox's embedding of `wgpu_core` defines its own types with /// methods that take `hal::Api` type parameters. Firefox uses `gfx_select!` to /// dynamically dispatch to the right specialization based on the resource's id. /// /// [`wgpu_types::Backend`]: wgt::Backend /// [`wgpu_core::global::Global`]: crate::global::Global /// [`Id`]: id::Id #[macro_export] macro_rules! gfx_select { // Simple two-component expression, like `self.0.method(..)`. ($id:expr => $c0:ident.$c1:tt.$method:ident $params:tt) => { $crate::gfx_select!($id => {$c0.$c1}, $method $params) }; // Simple identifier-only expression, like `global.method(..)`. ($id:expr => $c0:ident.$method:ident $params:tt) => { $crate::gfx_select!($id => {$c0}, $method $params) }; ($id:expr => {$($c:tt)*}, $method:ident $params:tt) => { match $id.backend() { wgt::Backend::Vulkan => $crate::gfx_if_vulkan!($($c)*.$method::<$crate::api::Vulkan> $params), wgt::Backend::Metal => $crate::gfx_if_metal!($($c)*.$method::<$crate::api::Metal> $params), wgt::Backend::Dx12 => $crate::gfx_if_dx12!($($c)*.$method::<$crate::api::Dx12> $params), wgt::Backend::Gl => $crate::gfx_if_gles!($($c)*.$method::<$crate::api::Gles> $params), wgt::Backend::Empty => $crate::gfx_if_empty!($($c)*.$method::<$crate::api::Empty> $params), other => panic!("Unexpected backend {:?}", other), } }; } #[cfg(feature = "api_log_info")] macro_rules! api_log { ($($arg:tt)+) => (log::info!($($arg)+)) } #[cfg(not(feature = "api_log_info"))] macro_rules! api_log { ($($arg:tt)+) => (log::trace!($($arg)+)) } pub(crate) use api_log; #[cfg(feature = "resource_log_info")] macro_rules! resource_log { ($($arg:tt)+) => (log::info!($($arg)+)) } #[cfg(not(feature = "resource_log_info"))] macro_rules! resource_log { ($($arg:tt)+) => (log::trace!($($arg)+)) } pub(crate) use resource_log; #[inline] pub(crate) fn get_lowest_common_denom(a: u32, b: u32) -> u32 { let gcd = if a >= b { get_greatest_common_divisor(a, b) } else { get_greatest_common_divisor(b, a) }; a * b / gcd } #[inline] pub(crate) fn get_greatest_common_divisor(mut a: u32, mut b: u32) -> u32 { assert!(a >= b); loop { let c = a % b; if c == 0 { return b; } else { a = b; b = c; } } } #[test] fn test_lcd() { assert_eq!(get_lowest_common_denom(2, 2), 2); assert_eq!(get_lowest_common_denom(2, 3), 6); assert_eq!(get_lowest_common_denom(6, 4), 12); } #[test] fn test_gcd() { assert_eq!(get_greatest_common_divisor(5, 1), 1); assert_eq!(get_greatest_common_divisor(4, 2), 2); assert_eq!(get_greatest_common_divisor(6, 4), 2); assert_eq!(get_greatest_common_divisor(7, 7), 7); }