1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 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. Disale 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.
//! - **`trace`** --- Enable API tracing.
//! - **`replay`** --- Enable API replaying
//! - **`serial-pass`** --- Enable serializable compute/render passes, and bundle encoders.
//! - **`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 scaleable, 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);
}