bevy_diagnostic/
frame_count_diagnostics_plugin.rs

1use bevy_app::prelude::*;
2use bevy_ecs::prelude::*;
3
4#[cfg(feature = "serialize")]
5use serde::{
6    de::{Error, Visitor},
7    Deserialize, Deserializer, Serialize, Serializer,
8};
9
10/// Maintains a count of frames rendered since the start of the application.
11///
12/// [`FrameCount`] is incremented during [`Last`], providing predictable
13/// behavior: it will be 0 during the first update, 1 during the next, and so forth.
14///
15/// # Overflows
16///
17/// [`FrameCount`] will wrap to 0 after exceeding [`u32::MAX`]. Within reasonable
18/// assumptions, one may exploit wrapping arithmetic to determine the number of frames
19/// that have elapsed between two observations – see [`u32::wrapping_sub()`].
20#[derive(Debug, Default, Resource, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
21pub struct FrameCount(pub u32);
22
23/// Adds frame counting functionality to Apps.
24#[derive(Default)]
25pub struct FrameCountPlugin;
26
27impl Plugin for FrameCountPlugin {
28    fn build(&self, app: &mut App) {
29        app.init_resource::<FrameCount>();
30        app.add_systems(Last, update_frame_count);
31    }
32}
33
34/// A system used to increment [`FrameCount`] with wrapping addition.
35///
36/// See [`FrameCount`] for more details.
37pub fn update_frame_count(mut frame_count: ResMut<FrameCount>) {
38    frame_count.0 = frame_count.0.wrapping_add(1);
39}
40
41#[cfg(feature = "serialize")]
42// Manually implementing serialize/deserialize allows us to use a more compact representation as simple integers
43impl Serialize for FrameCount {
44    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
45        serializer.serialize_u32(self.0)
46    }
47}
48
49#[cfg(feature = "serialize")]
50impl<'de> Deserialize<'de> for FrameCount {
51    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
52        deserializer.deserialize_u32(FrameVisitor)
53    }
54}
55
56#[cfg(feature = "serialize")]
57struct FrameVisitor;
58
59#[cfg(feature = "serialize")]
60impl<'de> Visitor<'de> for FrameVisitor {
61    type Value = FrameCount;
62
63    fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
64        formatter.write_str(core::any::type_name::<FrameCount>())
65    }
66
67    fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
68    where
69        E: Error,
70    {
71        Ok(FrameCount(v))
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn frame_counter_update() {
81        let mut app = App::new();
82        app.add_plugins(FrameCountPlugin);
83        app.update();
84
85        let frame_count = app.world().resource::<FrameCount>();
86        assert_eq!(1, frame_count.0);
87    }
88}
89
90#[cfg(all(test, feature = "serialize"))]
91mod serde_tests {
92    use super::*;
93
94    use serde_test::{assert_tokens, Token};
95
96    #[test]
97    fn test_serde_frame_count() {
98        let frame_count = FrameCount(100);
99        assert_tokens(&frame_count, &[Token::U32(100)]);
100    }
101}