Skip to main content

bevy_material/
key.rs

1use bevy_platform::{hash::FixedHasher, sync::Arc};
2use core::{
3    any::{Any, TypeId},
4    hash::{BuildHasher, Hash, Hasher},
5};
6
7/// A type-erased mesh pipeline key, which stores the bits of the key as a `u64`.
8#[derive(Clone, Copy)]
9pub struct ErasedMeshPipelineKey {
10    bits: u64,
11    type_id: TypeId,
12}
13
14impl ErasedMeshPipelineKey {
15    #[inline]
16    pub fn new<T: 'static>(key: T) -> Self
17    where
18        u64: From<T>,
19    {
20        Self {
21            bits: key.into(),
22            type_id: TypeId::of::<T>(),
23        }
24    }
25
26    #[inline]
27    pub fn downcast<T: 'static + From<u64>>(&self) -> T {
28        assert_eq!(
29            self.type_id,
30            TypeId::of::<T>(),
31            "ErasedMeshPipelineKey::downcast called with wrong type"
32        );
33        self.bits.into()
34    }
35}
36
37impl PartialEq for ErasedMeshPipelineKey {
38    #[inline]
39    fn eq(&self, other: &Self) -> bool {
40        self.type_id == other.type_id && self.bits == other.bits
41    }
42}
43
44impl Eq for ErasedMeshPipelineKey {}
45
46impl Hash for ErasedMeshPipelineKey {
47    #[inline]
48    fn hash<H: Hasher>(&self, state: &mut H) {
49        self.type_id.hash(state);
50        self.bits.hash(state);
51    }
52}
53
54impl Default for ErasedMeshPipelineKey {
55    fn default() -> Self {
56        Self {
57            bits: 0,
58            type_id: TypeId::of::<()>(),
59        }
60    }
61}
62
63impl core::fmt::Debug for ErasedMeshPipelineKey {
64    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
65        f.debug_struct("ErasedMeshPipelineKey")
66            .field("type_id", &self.type_id)
67            .field("bits", &format_args!("{:#018x}", self.bits))
68            .finish()
69    }
70}
71
72#[derive(Clone, Debug, PartialEq, Eq, Hash)]
73pub struct ErasedMaterialPipelineKey {
74    pub mesh_key: ErasedMeshPipelineKey,
75    pub material_key: ErasedMaterialKey,
76    pub type_id: TypeId,
77}
78
79#[derive(Debug)]
80pub struct ErasedMaterialKey {
81    type_id: TypeId,
82    hash: u64,
83    value: Box<dyn Any + Send + Sync>,
84    vtable: Arc<ErasedMaterialKeyVTable>,
85}
86
87#[derive(Debug)]
88pub struct ErasedMaterialKeyVTable {
89    clone_fn: fn(&dyn Any) -> Box<dyn Any + Send + Sync>,
90    partial_eq_fn: fn(&dyn Any, &dyn Any) -> bool,
91}
92
93impl ErasedMaterialKey {
94    pub fn new<T>(material_key: T) -> Self
95    where
96        T: Clone + Hash + PartialEq + Send + Sync + 'static,
97    {
98        let type_id = TypeId::of::<T>();
99        let hash = FixedHasher::hash_one(&FixedHasher, &material_key);
100
101        fn clone<T: Clone + Send + Sync + 'static>(any: &dyn Any) -> Box<dyn Any + Send + Sync> {
102            Box::new(any.downcast_ref::<T>().unwrap().clone())
103        }
104        fn partial_eq<T: PartialEq + 'static>(a: &dyn Any, b: &dyn Any) -> bool {
105            a.downcast_ref::<T>().unwrap() == b.downcast_ref::<T>().unwrap()
106        }
107
108        Self {
109            type_id,
110            hash,
111            value: Box::new(material_key),
112            vtable: Arc::new(ErasedMaterialKeyVTable {
113                clone_fn: clone::<T>,
114                partial_eq_fn: partial_eq::<T>,
115            }),
116        }
117    }
118
119    pub fn to_key<T: Clone + 'static>(&self) -> T {
120        debug_assert_eq!(self.type_id, TypeId::of::<T>());
121        self.value.downcast_ref::<T>().unwrap().clone()
122    }
123}
124
125impl PartialEq for ErasedMaterialKey {
126    fn eq(&self, other: &Self) -> bool {
127        self.type_id == other.type_id
128            && (self.vtable.partial_eq_fn)(self.value.as_ref(), other.value.as_ref())
129    }
130}
131
132impl Eq for ErasedMaterialKey {}
133
134impl Clone for ErasedMaterialKey {
135    fn clone(&self) -> Self {
136        Self {
137            type_id: self.type_id,
138            hash: self.hash,
139            value: (self.vtable.clone_fn)(self.value.as_ref()),
140            vtable: self.vtable.clone(),
141        }
142    }
143}
144
145impl Hash for ErasedMaterialKey {
146    fn hash<H: Hasher>(&self, state: &mut H) {
147        self.type_id.hash(state);
148        self.hash.hash(state);
149    }
150}
151
152impl Default for ErasedMaterialKey {
153    fn default() -> Self {
154        Self::new(())
155    }
156}