Skip to main content

firewheel_core/diff/
notify.rs

1use bevy_platform::sync::atomic::Ordering;
2
3use crate::{
4    diff::{Diff, Patch, RealtimeClone},
5    event::ParamData,
6};
7
8// Increment a realtime-safe atomic counter.
9//
10// This is gauranteed to never return zero.
11fn increment_counter() -> NotifyID {
12    portable_atomic::cfg_has_atomic_64! {
13        use portable_atomic::AtomicU64;
14
15        static NOTIFY_COUNTER: AtomicU64 = AtomicU64::new(1);
16
17        portable_atomic::cfg_has_atomic_cas! {
18            NotifyID(NOTIFY_COUNTER.fetch_add(1, Ordering::Relaxed))
19        }
20
21        portable_atomic::cfg_no_atomic_cas! {
22            let val = NOTIFY_COUNTER.load(Ordering::Relaxed) + 1;
23            NOTIFY_COUNTER.store(val, Ordering::Relaxed);
24            NotifyID(val)
25        }
26    }
27
28    portable_atomic::cfg_no_atomic_64! {
29        portable_atomic::cfg_has_atomic_32! {
30            use portable_atomic::AtomicU32;
31
32            // While it is technically possible that the high and low bits in the counter
33            // get desynced, in practice this is only ever used for diffing, so it is
34            // exceedingly unlikely that a desync in the counter will have an effect on
35            // the desired diffing behavior.
36            static NOTIFY_COUNTER_0: AtomicU32 = AtomicU32::new(1);
37            static NOTIFY_COUNTER_1: AtomicU32 = AtomicU32::new(0);
38
39            let val_0 = NOTIFY_COUNTER_0.load(Ordering::Relaxed);
40            let val_1 = NOTIFY_COUNTER_1.load(Ordering::Relaxed);
41
42            let val = val_0 as u64 + ((val_1 as u64) << 32) + 1u64;
43
44            NOTIFY_COUNTER_0.store((val & (u32::MAX as u64)) as u32, Ordering::Relaxed);
45            NOTIFY_COUNTER_1.store((val >> 32) as u32, Ordering::Relaxed);
46
47            NotifyID(val)
48        }
49
50        portable_atomic::cfg_no_atomic_32! {
51            use portable_atomic::AtomicU64;
52
53            // Just accept the locking behavior for these esoteric platforms.
54            static NOTIFY_COUNTER: AtomicU64 = AtomicU64::new(1);
55            NotifyID(NOTIFY_COUNTER.fetch_add(1, Ordering::Relaxed))
56        }
57    }
58}
59
60/// An identifier representing the "generation" of a [`Notify`] parameter.
61///
62/// Whenever a `Notify` parameter is mutated, it will be assigned a new [`NotifyID`].
63/// For all practical purposes, the ID can be considered unique among all [`Notify`]
64/// instances.
65///
66/// Valid (non-dangling) [`NotifyID`]s are guaranteed to never be 0, so it can be
67/// used as a sentinel value.
68#[repr(transparent)]
69#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
70#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
71#[cfg_attr(feature = "bevy_reflect", reflect(opaque))]
72pub struct NotifyID(pub u64);
73
74impl NotifyID {
75    pub const DANGLING: Self = Self(0);
76}
77
78/// A lightweight wrapper that guarantees an event
79/// will be generated every time the inner value is accessed mutably,
80/// even if the value doesn't change.
81///
82/// This is useful for types like a play head
83/// where periodically writing the same value
84/// carries useful information.
85///
86/// [`Notify`] implements [`core::ops::Deref`] and [`core::ops::DerefMut`]
87/// for the inner `T`.
88#[derive(Debug, Clone)]
89pub struct Notify<T> {
90    value: T,
91    id: NotifyID,
92}
93
94impl<T> Notify<T> {
95    /// Construct a new [`Notify`].
96    ///
97    /// If two instances of [`Notify`] are constructed separately,
98    /// a call to [`Diff::diff`] will produce an event, even if the
99    /// value is the same.
100    ///
101    /// ```
102    /// # use firewheel_core::diff::Notify;
103    /// // Diffing `a` and `b` will produce an event
104    /// let a = Notify::new(1);
105    /// let b = Notify::new(1);
106    ///
107    /// // whereas `b` and `c` will not.
108    /// let c = b.clone();
109    /// ```
110    pub fn new(value: T) -> Self {
111        Self {
112            value,
113            id: increment_counter(),
114        }
115    }
116
117    pub(crate) fn from_raw(value: T, id: NotifyID) -> Self {
118        Self { value, id }
119    }
120
121    /// An identifier representing the "generation" of this [`Notify`] parameter.
122    ///
123    /// Whenever this parameter is mutated, it will be assigned a new [`NotifyID`].
124    /// For all practical purposes, the ID can be considered unique among all [`Notify`]
125    /// instances.
126    ///
127    /// Valid (non-dangling) [`NotifyID`]s are guaranteed to never be 0, so it can be
128    /// used as a sentinel value.
129    #[inline(always)]
130    pub fn id(&self) -> NotifyID {
131        self.id
132    }
133
134    /// Get mutable access to the inner value without updating the ID.
135    pub fn as_mut_unsync(&mut self) -> &mut T {
136        &mut self.value
137    }
138
139    /// Manually update the internal ID without modifying the internals.
140    pub fn notify(&mut self) {
141        self.id = increment_counter();
142    }
143}
144
145impl<T> AsRef<T> for Notify<T> {
146    fn as_ref(&self) -> &T {
147        &self.value
148    }
149}
150
151impl<T> AsMut<T> for Notify<T> {
152    fn as_mut(&mut self) -> &mut T {
153        self.id = increment_counter();
154
155        &mut self.value
156    }
157}
158
159impl<T: Default> Default for Notify<T> {
160    fn default() -> Self {
161        Self::new(T::default())
162    }
163}
164
165impl<T> core::ops::Deref for Notify<T> {
166    type Target = T;
167
168    fn deref(&self) -> &Self::Target {
169        &self.value
170    }
171}
172
173impl<T> core::ops::DerefMut for Notify<T> {
174    fn deref_mut(&mut self) -> &mut Self::Target {
175        self.id = increment_counter();
176
177        &mut self.value
178    }
179}
180
181impl<T: Copy> Copy for Notify<T> {}
182
183impl<T> PartialEq for Notify<T> {
184    fn eq(&self, other: &Self) -> bool {
185        // under normal usage, it is not possible that the inner value
186        // can change without incrementing the counter
187        self.id == other.id
188    }
189}
190
191impl<T: RealtimeClone + Send + Sync + 'static> Diff for Notify<T> {
192    fn diff<E: super::EventQueue>(
193        &self,
194        baseline: &Self,
195        path: super::PathBuilder,
196        event_queue: &mut E,
197    ) {
198        if self.id != baseline.id {
199            event_queue.push_param(ParamData::any(self.clone()), path);
200        }
201    }
202}
203
204impl<T: RealtimeClone + Send + Sync + 'static> Patch for Notify<T> {
205    type Patch = Self;
206
207    fn patch(data: &ParamData, _: &[u32]) -> Result<Self::Patch, super::PatchError> {
208        data.downcast_ref()
209            .ok_or(super::PatchError::InvalidData)
210            .cloned()
211    }
212
213    fn apply(&mut self, patch: Self::Patch) {
214        *self = patch;
215    }
216}
217
218#[cfg(test)]
219mod test {
220    use crate::diff::PathBuilder;
221
222    use super::*;
223
224    #[test]
225    fn test_identical_write() {
226        #[cfg(not(feature = "std"))]
227        use bevy_platform::prelude::Vec;
228
229        let baseline = Notify::new(0.5f32);
230        let mut value = baseline;
231
232        let mut events = Vec::new();
233        value.diff(&baseline, PathBuilder::default(), &mut events);
234        assert_eq!(events.len(), 0);
235
236        *value = 0.5f32;
237
238        value.diff(&baseline, PathBuilder::default(), &mut events);
239        assert_eq!(events.len(), 1);
240    }
241}
242
243#[cfg(feature = "bevy_reflect")]
244mod reflect {
245    use super::Notify;
246
247    #[cfg(not(feature = "std"))]
248    use bevy_platform::prelude::{Box, ToString};
249
250    impl<T> bevy_reflect::GetTypeRegistration for Notify<T>
251    where
252        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
253        T: Clone
254            + Default
255            + bevy_reflect::FromReflect
256            + bevy_reflect::TypePath
257            + bevy_reflect::MaybeTyped
258            + bevy_reflect::__macro_exports::RegisterForReflection,
259    {
260        fn get_type_registration() -> bevy_reflect::TypeRegistration {
261            let mut registration = bevy_reflect::TypeRegistration::of::<Self>();
262            registration.insert:: <bevy_reflect::ReflectFromPtr>(bevy_reflect::FromType:: <Self> ::from_type());
263            registration.insert::<bevy_reflect::ReflectFromReflect>(
264                bevy_reflect::FromType::<Self>::from_type(),
265            );
266            registration
267                .insert::<bevy_reflect::prelude::ReflectDefault>(
268                    bevy_reflect::FromType::<Self>::from_type(),
269                );
270            registration
271        }
272
273        #[inline(never)]
274        fn register_type_dependencies(registry: &mut bevy_reflect::TypeRegistry) {
275            <T as bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
276            <u64 as bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
277        }
278    }
279
280    impl<T> bevy_reflect::Typed for Notify<T>
281    where
282        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
283        T: Clone
284            + bevy_reflect::FromReflect
285            + bevy_reflect::TypePath
286            + bevy_reflect::MaybeTyped
287            + bevy_reflect::__macro_exports::RegisterForReflection,
288    {
289        #[inline]
290        fn type_info() -> &'static bevy_reflect::TypeInfo {
291            static CELL: bevy_reflect::utility::GenericTypeInfoCell =
292                bevy_reflect::utility::GenericTypeInfoCell::new();
293            CELL.get_or_insert::<Self, _>(|| {
294                bevy_reflect::TypeInfo::Struct(
295                    bevy_reflect::structs::StructInfo::new::<Self>(&[
296                        bevy_reflect::NamedField::new::<T>("value").with_custom_attributes(
297                            bevy_reflect::attributes::CustomAttributes::default(),
298                        ),
299                    ])
300                    .with_custom_attributes(bevy_reflect::attributes::CustomAttributes::default())
301                    .with_generics(bevy_reflect::Generics::from_iter([
302                        bevy_reflect::GenericInfo::Type(bevy_reflect::TypeParamInfo::new::<T>(
303                            // TODO: Use nicer path once bevy_reflect exposes it.
304                            bevy_reflect::__macro_exports::alloc_utils::Cow::Borrowed("T"),
305                        )),
306                    ])),
307                )
308            })
309        }
310    }
311
312    extern crate alloc;
313    impl<T> bevy_reflect::TypePath for Notify<T>
314    where
315        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
316        T: bevy_reflect::TypePath,
317    {
318        fn type_path() -> &'static str {
319            static CELL: bevy_reflect::utility::GenericTypePathCell =
320                bevy_reflect::utility::GenericTypePathCell::new();
321            CELL.get_or_insert::<Self, _>(|| {
322                ::core::ops::Add::<&str>::add(
323                    ::core::ops::Add::<&str>::add(
324                        ToString::to_string(::core::concat!(
325                            ::core::concat!(
326                                ::core::concat!(::core::module_path!(), "::"),
327                                "Notify"
328                            ),
329                            "<"
330                        )),
331                        <T as bevy_reflect::TypePath>::type_path(),
332                    ),
333                    ">",
334                )
335            })
336        }
337        fn short_type_path() -> &'static str {
338            static CELL: bevy_reflect::utility::GenericTypePathCell =
339                bevy_reflect::utility::GenericTypePathCell::new();
340            CELL.get_or_insert::<Self, _>(|| {
341                ::core::ops::Add::<&str>::add(
342                    ::core::ops::Add::<&str>::add(
343                        ToString::to_string(::core::concat!("Notify", "<")),
344                        <T as bevy_reflect::TypePath>::short_type_path(),
345                    ),
346                    ">",
347                )
348            })
349        }
350        fn type_ident() -> Option<&'static str> {
351            Some("Notify")
352        }
353        fn crate_name() -> Option<&'static str> {
354            Some(::core::module_path!().split(':').next().unwrap())
355        }
356        fn module_path() -> Option<&'static str> {
357            Some(::core::module_path!())
358        }
359    }
360
361    impl<T> bevy_reflect::Reflect for Notify<T>
362    where
363        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
364        T: Clone
365            + bevy_reflect::FromReflect
366            + bevy_reflect::TypePath
367            + bevy_reflect::MaybeTyped
368            + bevy_reflect::__macro_exports::RegisterForReflection,
369    {
370        #[inline]
371        fn into_any(self: Box<Self>) -> Box<dyn ::core::any::Any> {
372            self
373        }
374        #[inline]
375        fn as_any(&self) -> &dyn ::core::any::Any {
376            self
377        }
378        #[inline]
379        fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any {
380            self
381        }
382        #[inline]
383        fn into_reflect(self: Box<Self>) -> Box<dyn bevy_reflect::Reflect> {
384            self
385        }
386        #[inline]
387        fn as_reflect(&self) -> &dyn bevy_reflect::Reflect {
388            self
389        }
390        #[inline]
391        fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
392            self
393        }
394        #[inline]
395        fn set(
396            &mut self,
397            value: Box<dyn bevy_reflect::Reflect>,
398        ) -> Result<(), Box<dyn bevy_reflect::Reflect>> {
399            *self = <dyn bevy_reflect::Reflect>::take(value)?;
400            Ok(())
401        }
402    }
403
404    impl<T> bevy_reflect::prelude::Struct for Notify<T>
405    where
406        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
407        T: Clone
408            + bevy_reflect::FromReflect
409            + bevy_reflect::TypePath
410            + bevy_reflect::MaybeTyped
411            + bevy_reflect::__macro_exports::RegisterForReflection,
412    {
413        fn field(&self, name: &str) -> Option<&dyn bevy_reflect::PartialReflect> {
414            match name {
415                "value" => Some(&self.value),
416                _ => None,
417            }
418        }
419
420        fn field_mut(&mut self, name: &str) -> Option<&mut dyn bevy_reflect::PartialReflect> {
421            match name {
422                "value" => Some(self.as_mut()),
423                _ => None,
424            }
425        }
426
427        fn field_at(&self, index: usize) -> Option<&dyn bevy_reflect::PartialReflect> {
428            match index {
429                0usize => Some(&self.value),
430                _ => None,
431            }
432        }
433
434        fn field_at_mut(&mut self, index: usize) -> Option<&mut dyn bevy_reflect::PartialReflect> {
435            match index {
436                0usize => Some(self.as_mut()),
437                _ => None,
438            }
439        }
440
441        fn name_at(&self, index: usize) -> Option<&str> {
442            match index {
443                0usize => Some("value"),
444                _ => None,
445            }
446        }
447
448        fn field_len(&self) -> usize {
449            1usize
450        }
451
452        fn iter_fields<'a>(&'a self) -> bevy_reflect::structs::FieldIter<'a> {
453            bevy_reflect::structs::FieldIter::new(self)
454        }
455
456        fn to_dynamic_struct(&self) -> bevy_reflect::structs::DynamicStruct {
457            let mut dynamic: bevy_reflect::structs::DynamicStruct = Default::default();
458            dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(
459                self,
460            ));
461            dynamic.insert_boxed(
462                "value",
463                bevy_reflect::PartialReflect::to_dynamic(&self.value),
464            );
465            dynamic
466        }
467
468        fn index_of_name(&self, name: &str) -> Option<usize> {
469            (name == "value").then_some(0)
470        }
471    }
472
473    impl<T> bevy_reflect::PartialReflect for Notify<T>
474    where
475        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
476        T: Clone
477            + bevy_reflect::FromReflect
478            + bevy_reflect::TypePath
479            + bevy_reflect::MaybeTyped
480            + bevy_reflect::__macro_exports::RegisterForReflection,
481    {
482        #[inline]
483        fn get_represented_type_info(&self) -> Option<&'static bevy_reflect::TypeInfo> {
484            Some(<Self as bevy_reflect::Typed>::type_info())
485        }
486
487        #[inline]
488        fn try_apply(
489            &mut self,
490            value: &dyn bevy_reflect::PartialReflect,
491        ) -> Result<(), bevy_reflect::ApplyError> {
492            if let bevy_reflect::ReflectRef::Struct(struct_value) =
493                bevy_reflect::PartialReflect::reflect_ref(value)
494            {
495                for (name, value) in struct_value {
496                    if let Some(v) = bevy_reflect::prelude::Struct::field_mut(self, name) {
497                        bevy_reflect::PartialReflect::try_apply(v, value)?;
498                    }
499                }
500            } else {
501                return Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
502                    from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
503                    to_kind: bevy_reflect::ReflectKind::Struct,
504                });
505            }
506            Ok(())
507        }
508
509        #[inline]
510        fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
511            bevy_reflect::ReflectKind::Struct
512        }
513
514        #[inline]
515        fn reflect_ref<'a>(&'a self) -> bevy_reflect::ReflectRef<'a> {
516            bevy_reflect::ReflectRef::Struct(self)
517        }
518
519        #[inline]
520        fn reflect_mut<'a>(&'a mut self) -> bevy_reflect::ReflectMut<'a> {
521            bevy_reflect::ReflectMut::Struct(self)
522        }
523
524        #[inline]
525        fn reflect_owned(self: Box<Self>) -> bevy_reflect::ReflectOwned {
526            bevy_reflect::ReflectOwned::Struct(self)
527        }
528
529        #[inline]
530        fn try_into_reflect(
531            self: Box<Self>,
532        ) -> Result<Box<dyn bevy_reflect::Reflect>, Box<dyn bevy_reflect::PartialReflect>> {
533            Ok(self)
534        }
535
536        #[inline]
537        fn try_as_reflect(&self) -> Option<&dyn bevy_reflect::Reflect> {
538            Some(self)
539        }
540
541        #[inline]
542        fn try_as_reflect_mut(&mut self) -> Option<&mut dyn bevy_reflect::Reflect> {
543            Some(self)
544        }
545
546        #[inline]
547        fn into_partial_reflect(self: Box<Self>) -> Box<dyn bevy_reflect::PartialReflect> {
548            self
549        }
550
551        #[inline]
552        fn as_partial_reflect(&self) -> &dyn bevy_reflect::PartialReflect {
553            self
554        }
555
556        #[inline]
557        fn as_partial_reflect_mut(&mut self) -> &mut dyn bevy_reflect::PartialReflect {
558            self
559        }
560
561        fn reflect_partial_eq(&self, value: &dyn bevy_reflect::PartialReflect) -> Option<bool> {
562            (bevy_reflect::structs::struct_partial_eq)(self, value)
563        }
564
565        #[inline]
566        fn reflect_clone(
567            &self,
568        ) -> Result<Box<dyn bevy_reflect::Reflect>, bevy_reflect::ReflectCloneError> {
569            Ok(Box::new(Clone::clone(self)))
570        }
571    }
572
573    impl<T> bevy_reflect::FromReflect for Notify<T>
574    where
575        Notify<T>: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
576        T: Default
577            + Clone
578            + bevy_reflect::FromReflect
579            + bevy_reflect::TypePath
580            + bevy_reflect::MaybeTyped
581            + bevy_reflect::__macro_exports::RegisterForReflection,
582    {
583        fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect) -> Option<Self> {
584            if let bevy_reflect::ReflectRef::Struct(ref_struct) =
585                bevy_reflect::PartialReflect::reflect_ref(reflect)
586            {
587                let mut this = <Self as ::core::default::Default>::default();
588                if let Some(field) = (|| {
589                    <T as bevy_reflect::FromReflect>::from_reflect(
590                        bevy_reflect::prelude::Struct::field(ref_struct, "value")?,
591                    )
592                })() {
593                    this.value = field;
594                }
595                Some(this)
596            } else {
597                None
598            }
599        }
600    }
601}