Skip to content

Commit

Permalink
Clarify docs for wgpu_core's Id and gfx_select!. (#2766)
Browse files Browse the repository at this point in the history
Clarify that `gfx_select!` is not specific to `hub::Global`.

Clarify that the `T` in `id<T>` is not a real resource type.
  • Loading branch information
jimblandy authored Jun 14, 2022
1 parent fd22c7f commit b30b445
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 19 deletions.
29 changes: 29 additions & 0 deletions wgpu-core/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>` 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<T>` 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<A>`], for
/// example) are always parameterized by a specific backend `A`.
///
/// So the `T` in `Id<T>` is usually a resource type like `Texture<Empty>`,
/// 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<A>`] pair up each
/// `X<Empty>` type with the resource type `X<A>`, for some specific backend
/// `A`.
///
/// [`Global`]: crate::hub::Global
/// [`Hub`]: crate::hub::Hub
/// [`Hub<A>`]: crate::hub::Hub
/// [`Storage`]: crate::hub::Storage
/// [`Texture<A>`]: 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(
Expand Down
52 changes: 33 additions & 19 deletions wgpu-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<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.
///
/// 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<A: hal::Api>(&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.
///
/// [`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 {
Expand Down

0 comments on commit b30b445

Please sign in to comment.