wgpu_core/
lib.rs

1//! This library safely implements WebGPU on native platforms.
2//! It is designed for integration into browsers, as well as wrapping
3//! into other language-specific user-friendly libraries.
4//!
5//! ## Feature flags
6#![doc = document_features::document_features!()]
7//!
8
9// When we have no backends, we end up with a lot of dead or otherwise unreachable code.
10#![cfg_attr(
11    all(
12        not(all(feature = "vulkan", not(target_arch = "wasm32"))),
13        not(all(feature = "metal", any(target_os = "macos", target_os = "ios"))),
14        not(all(feature = "dx12", windows)),
15        not(feature = "gles"),
16    ),
17    allow(unused, clippy::let_and_return)
18)]
19#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
20#![allow(
21    // It is much clearer to assert negative conditions with eq! false
22    clippy::bool_assert_comparison,
23    // We use loops for getting early-out of scope without closures.
24    clippy::never_loop,
25    // We don't use syntax sugar where it's not necessary.
26    clippy::match_like_matches_macro,
27    // Redundant matching is more explicit.
28    clippy::redundant_pattern_matching,
29    // Explicit lifetimes are often easier to reason about.
30    clippy::needless_lifetimes,
31    // No need for defaults in the internal types.
32    clippy::new_without_default,
33    // Needless updates are more scalable, easier to play with features.
34    clippy::needless_update,
35    // Need many arguments for some core functions to be able to re-use code in many situations.
36    clippy::too_many_arguments,
37    // For some reason `rustc` can warn about these in const generics even
38    // though they are required.
39    unused_braces,
40    // It gets in the way a lot and does not prevent bugs in practice.
41    clippy::pattern_type_mismatch,
42    // `wgpu-core` isn't entirely user-facing, so it's useful to document internal items.
43    rustdoc::private_intra_doc_links
44)]
45#![warn(
46    trivial_casts,
47    trivial_numeric_casts,
48    unsafe_op_in_unsafe_fn,
49    unused_extern_crates,
50    unused_qualifications
51)]
52
53pub mod binding_model;
54pub mod command;
55mod conv;
56pub mod device;
57pub mod error;
58pub mod global;
59pub mod hal_api;
60mod hash_utils;
61pub mod hub;
62pub mod id;
63pub mod identity;
64mod init_tracker;
65pub mod instance;
66mod lock;
67pub mod pipeline;
68mod pool;
69pub mod present;
70pub mod registry;
71pub mod resource;
72mod snatch;
73pub mod storage;
74mod track;
75// This is public for users who pre-compile shaders while still wanting to
76// preserve all run-time checks that `wgpu-core` does.
77// See <https://github.com/gfx-rs/wgpu/issues/3103>, after which this can be
78// made private again.
79pub mod validation;
80
81pub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_ATTACHMENTS, MAX_VERTEX_BUFFERS};
82pub use naga;
83
84use std::{borrow::Cow, os::raw::c_char};
85
86pub(crate) use hash_utils::*;
87
88/// The index of a queue submission.
89///
90/// These are the values stored in `Device::fence`.
91type SubmissionIndex = hal::FenceValue;
92
93type Index = u32;
94type Epoch = u32;
95
96pub type RawString = *const c_char;
97pub type Label<'a> = Option<Cow<'a, str>>;
98
99trait LabelHelpers<'a> {
100    fn borrow_option(&'a self) -> Option<&'a str>;
101    fn to_hal(&'a self, flags: wgt::InstanceFlags) -> Option<&'a str>;
102    fn borrow_or_default(&'a self) -> &'a str;
103}
104impl<'a> LabelHelpers<'a> for Label<'a> {
105    fn borrow_option(&'a self) -> Option<&'a str> {
106        self.as_ref().map(|cow| cow.as_ref())
107    }
108    fn to_hal(&'a self, flags: wgt::InstanceFlags) -> Option<&'a str> {
109        if flags.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS) {
110            return None;
111        }
112
113        self.as_ref().map(|cow| cow.as_ref())
114    }
115    fn borrow_or_default(&'a self) -> &'a str {
116        self.borrow_option().unwrap_or_default()
117    }
118}
119
120pub fn hal_label(opt: Option<&str>, flags: wgt::InstanceFlags) -> Option<&str> {
121    if flags.contains(wgt::InstanceFlags::DISCARD_HAL_LABELS) {
122        return None;
123    }
124
125    opt
126}
127
128const DOWNLEVEL_WARNING_MESSAGE: &str = "The underlying API or device in use does not \
129support enough features to be a fully compliant implementation of WebGPU. A subset of the features can still be used. \
130If you are running this program on native and not in a browser and wish to limit the features you use to the supported subset, \
131call Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \
132platform supports.";
133const DOWNLEVEL_ERROR_MESSAGE: &str = "This is not an invalid use of WebGPU: the underlying API or device does not \
134support enough features to be a fully compliant implementation. A subset of the features can still be used. \
135If you are running this program on native and not in a browser and wish to work around this issue, call \
136Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \
137platform supports.";
138
139// #[cfg] attributes in exported macros are interesting!
140//
141// The #[cfg] conditions in a macro's expansion are evaluated using the
142// configuration options (features, target architecture and os, etc.) in force
143// where the macro is *used*, not where it is *defined*. That is, if crate A
144// defines a macro like this:
145//
146//     #[macro_export]
147//     macro_rules! if_bleep {
148//         { } => {
149//             #[cfg(feature = "bleep")]
150//             bleep();
151//         }
152//     }
153//
154// and then crate B uses it like this:
155//
156//     fn f() {
157//         if_bleep! { }
158//     }
159//
160// then it is crate B's `"bleep"` feature, not crate A's, that determines
161// whether the macro expands to a function call or an empty statement. The
162// entire configuration predicate is evaluated in the use's context, not the
163// definition's.
164//
165// Since `wgpu-core` selects back ends using features, we need to make sure the
166// arms of the `gfx_select!` macro are pruned according to `wgpu-core`'s
167// features, not those of whatever crate happens to be using `gfx_select!`. This
168// means we can't use `#[cfg]` attributes in `gfx_select!`s definition itself.
169// Instead, for each backend, `gfx_select!` must use a macro whose definition is
170// selected by `#[cfg]` in `wgpu-core`. The configuration predicate is still
171// evaluated when the macro is used; we've just moved the `#[cfg]` into a macro
172// used by `wgpu-core` itself.
173
174/// Define an exported macro named `$public` that expands to an expression if
175/// the feature `$feature` is enabled, or to a panic otherwise.
176///
177/// This is used in the definition of `gfx_select!`, to dispatch the
178/// call to the appropriate backend, but panic if that backend was not
179/// compiled in.
180///
181/// For a call like this:
182///
183/// ```ignore
184/// define_backend_caller! { name, private, "feature" if cfg_condition }
185/// ```
186///
187/// define a macro `name`, used like this:
188///
189/// ```ignore
190/// name!(expr)
191/// ```
192///
193/// that expands to `expr` if `#[cfg(cfg_condition)]` is enabled, or a
194/// panic otherwise. The panic message complains that `"feature"` is
195/// not enabled.
196///
197/// Because of odd technical limitations on exporting macros expanded
198/// by other macros, you must supply both a public-facing name for the
199/// macro and a private name, `$private`, which is never used
200/// outside this macro. For details:
201/// <https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997>
202macro_rules! define_backend_caller {
203    { $public:ident, $private:ident, $feature:literal if $cfg:meta } => {
204        #[cfg($cfg)]
205        #[macro_export]
206        macro_rules! $private {
207            ( $call:expr ) => ( $call )
208        }
209
210        #[cfg(not($cfg))]
211        #[macro_export]
212        macro_rules! $private {
213            ( $call:expr ) => (
214                panic!("Identifier refers to disabled backend feature {:?}", $feature)
215            )
216        }
217
218        // See note about rust-lang#52234 above.
219        #[doc(hidden)] pub use $private as $public;
220    }
221}
222
223// Define a macro for each `gfx_select!` match arm. For example,
224//
225//     gfx_if_vulkan!(expr)
226//
227// expands to `expr` if the `"vulkan"` feature is enabled, or to a panic
228// otherwise.
229define_backend_caller! { gfx_if_vulkan, gfx_if_vulkan_hidden, "vulkan" if all(feature = "vulkan", not(target_arch = "wasm32")) }
230define_backend_caller! { gfx_if_metal, gfx_if_metal_hidden, "metal" if all(feature = "metal", any(target_os = "macos", target_os = "ios")) }
231define_backend_caller! { gfx_if_dx12, gfx_if_dx12_hidden, "dx12" if all(feature = "dx12", windows) }
232define_backend_caller! { gfx_if_gles, gfx_if_gles_hidden, "gles" if feature = "gles" }
233define_backend_caller! { gfx_if_empty, gfx_if_empty_hidden, "empty" if all(
234    not(any(feature = "metal", feature = "vulkan", feature = "gles")),
235    any(target_os = "macos", target_os = "ios"),
236) }
237
238/// Dispatch on an [`Id`]'s backend to a backend-generic method.
239///
240/// Uses of this macro have the form:
241///
242/// ```ignore
243///
244///     gfx_select!(id => value.method(args...))
245///
246/// ```
247///
248/// This expands to an expression that calls `value.method::<A>(args...)` for
249/// the backend `A` selected by `id`. The expansion matches on `id.backend()`,
250/// with an arm for each backend type in [`wgpu_types::Backend`] which calls the
251/// specialization of `method` for the given backend. This allows resource
252/// identifiers to select backends dynamically, even though many `wgpu_core`
253/// methods are compiled and optimized for a specific back end.
254///
255/// This macro is typically used to call methods on [`wgpu_core::global::Global`],
256/// many of which take a single `hal::Api` type parameter. For example, to
257/// create a new buffer on the device indicated by `device_id`, one would say:
258///
259/// ```ignore
260/// gfx_select!(device_id => global.device_create_buffer(device_id, ...))
261/// ```
262///
263/// where the `device_create_buffer` method is defined like this:
264///
265/// ```ignore
266/// impl Global {
267///    pub fn device_create_buffer<A: HalApi>(&self, ...) -> ...
268///    { ... }
269/// }
270/// ```
271///
272/// That `gfx_select!` call uses `device_id`'s backend to select the right
273/// backend type `A` for a call to `Global::device_create_buffer<A>`.
274///
275/// However, there's nothing about this macro that is specific to `hub::Global`.
276/// For example, Firefox's embedding of `wgpu_core` defines its own types with
277/// methods that take `hal::Api` type parameters. Firefox uses `gfx_select!` to
278/// dynamically dispatch to the right specialization based on the resource's id.
279///
280/// [`wgpu_types::Backend`]: wgt::Backend
281/// [`wgpu_core::global::Global`]: crate::global::Global
282/// [`Id`]: id::Id
283#[macro_export]
284macro_rules! gfx_select {
285    // Simple two-component expression, like `self.0.method(..)`.
286    ($id:expr => $c0:ident.$c1:tt.$method:ident $params:tt) => {
287        $crate::gfx_select!($id => {$c0.$c1}, $method $params)
288    };
289
290    // Simple identifier-only expression, like `global.method(..)`.
291    ($id:expr => $c0:ident.$method:ident $params:tt) => {
292        $crate::gfx_select!($id => {$c0}, $method $params)
293    };
294
295    ($id:expr => {$($c:tt)*}, $method:ident $params:tt) => {
296        match $id.backend() {
297            wgt::Backend::Vulkan => $crate::gfx_if_vulkan!($($c)*.$method::<$crate::api::Vulkan> $params),
298            wgt::Backend::Metal => $crate::gfx_if_metal!($($c)*.$method::<$crate::api::Metal> $params),
299            wgt::Backend::Dx12 => $crate::gfx_if_dx12!($($c)*.$method::<$crate::api::Dx12> $params),
300            wgt::Backend::Gl => $crate::gfx_if_gles!($($c)*.$method::<$crate::api::Gles> $params),
301            wgt::Backend::Empty => $crate::gfx_if_empty!($($c)*.$method::<$crate::api::Empty> $params),
302            other => panic!("Unexpected backend {:?}", other),
303        }
304    };
305}
306
307#[cfg(feature = "api_log_info")]
308macro_rules! api_log {
309    ($($arg:tt)+) => (log::info!($($arg)+))
310}
311#[cfg(not(feature = "api_log_info"))]
312macro_rules! api_log {
313    ($($arg:tt)+) => (log::trace!($($arg)+))
314}
315pub(crate) use api_log;
316
317#[cfg(feature = "resource_log_info")]
318macro_rules! resource_log {
319    ($($arg:tt)+) => (log::info!($($arg)+))
320}
321#[cfg(not(feature = "resource_log_info"))]
322macro_rules! resource_log {
323    ($($arg:tt)+) => (log::trace!($($arg)+))
324}
325pub(crate) use resource_log;
326
327#[inline]
328pub(crate) fn get_lowest_common_denom(a: u32, b: u32) -> u32 {
329    let gcd = if a >= b {
330        get_greatest_common_divisor(a, b)
331    } else {
332        get_greatest_common_divisor(b, a)
333    };
334    a * b / gcd
335}
336
337#[inline]
338pub(crate) fn get_greatest_common_divisor(mut a: u32, mut b: u32) -> u32 {
339    assert!(a >= b);
340    loop {
341        let c = a % b;
342        if c == 0 {
343            return b;
344        } else {
345            a = b;
346            b = c;
347        }
348    }
349}
350
351#[test]
352fn test_lcd() {
353    assert_eq!(get_lowest_common_denom(2, 2), 2);
354    assert_eq!(get_lowest_common_denom(2, 3), 6);
355    assert_eq!(get_lowest_common_denom(6, 4), 12);
356}
357
358#[test]
359fn test_gcd() {
360    assert_eq!(get_greatest_common_divisor(5, 1), 1);
361    assert_eq!(get_greatest_common_divisor(4, 2), 2);
362    assert_eq!(get_greatest_common_divisor(6, 4), 2);
363    assert_eq!(get_greatest_common_divisor(7, 7), 7);
364}