Skip to content

Commit 6e06ec9

Browse files
authored
[wgpu] Document dispatch_type! macro. (gfx-rs#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.
1 parent 3443224 commit 6e06ec9

1 file changed

Lines changed: 73 additions & 9 deletions

File tree

wgpu/src/dispatch.rs

Lines changed: 73 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
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.
11+
//!
12+
//! [`dispatch_types`]: macro.dispatch_types.html
1013
1114
#![allow(drop_bounds)] // This exists to remind implementors to impl drop.
1215
#![allow(clippy::too_many_arguments)] // It's fine.
@@ -581,16 +584,77 @@ pub trait BufferMappedRangeInterface: CommonTraits {
581584
fn as_uint8array(&self) -> &js_sys::Uint8Array;
582585
}
583586

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

0 commit comments

Comments
 (0)