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
6// When we have no backends, we end up with a lot of dead or otherwise unreachable code.
7#![cfg_attr(
8 all(
9 not(all(feature = "vulkan", not(target_arch = "wasm32"))),
10 not(all(feature = "metal", any(target_os = "macos", target_os = "ios"))),
11 not(all(feature = "dx12", windows)),
12 not(all(feature = "dx11", windows)),
13 not(feature = "gles"),
14 ),
15 allow(unused, clippy::let_and_return)
16)]
17#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
18#![allow(
19 // It is much clearer to assert negative conditions with eq! false
20 clippy::bool_assert_comparison,
21 // We use loops for getting early-out of scope without closures.
22 clippy::never_loop,
23 // We don't use syntax sugar where it's not necessary.
24 clippy::match_like_matches_macro,
25 // Redundant matching is more explicit.
26 clippy::redundant_pattern_matching,
27 // Explicit lifetimes are often easier to reason about.
28 clippy::needless_lifetimes,
29 // No need for defaults in the internal types.
30 clippy::new_without_default,
31 // Needless updates are more scaleable, easier to play with features.
32 clippy::needless_update,
33 // Need many arguments for some core functions to be able to re-use code in many situations.
34 clippy::too_many_arguments,
35 // For some reason `rustc` can warn about these in const generics even
36 // though they are required.
37 unused_braces,
38 // Clashes with clippy::pattern_type_mismatch
39 clippy::needless_borrowed_reference,
40)]
41#![warn(
42 trivial_casts,
43 trivial_numeric_casts,
44 unsafe_op_in_unsafe_fn,
45 unused_extern_crates,
46 unused_qualifications,
47 // We don't match on a reference, unless required.
48 clippy::pattern_type_mismatch,
49)]
50
51pub mod binding_model;
52pub mod command;
53mod conv;
54pub mod device;
55pub mod error;
56pub mod global;
57pub mod hal_api;
58pub mod hub;
59pub mod id;
60pub mod identity;
61mod init_tracker;
62pub mod instance;
63pub mod pipeline;
64pub mod present;
65pub mod registry;
66pub mod resource;
67pub mod storage;
68mod track;
69mod validation;
70
71pub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_ATTACHMENTS, MAX_VERTEX_BUFFERS};
72
73use atomic::{AtomicUsize, Ordering};
74
75use std::{borrow::Cow, os::raw::c_char, ptr, sync::atomic};
76
77/// The index of a queue submission.
78///
79/// These are the values stored in `Device::fence`.
80type SubmissionIndex = hal::FenceValue;
81
82type Index = u32;
83type Epoch = u32;
84
85pub type RawString = *const c_char;
86pub type Label<'a> = Option<Cow<'a, str>>;
87
88trait LabelHelpers<'a> {
89 fn borrow_option(&'a self) -> Option<&'a str>;
90 fn borrow_or_default(&'a self) -> &'a str;
91}
92impl<'a> LabelHelpers<'a> for Label<'a> {
93 fn borrow_option(&'a self) -> Option<&'a str> {
94 self.as_ref().map(|cow| cow.as_ref())
95 }
96 fn borrow_or_default(&'a self) -> &'a str {
97 self.borrow_option().unwrap_or_default()
98 }
99}
100
101/// Reference count object that is 1:1 with each reference.
102///
103/// All the clones of a given `RefCount` point to the same
104/// heap-allocated atomic reference count. When the count drops to
105/// zero, only the count is freed. No other automatic cleanup takes
106/// place; this is just a reference count, not a smart pointer.
107///
108/// `RefCount` values are created only by [`LifeGuard::new`] and by
109/// `Clone`, so every `RefCount` is implicitly tied to some
110/// [`LifeGuard`].
111#[derive(Debug)]
112struct RefCount(ptr::NonNull<AtomicUsize>);
113
114unsafe impl Send for RefCount {}
115unsafe impl Sync for RefCount {}
116
117impl RefCount {
118 const MAX: usize = 1 << 24;
119
120 /// Construct a new `RefCount`, with an initial count of 1.
121 fn new() -> RefCount {
122 let bx = Box::new(AtomicUsize::new(1));
123 Self(unsafe { ptr::NonNull::new_unchecked(Box::into_raw(bx)) })
124 }
125
126 fn load(&self) -> usize {
127 unsafe { self.0.as_ref() }.load(Ordering::Acquire)
128 }
129}
130
131impl Clone for RefCount {
132 fn clone(&self) -> Self {
133 let old_size = unsafe { self.0.as_ref() }.fetch_add(1, Ordering::AcqRel);
134 assert!(old_size < Self::MAX);
135 Self(self.0)
136 }
137}
138
139impl Drop for RefCount {
140 fn drop(&mut self) {
141 unsafe {
142 if self.0.as_ref().fetch_sub(1, Ordering::AcqRel) == 1 {
143 drop(Box::from_raw(self.0.as_ptr()));
144 }
145 }
146 }
147}
148
149/// Reference count object that tracks multiple references.
150/// Unlike `RefCount`, it's manually inc()/dec() called.
151#[derive(Debug)]
152struct MultiRefCount(AtomicUsize);
153
154impl MultiRefCount {
155 fn new() -> Self {
156 Self(AtomicUsize::new(1))
157 }
158
159 fn inc(&self) {
160 self.0.fetch_add(1, Ordering::AcqRel);
161 }
162
163 fn dec_and_check_empty(&self) -> bool {
164 self.0.fetch_sub(1, Ordering::AcqRel) == 1
165 }
166}
167
168/// Information needed to decide when it's safe to free some wgpu-core
169/// resource.
170///
171/// Each type representing a `wgpu-core` resource, like [`Device`],
172/// [`Buffer`], etc., contains a `LifeGuard` which indicates whether
173/// it is safe to free.
174///
175/// A resource may need to be retained for any of several reasons:
176///
177/// - The user may hold a reference to it (via a `wgpu::Buffer`, say).
178///
179/// - Other resources may depend on it (a texture view's backing
180/// texture, for example).
181///
182/// - It may be used by commands sent to the GPU that have not yet
183/// finished execution.
184///
185/// [`Device`]: device::Device
186/// [`Buffer`]: resource::Buffer
187#[derive(Debug)]
188pub struct LifeGuard {
189 /// `RefCount` for the user's reference to this resource.
190 ///
191 /// When the user first creates a `wgpu-core` resource, this `RefCount` is
192 /// created along with the resource's `LifeGuard`. When the user drops the
193 /// resource, we swap this out for `None`. Note that the resource may
194 /// still be held alive by other resources.
195 ///
196 /// Any `Stored<T>` value holds a clone of this `RefCount` along with the id
197 /// of a `T` resource.
198 ref_count: Option<RefCount>,
199
200 /// The index of the last queue submission in which the resource
201 /// was used.
202 ///
203 /// Each queue submission is fenced and assigned an index number
204 /// sequentially. Thus, when a queue submission completes, we know any
205 /// resources used in that submission and any lower-numbered submissions are
206 /// no longer in use by the GPU.
207 submission_index: AtomicUsize,
208
209 /// The `label` from the descriptor used to create the resource.
210 #[cfg(debug_assertions)]
211 pub(crate) label: String,
212}
213
214impl LifeGuard {
215 #[allow(unused_variables)]
216 fn new(label: &str) -> Self {
217 Self {
218 ref_count: Some(RefCount::new()),
219 submission_index: AtomicUsize::new(0),
220 #[cfg(debug_assertions)]
221 label: label.to_string(),
222 }
223 }
224
225 fn add_ref(&self) -> RefCount {
226 self.ref_count.clone().unwrap()
227 }
228
229 /// Record that this resource will be used by the queue submission with the
230 /// given index.
231 ///
232 /// Returns `true` if the resource is still held by the user.
233 fn use_at(&self, submit_index: SubmissionIndex) -> bool {
234 self.submission_index
235 .store(submit_index as _, Ordering::Release);
236 self.ref_count.is_some()
237 }
238
239 fn life_count(&self) -> SubmissionIndex {
240 self.submission_index.load(Ordering::Acquire) as _
241 }
242}
243
244#[derive(Clone, Debug)]
245struct Stored<T> {
246 value: id::Valid<T>,
247 ref_count: RefCount,
248}
249
250const DOWNLEVEL_WARNING_MESSAGE: &str = "The underlying API or device in use does not \
251support enough features to be a fully compliant implementation of WebGPU. A subset of the features can still be used. \
252If you are running this program on native and not in a browser and wish to limit the features you use to the supported subset, \
253call Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \
254platform supports.";
255const DOWNLEVEL_ERROR_MESSAGE: &str = "This is not an invalid use of WebGPU: the underlying API or device does not \
256support enough features to be a fully compliant implementation. A subset of the features can still be used. \
257If you are running this program on native and not in a browser and wish to work around this issue, call \
258Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \
259platform supports.";
260
261// #[cfg] attributes in exported macros are interesting!
262//
263// The #[cfg] conditions in a macro's expansion are evaluated using the
264// configuration options (features, target architecture and os, etc.) in force
265// where the macro is *used*, not where it is *defined*. That is, if crate A
266// defines a macro like this:
267//
268// #[macro_export]
269// macro_rules! if_bleep {
270// { } => {
271// #[cfg(feature = "bleep")]
272// bleep();
273// }
274// }
275//
276// and then crate B uses it like this:
277//
278// fn f() {
279// if_bleep! { }
280// }
281//
282// then it is crate B's `"bleep"` feature, not crate A's, that determines
283// whether the macro expands to a function call or an empty statement. The
284// entire configuration predicate is evaluated in the use's context, not the
285// definition's.
286//
287// Since `wgpu-core` selects back ends using features, we need to make sure the
288// arms of the `gfx_select!` macro are pruned according to `wgpu-core`'s
289// features, not those of whatever crate happens to be using `gfx_select!`. This
290// means we can't use `#[cfg]` attributes in `gfx_select!`s definition itself.
291// Instead, for each backend, `gfx_select!` must use a macro whose definition is
292// selected by `#[cfg]` in `wgpu-core`. The configuration predicate is still
293// evaluated when the macro is used; we've just moved the `#[cfg]` into a macro
294// used by `wgpu-core` itself.
295
296/// Define an exported macro named `$public` that expands to an expression if
297/// the feature `$feature` is enabled, or to a panic otherwise.
298///
299/// This is used in the definition of `gfx_select!`, to dispatch the
300/// call to the appropriate backend, but panic if that backend was not
301/// compiled in.
302///
303/// For a call like this:
304///
305/// ```ignore
306/// define_backend_caller! { name, private, "feature" if cfg_condition }
307/// ```
308///
309/// define a macro `name`, used like this:
310///
311/// ```ignore
312/// name!(expr)
313/// ```
314///
315/// that expands to `expr` if `#[cfg(cfg_condition)]` is enabled, or a
316/// panic otherwise. The panic message complains that `"feature"` is
317/// not enabled.
318///
319/// Because of odd technical limitations on exporting macros expanded
320/// by other macros, you must supply both a public-facing name for the
321/// macro and a private name, `$private`, which is never used
322/// outside this macro. For details:
323/// <https://github.com/rust-lang/rust/pull/52234#issuecomment-976702997>
324macro_rules! define_backend_caller {
325 { $public:ident, $private:ident, $feature:literal if $cfg:meta } => {
326 #[cfg($cfg)]
327 #[macro_export]
328 macro_rules! $private {
329 ( $call:expr ) => ( $call )
330 }
331
332 #[cfg(not($cfg))]
333 #[macro_export]
334 macro_rules! $private {
335 ( $call:expr ) => (
336 panic!("Identifier refers to disabled backend feature {:?}", $feature)
337 )
338 }
339
340 // See note about rust-lang#52234 above.
341 #[doc(hidden)] pub use $private as $public;
342 }
343}
344
345// Define a macro for each `gfx_select!` match arm. For example,
346//
347// gfx_if_vulkan!(expr)
348//
349// expands to `expr` if the `"vulkan"` feature is enabled, or to a panic
350// otherwise.
351define_backend_caller! { gfx_if_vulkan, gfx_if_vulkan_hidden, "vulkan" if all(feature = "vulkan", not(target_arch = "wasm32")) }
352define_backend_caller! { gfx_if_metal, gfx_if_metal_hidden, "metal" if all(feature = "metal", any(target_os = "macos", target_os = "ios")) }
353define_backend_caller! { gfx_if_dx12, gfx_if_dx12_hidden, "dx12" if all(feature = "dx12", windows) }
354define_backend_caller! { gfx_if_dx11, gfx_if_dx11_hidden, "dx11" if all(feature = "dx11", windows) }
355define_backend_caller! { gfx_if_gles, gfx_if_gles_hidden, "gles" if feature = "gles" }
356
357/// Dispatch on an [`Id`]'s backend to a backend-generic method.
358///
359/// Uses of this macro have the form:
360///
361/// ```ignore
362///
363/// gfx_select!(id => value.method(args...))
364///
365/// ```
366///
367/// This expands to an expression that calls `value.method::<A>(args...)` for
368/// the backend `A` selected by `id`. The expansion matches on `id.backend()`,
369/// with an arm for each backend type in [`wgpu_types::Backend`] which calls the
370/// specialization of `method` for the given backend. This allows resource
371/// identifiers to select backends dynamically, even though many `wgpu_core`
372/// methods are compiled and optimized for a specific back end.
373///
374/// This macro is typically used to call methods on [`wgpu_core::global::Global`],
375/// many of which take a single `hal::Api` type parameter. For example, to
376/// create a new buffer on the device indicated by `device_id`, one would say:
377///
378/// ```ignore
379/// gfx_select!(device_id => global.device_create_buffer(device_id, ...))
380/// ```
381///
382/// where the `device_create_buffer` method is defined like this:
383///
384/// ```ignore
385/// impl<...> Global<...> {
386/// pub fn device_create_buffer<A: hal::Api>(&self, ...) -> ...
387/// { ... }
388/// }
389/// ```
390///
391/// That `gfx_select!` call uses `device_id`'s backend to select the right
392/// backend type `A` for a call to `Global::device_create_buffer<A>`.
393///
394/// However, there's nothing about this macro that is specific to `global::Global`.
395/// For example, Firefox's embedding of `wgpu_core` defines its own types with
396/// methods that take `hal::Api` type parameters. Firefox uses `gfx_select!` to
397/// dynamically dispatch to the right specialization based on the resource's id.
398///
399/// [`wgpu_types::Backend`]: wgt::Backend
400/// [`wgpu_core::global::Global`]: crate::global::Global
401/// [`Id`]: id::Id
402#[macro_export]
403macro_rules! gfx_select {
404 ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
405 match $id.backend() {
406 wgt::Backend::Vulkan => $crate::gfx_if_vulkan!($global.$method::<$crate::api::Vulkan>( $($param),* )),
407 wgt::Backend::Metal => $crate::gfx_if_metal!($global.$method::<$crate::api::Metal>( $($param),* )),
408 wgt::Backend::Dx12 => $crate::gfx_if_dx12!($global.$method::<$crate::api::Dx12>( $($param),* )),
409 wgt::Backend::Dx11 => $crate::gfx_if_dx11!($global.$method::<$crate::api::Dx11>( $($param),* )),
410 wgt::Backend::Gl => $crate::gfx_if_gles!($global.$method::<$crate::api::Gles>( $($param),+ )),
411 other => panic!("Unexpected backend {:?}", other),
412 }
413 };
414}
415
416/// Fast hash map used internally.
417type FastHashMap<K, V> =
418 std::collections::HashMap<K, V, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
419/// Fast hash set used internally.
420type FastHashSet<K> =
421 std::collections::HashSet<K, std::hash::BuildHasherDefault<rustc_hash::FxHasher>>;
422
423#[inline]
424pub(crate) fn get_lowest_common_denom(a: u32, b: u32) -> u32 {
425 let gcd = if a >= b {
426 get_greatest_common_divisor(a, b)
427 } else {
428 get_greatest_common_divisor(b, a)
429 };
430 a * b / gcd
431}
432
433#[inline]
434pub(crate) fn get_greatest_common_divisor(mut a: u32, mut b: u32) -> u32 {
435 assert!(a >= b);
436 loop {
437 let c = a % b;
438 if c == 0 {
439 return b;
440 } else {
441 a = b;
442 b = c;
443 }
444 }
445}
446
447#[test]
448fn test_lcd() {
449 assert_eq!(get_lowest_common_denom(2, 2), 2);
450 assert_eq!(get_lowest_common_denom(2, 3), 6);
451 assert_eq!(get_lowest_common_denom(6, 4), 12);
452}
453
454#[test]
455fn test_gcd() {
456 assert_eq!(get_greatest_common_divisor(5, 1), 1);
457 assert_eq!(get_greatest_common_divisor(4, 2), 2);
458 assert_eq!(get_greatest_common_divisor(6, 4), 2);
459 assert_eq!(get_greatest_common_divisor(7, 7), 7);
460}