[wgpu] Document dispatch_type! macro. (#8483)

Add more documentation for the `dispatch_type` macro, and provide a
more detailed explanation of how it enables devirtualization in the
single-backend case.
This commit is contained in:
Jim Blandy 2025-11-06 16:56:46 -08:00 committed by GitHub
parent 3443224e78
commit 6e06ec99ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -5,8 +5,11 @@
//!
//! The interface traits are all object safe and listed in the `InterfaceTypes` trait.
//!
//! The method for dispatching should optimize well if only one backend is compiled in,
//! as-if there was no dispatching at all.
//! The method for dispatching should optimize well if only one backend is
//! compiled in, as-if there was no dispatching at all. See the comments on
//! [`dispatch_types`] for details.
//!
//! [`dispatch_types`]: macro.dispatch_types.html
#![allow(drop_bounds)] // This exists to remind implementors to impl drop.
#![allow(clippy::too_many_arguments)] // It's fine.
@ -581,16 +584,77 @@ pub trait BufferMappedRangeInterface: CommonTraits {
fn as_uint8array(&self) -> &js_sys::Uint8Array;
}
/// Generates Dispatch types for each of the interfaces. Each type is a wrapper around the
/// wgpu_core and webgpu types, and derefs to the appropriate interface trait-object.
/// Generates a dispatch type for some `wgpu` API type.
///
/// When there is only one backend, devirtualization fires and all dispatches should turn into
/// direct calls. If there are multiple, some dispatching will occur.
/// Invocations of this macro take one of the following forms:
///
/// This also provides `as_*` methods so that the backend implementations can dereference other
/// arguments. These are similarly free when there is only one backend.
/// ```ignore
/// dispatch_types! {mut type D: I = Core, Web, Dyn }
/// dispatch_types! {ref type D: I = Core, Web, Dyn }
/// ```
///
/// In the future, we may want a truly generic backend, which could be extended from this enum.
/// This defines `D` as a type that dereferences to a `dyn I` trait object. Most uses of
/// `D` in the rest of this crate just call the methods from the `dyn I` object, not from
/// `D` itself.
///
/// Internally, `D` is an enum with up to three variants holding values of type `Core`,
/// `Web`, and `Dyn`, all of which must implement `I`. `Core`, `Web` and `Dyn` are the
/// types from the `wgpu_core`, `webgpu`, and `custom` submodules of `wgpu::backend` that
/// correspond to `D`. The macro generates `Deref` and `DerefMut` implementations that
/// match on this enum and produce a `dyn I` reference for each variant.
///
/// The macro's `mut type` form defines `D` as the unique owner of the backend type, with
/// a `DerefMut` implementation, and `as_*_mut` methods that return `&mut` references.
/// This `D` does not implement `Clone`.
///
/// The macro's `ref type` form defines `D` to hold an `Arc` pointing to the backend type,
/// permitting `Clone` and `Deref`, but losing exclusive, mutable access.
///
/// For example:
///
/// ```ignore
/// dispatch_types! {ref type DispatchBuffer: BufferInterface =
/// CoreBuffer, WebBuffer, DynBuffer}
/// ```
///
/// This defines `DispatchBuffer` as a type that dereferences to `&dyn BufferInterface`,
/// which has methods like `map_async` and `destroy`. The enum would be:
///
/// ```ignore
/// pub enum DispatchBuffer {
/// #[cfg(wgpu_core)]
/// Core(Arc<CoreBuffer>),
/// #[cfg(webgpu)]
/// WebGPU(WebBuffer),
/// #[cfg(custom)]
/// Custom(DynBuffer),
/// }
/// ```
///
/// This macro also defines `as_*` methods so that the backend implementations can
/// dereference other arguments.
///
/// ## Devirtualization
///
/// The dispatch types generated by this macro are carefully designed to allow the
/// compiler to completely devirtualize calls in most circumstances.
///
/// Note that every variant of the enum generated by this macro is under a `#[cfg]`.
/// Naturally, the `match` expressions in the `Deref` and `DerefMut` implementations have
/// matching `#[cfg]` attributes on each match arm.
///
/// In practice, when `wgpu`'s `"custom"` feature is not enabled, there is usually only
/// one variant in the `enum`, making it effectively a newtype around the sole variant's
/// data: it has no discriminant to branch on, and the `match` expressions are removed
/// entirely by the compiler.
///
/// In this case, when we invoke a method from the interface trait `I` on a dispatch type,
/// the `Deref` and `DerefMut` implementations' `match` statements build a `&dyn I` for
/// the data, on which we immediately invoke a method. The vtable is a constant, allowing
/// the Rust compiler to turn the `dyn` method call into an ordinary method call. This
/// creates opportunities for inlining.
///
/// Similarly, the `as_*` methods are free when there is only one backend.
macro_rules! dispatch_types {
(
ref type $name:ident: $interface:ident = $core_type:ident,$webgpu_type:ident,$custom_type:ident