Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify docs for wgpu_core's Id and gfx_select!. #2766

Merged
merged 1 commit into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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