Specializer

Trait Specializer 

Source
pub trait Specializer<T: Specializable>:
    Send
    + Sync
    + 'static {
    type Key: SpecializerKey;

    // Required method
    fn specialize(
        &self,
        key: Self::Key,
        descriptor: &mut T::Descriptor,
    ) -> Result<Canonical<Self::Key>, BevyError>;
}
Expand description

Defines a type capable of “specializing” values of a type T.

Specialization is the process of generating variants of a type T from small hashable keys, and specializers themselves can be thought of as pure functions from the key type to T, that memoize their results based on the key.

Because specialization is designed for use with render and compute pipelines, specializers act on descriptors of T rather than produce T itself, but the above comparison is still valid.

Since compiling render and compute pipelines can be so slow, specialization allows a Bevy app to detect when it would compile a duplicate pipeline and reuse what’s already in the cache. While pipelines could all be memoized hashing each whole descriptor, this would be much slower and could still create duplicates. In contrast, memoizing groups of related pipelines based on a small hashable key is much faster. See the docs on SpecializerKey for more info.

§Composing Specializers

This trait can be derived with #[derive(Specializer)] for structs whose fields all implement Specializer. This allows for composing multiple specializers together, and makes encapsulation and separating concerns between specializers much nicer. One could make individual specializers for common operations and place them in entirely separate modules, then compose them together with a single #[derive]

struct A;
struct B;
#[derive(Copy, Clone, PartialEq, Eq, Hash, SpecializerKey)]
struct BKey { contrived_number: u32 };

impl Specializer<RenderPipeline> for A {
    type Key = ();

    fn specialize(
        &self,
        key: (),
        descriptor: &mut RenderPipelineDescriptor
    ) -> Result<(), BevyError>  {
        // mutate the descriptor here
        Ok(key)
    }
}

impl Specializer<RenderPipeline> for B {
    type Key = BKey;

    fn specialize(
        &self,
        key: BKey,
        descriptor: &mut RenderPipelineDescriptor
    ) -> Result<BKey, BevyError> {
        // mutate the descriptor here
        Ok(key)
    }
}

#[derive(Specializer)]
#[specialize(RenderPipeline)]
struct C {
    #[key(default)]
    a: A,
    b: B,
}

/*
The generated implementation:
impl Specializer<RenderPipeline> for C {
    type Key = BKey;
    fn specialize(
        &self,
        key: Self::Key,
        descriptor: &mut RenderPipelineDescriptor
    ) -> Result<Canonical<Self::Key>, BevyError> {
        let _ = self.a.specialize((), descriptor);
        let key = self.b.specialize(key, descriptor);
        Ok(key)
    }
}
*/

The key type for a composed specializer will be a tuple of the keys of each field, and their specialization logic will be applied in field order. Since derive macros can’t have generic parameters, the derive macro requires an additional #[specialize(..targets)] attribute to specify a list of types to target for the implementation. #[specialize(all)] is also allowed, and will generate a fully generic implementation at the cost of slightly worse error messages.

Additionally, each field can optionally take a #[key] attribute to specify a “key override”. This will hide that field’s key from being exposed by the wrapper, and always use the value given by the attribute. Values for this attribute may either be default which will use the key’s Default implementation, or a valid rust expression of the key type.

Required Associated Types§

Required Methods§

Source

fn specialize( &self, key: Self::Key, descriptor: &mut T::Descriptor, ) -> Result<Canonical<Self::Key>, BevyError>

Implementations on Foreign Types§

Source§

impl<T: Specializable> Specializer<T> for ()

Source§

type Key = ()

Source§

fn specialize( &self, _key: Self::Key, _descriptor: &mut T::Descriptor, ) -> Result<(), BevyError>

Source§

impl<T: Specializable, V: Send + Sync + 'static> Specializer<T> for PhantomData<V>

Source§

type Key = ()

Source§

fn specialize( &self, _key: Self::Key, _descriptor: &mut T::Descriptor, ) -> Result<(), BevyError>

Implementors§