diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 0955857235..ea28fec642 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -22,6 +22,35 @@ const BACKEND_SHIFT: usize = INDEX_BITS * 2 - BACKEND_BITS; pub const EPOCH_MASK: u32 = (1 << (EPOCH_BITS)) - 1; type Dummy = hal::api::Empty; +/// An identifier for a wgpu object. +/// +/// An `Id` value identifies a value stored in a [`Global`]'s [`Hub`]'s [`Storage`]. +/// `Storage` implements [`Index`] and [`IndexMut`], accepting `Id` values as indices. +/// +/// ## Note on `Id` typing +/// +/// You might assume that an `Id` can only be used to retrieve a resource of +/// type `T`, but that is not quite the case. The id types in `wgpu-core`'s +/// public API ([`TextureId`], for example) can refer to resources belonging to +/// any backend, but the corresponding resource types ([`Texture`], for +/// example) are always parameterized by a specific backend `A`. +/// +/// So the `T` in `Id` is usually a resource type like `Texture`, +/// where [`Empty`] is the `wgpu_hal` dummy back end. These empty types are +/// never actually used, beyond just making sure you access each `Storage` with +/// the right kind of identifier. The members of [`Hub`] pair up each +/// `X` type with the resource type `X`, for some specific backend +/// `A`. +/// +/// [`Global`]: crate::hub::Global +/// [`Hub`]: crate::hub::Hub +/// [`Hub`]: crate::hub::Hub +/// [`Storage`]: crate::hub::Storage +/// [`Texture`]: crate::resource::Texture +/// [`Index`]: std::ops::Index +/// [`IndexMut`]: std::ops::IndexMut +/// [`Registry`]: crate::hub::Registry +/// [`Empty`]: hal::api::Empty #[repr(transparent)] #[cfg_attr(feature = "trace", derive(serde::Serialize), serde(into = "SerialId"))] #[cfg_attr( diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index 8d28a3df26..ced62e09a6 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -238,36 +238,50 @@ If you are running this program on native and not in a browser and wish to work Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \ platform supports."; -/// Call a `Global` method, dispatching dynamically to the appropriate back end. +/// Dispatch on an [`Id`]'s backend to a backend-generic method. /// /// Uses of this macro have the form: /// /// ```ignore /// -/// gfx_select!(id => global.method(args...)) +/// gfx_select!(id => value.method(args...)) /// /// ``` /// -/// where `id` is some [`id::Id`] resource id, `global` is a [`hub::Global`], -/// and `method` is any method on [`Global`] that takes a single generic -/// parameter that implements [`hal::Api`] (for example, -/// [`Global::device_create_buffer`]). +/// 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. /// -/// The `wgpu-core` crate can support multiple back ends simultaneously (Vulkan, -/// Metal, etc.), depending on features and availability. Each [`Id`]'s value -/// indicates which back end its resource belongs to. This macro does a switch -/// on `id`'s back end, and calls the `Global` method specialized for that back -/// end. +/// This macro is typically used to call methods on [`wgpu_core::hub::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: /// -/// Internally to `wgpu-core`, most types take the back end (some type that -/// implements `hal::Api`) as a generic parameter, so their methods are compiled -/// with full knowledge of which back end they're working with. This macro -/// serves as the boundary between dynamic `Id` values provided by `wgpu-core`'s -/// users and the crate's mostly-monomorphized implementation, selecting the -/// `hal::Api` implementation appropriate to the `Id` value's back end. +/// ```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. /// -/// [`Global`]: hub::Global -/// [`Global::device_create_buffer`]: hub::Global::device_create_buffer +/// [`wgpu_types::Backend`]: wgt::Backend +/// [`wgpu_core::hub::Global`]: crate::hub::Global /// [`Id`]: id::Id #[macro_export] macro_rules! gfx_select {