Skip to content

Commit e2d3afd

Browse files
committed
[wgpu] Document dispatch_type! macro.
Add more documentation for the `dispatch_type` macro, and provide a more detailed explanation of how it enables devirtualization in the single-backend case.
1 parent 5633ae8 commit e2d3afd

1 file changed

Lines changed: 70 additions & 9 deletions

File tree

wgpu/src/dispatch.rs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
//!
66
//! The interface traits are all object safe and listed in the `InterfaceTypes` trait.
77
//!
8-
//! The method for dispatching should optimize well if only one backend is compiled in,
9-
//! as-if there was no dispatching at all.
8+
//! The method for dispatching should optimize well if only one backend is
9+
//! compiled in, as-if there was no dispatching at all. See the comments on
10+
//! `dispatch_types` for details.
1011
1112
#![allow(drop_bounds)] // This exists to remind implementors to impl drop.
1213
#![allow(clippy::too_many_arguments)] // It's fine.
@@ -581,16 +582,76 @@ pub trait BufferMappedRangeInterface: CommonTraits {
581582
fn as_uint8array(&self) -> &js_sys::Uint8Array;
582583
}
583584

584-
/// Generates Dispatch types for each of the interfaces. Each type is a wrapper around the
585-
/// wgpu_core and webgpu types, and derefs to the appropriate interface trait-object.
585+
/// Generates a dispatch type for some `wgpu` API type.
586586
///
587-
/// When there is only one backend, devirtualization fires and all dispatches should turn into
588-
/// direct calls. If there are multiple, some dispatching will occur.
587+
/// Invocations of this macro take one of the following forms:
589588
///
590-
/// This also provides `as_*` methods so that the backend implementations can dereference other
591-
/// arguments. These are similarly free when there is only one backend.
589+
/// ```ignore
590+
/// dispatch_types! {mut type D: I = Core, Web, Dyn }
591+
/// dispatch_types! {ref type D: I = Core, Web, Dyn }
592+
/// ```
592593
///
593-
/// In the future, we may want a truly generic backend, which could be extended from this enum.
594+
/// This defines `D` as a type that dereferences to a `dyn I` trait object. Most uses of
595+
/// `D` in the rest of this crate just call methods from `I`, not from `D` itself.
596+
///
597+
/// Internally, `D` is an enum with up to three variants holding values of type `Core`,
598+
/// `Web`, and `Dyn`, all of which must implement `I`. `Core`, `Web` and `Dyn` are the
599+
/// types from the `wgpu_core`, `webgpu`, and `custom` submodules of `wgpu::backend` that
600+
/// correspond to `D`. The macro generates `Deref` and `DerefMut` implementations that
601+
/// match on this enum and produce a `dyn I` reference for each variant.
602+
///
603+
/// The macro's `mut type` form defines `D` as the unique owner of the backend type, with
604+
/// a `DerefMut` implementation, and `as_*_mut` methods that return `&mut` references.
605+
/// This `D` does not implement `Clone`.
606+
///
607+
/// The macro's `ref type` form defines `D` to hold an `Arc` pointing to the backend type,
608+
/// permitting `Clone` and `Deref`, but losing exclusive, mutable access.
609+
///
610+
/// For example:
611+
///
612+
/// ```ignore
613+
/// dispatch_types! {ref type DispatchBuffer: BufferInterface =
614+
/// CoreBuffer, WebBuffer, DynBuffer}
615+
/// ```
616+
///
617+
/// This defines `DispatchBuffer` as a type that dereferences to `&dyn BufferInterface`,
618+
/// which has methods like `map_async` and `destroy`. The enum would be:
619+
///
620+
/// ```ignore
621+
/// pub enum DispatchBuffer {
622+
/// #[cfg(wgpu_core)]
623+
/// Core(Arc<CoreBuffer>),
624+
/// #[cfg(webgpu)]
625+
/// WebGPU(WebBuffer),
626+
/// #[cfg(custom)]
627+
/// Custom(DynBuffer),
628+
/// }
629+
/// ```
630+
///
631+
/// This macro also defines `as_*` methods so that the backend implementations can
632+
/// dereference other arguments.
633+
///
634+
/// ## Devirtualization
635+
///
636+
/// The dispatch types generated by this macro are carefully designed to allow the
637+
/// compiler to completely devirtualize calls in most circumstances.
638+
///
639+
/// Note that every variant of the enum generated by this macro is under a `#[cfg]`.
640+
/// Naturally, the `match` expressions in the `Deref` and `DerefMut` implementations have
641+
/// matching `#[cfg]` attributes on each match arm.
642+
///
643+
/// In practice, when `wgpu`'s `"custom"` feature is not enabled, there is usually only
644+
/// one variant in the `enum`, making it effectively a newtype around the sole variant's
645+
/// data: it has no discriminant to branch on, and the `match` expressions are removed
646+
/// entirely by the compiler.
647+
///
648+
/// In this case, when we invoke a method from the interface trait `I` on a dispatch type,
649+
/// the `Deref` and `DerefMut` implementations' `match` statements build a `&dyn I` for
650+
/// the data, on which we immediately invoke a method. The vtable is a constant, allowing
651+
/// the Rust compiler to turn the `dyn` method call into an ordinary method call. This
652+
/// creates opportunities for inlining.
653+
///
654+
/// Similarly, the `as_*` methods are free when there is only one backend.
594655
macro_rules! dispatch_types {
595656
(
596657
ref type $name:ident: $interface:ident = $core_type:ident,$webgpu_type:ident,$custom_type:ident

0 commit comments

Comments
 (0)