livesplit_core/rendering/
entity.rs1use core::{
2 hash::{Hash, Hasher},
3 mem,
4};
5
6use super::{
7 resource::{Handle, LabelHandle},
8 FillShader, Rgba, Transform,
9};
10
11struct FxHasher(u64);
12
13impl Hasher for FxHasher {
14 #[inline]
15 fn write_u8(&mut self, i: u8) {
16 self.write_u64(i as u64);
17 }
18
19 #[inline]
20 fn write_u16(&mut self, i: u16) {
21 self.write_u64(i as u64);
22 }
23
24 #[inline]
25 fn write_u32(&mut self, i: u32) {
26 self.write_u64(i as u64);
27 }
28
29 #[inline]
30 fn write_u64(&mut self, i: u64) {
31 self.0 = (self.0.rotate_left(5) ^ i).wrapping_mul(0x517cc1b727220a95);
32 }
33
34 #[inline]
35 fn write_u128(&mut self, i: u128) {
36 let [a, b]: [u64; 2] = bytemuck::cast(i);
37 self.write_u64(a);
38 self.write_u64(b);
39 }
40
41 #[inline]
42 fn write_usize(&mut self, i: usize) {
43 self.write(bytemuck::bytes_of(&i))
44 }
45
46 #[inline]
47 fn finish(&self) -> u64 {
48 self.0
49 }
50
51 #[inline]
52 fn write(&mut self, bytes: &[u8]) {
53 let (_, chunks, rem) = bytemuck::pod_align_to::<_, [u8; 8]>(bytes);
54 for chunk in chunks {
55 self.write_u64(bytemuck::cast(*chunk));
56 }
57 let (_, chunks, rem) = bytemuck::pod_align_to::<_, [u8; 4]>(rem);
58 for chunk in chunks {
59 self.write_u32(bytemuck::cast(*chunk));
60 }
61 for byte in rem {
62 self.write_u8(*byte);
63 }
64 }
65}
66
67pub enum Entity<P, I, L> {
70 FillPath(Handle<P>, FillShader, Transform),
75 StrokePath(Handle<P>, f32, Rgba, Transform),
79 Image(Handle<I>, Transform),
81 Label(LabelHandle<L>, FillShader, Transform),
83}
84
85pub fn calculate_hash<P, I, L>(
86 background: &Option<FillShader>,
87 entities: &[Entity<P, I, L>],
88) -> u64 {
89 let mut hasher = FxHasher(0x517cc1b727220a95);
90 mem::discriminant(background).hash(&mut hasher);
91 if let Some(background) = background {
92 hash_shader(background, &mut hasher);
93 }
94 entities.hash(&mut hasher);
95 hasher.finish()
96}
97
98#[inline]
99fn hash_float(f: f32, state: &mut impl Hasher) {
100 u32::hash(&bytemuck::cast(f), state);
101}
102
103#[inline]
104fn hash_transform(f: &Transform, state: &mut impl Hasher) {
105 const _: () = assert!(core::mem::size_of::<Transform>() == 16);
106 let [a, b]: [u64; 2] = bytemuck::cast(*f);
107 u64::hash(&a, state);
108 u64::hash(&b, state);
109}
110
111#[inline]
112fn hash_floats(f: &[f32; 4], state: &mut impl Hasher) {
113 let [a, b]: [u64; 2] = bytemuck::cast(*f);
114 u64::hash(&a, state);
115 u64::hash(&b, state);
116}
117
118fn hash_shader(shader: &FillShader, state: &mut impl Hasher) {
119 mem::discriminant(shader).hash(state);
120 match shader {
121 FillShader::SolidColor(c) => hash_floats(c, state),
122 FillShader::VerticalGradient(t, b) => {
123 hash_floats(t, state);
124 hash_floats(b, state);
125 }
126 FillShader::HorizontalGradient(l, r) => {
127 hash_floats(l, state);
128 hash_floats(r, state);
129 }
130 }
131}
132
133impl<P, I, L> Hash for Entity<P, I, L> {
134 fn hash<H: Hasher>(&self, state: &mut H) {
135 mem::discriminant(self).hash(state);
136 match self {
137 Entity::FillPath(path, shader, transform) => {
138 path.hash(state);
139 hash_shader(shader, state);
140 hash_transform(transform, state);
141 }
142 Entity::StrokePath(path, stroke_width, color, transform) => {
143 path.hash(state);
144 hash_float(*stroke_width, state);
145 hash_floats(color, state);
146 hash_transform(transform, state);
147 }
148 Entity::Image(image, transform) => {
149 image.hash(state);
150 hash_transform(transform, state);
151 }
152 Entity::Label(label, shader, transform) => {
153 label.hash(state);
154 hash_shader(shader, state);
155 hash_transform(transform, state);
156 }
157 }
158 }
159}