Macro dispatch_types

Source
macro_rules! dispatch_types {
    (
        ref type $name:ident: $interface:ident = $core_type:ident,$webgpu_type:ident,$custom_type:ident
    ) => { ... };
    (
        mut type $name:ident: $interface:ident = $core_type:ident,$webgpu_type:ident,$custom_type:ident
    ) => { ... };
}
Expand description

Generates a dispatch type for some wgpu API type.

Invocations of this macro take one of the following forms:

dispatch_types! {mut type D: I = Core, Web, Dyn }
dispatch_types! {ref type D: I = Core, Web, Dyn }

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:

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:

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.