diff options
Diffstat (limited to 'third_party/rust/wgpu-core/src/lib.rs')
-rw-r--r-- | third_party/rust/wgpu-core/src/lib.rs | 390 |
1 files changed, 390 insertions, 0 deletions
diff --git a/third_party/rust/wgpu-core/src/lib.rs b/third_party/rust/wgpu-core/src/lib.rs new file mode 100644 index 0000000000..9f6526fc11 --- /dev/null +++ b/third_party/rust/wgpu-core/src/lib.rs @@ -0,0 +1,390 @@ +//! 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 +// NOTE: feature docs. below should be kept in sync. with `Cargo.toml`! +//! +//! - **`api_log_info`** --- Log all API entry points at info instead of trace level. +//! - **`resource_log_info`** --- Log resource lifecycle management at info instead of trace level. +//! - **`link`** _(enabled by default)_ --- Use static linking for libraries. Disable to manually +//! link. Enabled by default. +//! - **`renderdoc`** --- Support the Renderdoc graphics debugger: +//! [https://renderdoc.org/](https://renderdoc.org/) +//! - **`strict_asserts`** --- Apply run-time checks, even in release builds. These are in addition +//! to the validation carried out at public APIs in all builds. +//! - **`serde`** --- Enables serialization via `serde` on common wgpu types. +//! - **`trace`** --- Enable API tracing. +//! - **`replay`** --- Enable API replaying +//! - **`wgsl`** --- Enable `ShaderModuleSource::Wgsl` +//! - **`fragile-send-sync-non-atomic-wasm`** --- Implement `Send` and `Sync` on Wasm, but only if +//! atomics are not enabled. +//! +//! WebGL/WebGPU objects can not be shared between threads. However, it can be useful to +//! artificially mark them as `Send` and `Sync` anyways to make it easier to write cross-platform +//! code. This is technically _very_ unsafe in a multithreaded environment, but on a wasm binary +//! compiled without atomics we know we are definitely not in a multithreaded environment. +//! +//! ### Backends, passed through to wgpu-hal +//! +//! - **`metal`** --- Enable the `metal` backend. +//! - **`vulkan`** --- Enable the `vulkan` backend. +//! - **`gles`** --- Enable the `GLES` backend. +//! +//! This is used for all of GLES, OpenGL, and WebGL. +//! - **`dx12`** --- Enable the `dx12` backend. + +// 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 <https://github.com/gfx-rs/wgpu/issues/3103>, 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<Cow<'a, str>>; + +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: +/// <https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997> +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::<A>(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<A: HalApi>(&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<A>`. +/// +/// 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); +} |