all_is_cubes_gpu/common/
info.rs

1use core::time::Duration;
2use core::{fmt, ops};
3
4#[cfg(feature = "rerun")]
5use all_is_cubes::rerun_glue as rg;
6use all_is_cubes::util::{Fmt, Refmt, ShowStatus, StatusText};
7use all_is_cubes_mesh::dynamic::CsmUpdateInfo;
8use all_is_cubes_render::{Flaws, camera::Layers};
9
10/// Performance info about drawing an entire scene.
11///
12/// This is intended to be displayed to the user as real-time diagnostic information.
13#[derive(Clone, Debug, Default)]
14#[non_exhaustive]
15pub struct RenderInfo {
16    pub(crate) waiting_for_gpu: Duration,
17    pub(crate) update: UpdateInfo,
18    pub(crate) draw: DrawInfo,
19    /// [Flaws] in the rendering.
20    pub flaws: Flaws,
21}
22
23/// Info about the “update” operation, where fresh scene information is gathered,
24/// processed (e.g. mesh generation), and copied to GPU memory.
25///
26/// All of the timings here are CPU time, so they do not account for GPU activity
27/// unless blocking occurs.
28#[derive(Clone, Debug, Default)]
29#[non_exhaustive]
30pub struct UpdateInfo {
31    /// Known flaws detected at update time.
32    ///
33    /// This should include all flaws reported within `spaces` too.
34    pub(crate) flaws: Flaws,
35    /// Start-to-finish time for the update operation.
36    pub(crate) total_time: Duration,
37    /// Time taken on miscellaneous preparatory actions such as calculating the current
38    /// camera state.
39    pub(crate) prep_time: Duration,
40    /// Time taken on gathering cursor and debug line vertices.
41    pub(crate) lines_time: Duration,
42    /// Time taken on submitting accumulated information to the GPU.
43    /// `None` if the graphics API does not expose this as a step.
44    pub(crate) submit_time: Option<Duration>,
45    /// Per-space details, including time taken.
46    pub(crate) spaces: Layers<SpaceUpdateInfo>,
47}
48
49impl UpdateInfo {
50    #[doc(hidden)] // unstable
51    pub fn flaws(&self) -> Flaws {
52        self.flaws
53    }
54}
55
56#[derive(Clone, Debug, Default)]
57#[non_exhaustive]
58pub(crate) struct DrawInfo {
59    /// Total time taken by drawing each layer.
60    pub(crate) times: Layers<Duration>,
61    pub(crate) space_info: Layers<SpaceDrawInfo>,
62    /// Time taken on submitting accumulated information to the GPU.
63    /// `None` if the graphics API does not expose this as a step.
64    pub(crate) submit_time: Option<Duration>,
65}
66impl DrawInfo {
67    pub(crate) fn flaws(&self) -> Flaws {
68        self.space_info.world.flaws | self.space_info.ui.flaws
69    }
70}
71
72impl Fmt<StatusText> for RenderInfo {
73    fn fmt(&self, fmt: &mut fmt::Formatter<'_>, fopt: &StatusText) -> fmt::Result {
74        let &Self {
75            waiting_for_gpu,
76            update:
77                UpdateInfo {
78                    flaws: _, // flaws are aggregated up
79                    total_time: update_time,
80                    prep_time: update_prep_time,
81                    lines_time,
82                    submit_time: update_submit_time,
83                    spaces: ref update_spaces,
84                },
85            draw:
86                DrawInfo {
87                    times: draw_time,
88                    space_info: ref draw_spaces,
89                    submit_time,
90                },
91            flaws,
92        } = self;
93
94        let total_time = waiting_for_gpu
95            .saturating_add(update_time)
96            .saturating_add(draw_time.world)
97            .saturating_add(draw_time.ui);
98
99        // Overall summary line
100        write!(
101            fmt,
102            // TODO: adjust this format to account for more pieces
103            "Frame time: {} (GPU wait {}, update {}, draw world {}, ui {}",
104            total_time.refmt(fopt),
105            waiting_for_gpu.refmt(fopt),
106            update_time.refmt(fopt),
107            draw_time.world.refmt(fopt),
108            draw_time.ui.refmt(fopt),
109        )?;
110        if let Some(t) = submit_time {
111            write!(fmt, ", submit {}", t.refmt(fopt))?;
112        }
113        writeln!(fmt, ")")?;
114
115        // UpdateInfo details
116        write!(
117            fmt,
118            "Update breakdown: prep {}, world mesh {}, ui mesh {}, lines {}",
119            update_prep_time.refmt(fopt),
120            update_spaces.world.total_time.refmt(fopt),
121            update_spaces.ui.total_time.refmt(fopt),
122            lines_time.refmt(fopt),
123        )?;
124        if let Some(t) = update_submit_time {
125            write!(fmt, ", submit {}", t.refmt(fopt))?;
126        }
127
128        // Spaces
129        if fopt.show.contains(ShowStatus::WORLD) {
130            write!(
131                fmt,
132                "\n\nWORLD:\n{}\n{}",
133                update_spaces.world.refmt(fopt),
134                draw_spaces.world.refmt(fopt)
135            )?;
136        }
137        if fopt.show.contains(ShowStatus::UI) {
138            write!(
139                fmt,
140                "\n\nUI:\n{}\n{}",
141                update_spaces.ui.refmt(fopt),
142                draw_spaces.ui.refmt(fopt)
143            )?;
144        }
145
146        write!(fmt, "\nRender flaws: {flaws}")?;
147        Ok(())
148    }
149}
150
151/// Performance info about copying [`Space`] data into the renderer per-frame.
152///
153/// This is intended to be displayed to the user as real-time diagnostic information,
154/// part of [`RenderInfo`].
155///
156/// [`Space`]: all_is_cubes::space::Space
157#[derive(Clone, Debug, Default, Eq, PartialEq)]
158#[non_exhaustive]
159pub struct SpaceUpdateInfo {
160    pub(crate) total_time: Duration,
161
162    /// Status of the block and chunk meshes.
163    pub(crate) chunk_info: CsmUpdateInfo,
164    /// Status of the texture atlas.
165    pub(crate) texture_info: BlockTextureInfo,
166
167    /// Time taken to upload light data.
168    pub(crate) light_update_time: Duration,
169    /// Number of light cubes updated
170    pub(crate) light_update_count: usize,
171}
172
173impl SpaceUpdateInfo {
174    pub(crate) fn flaws(&self) -> Flaws {
175        self.chunk_info.flaws
176    }
177
178    #[cfg(feature = "rerun")]
179    pub(crate) fn write_to_rerun(&self, destination: &rg::Destination) {
180        use alloc::string::ToString as _;
181
182        destination.log(
183            &"info".into(),
184            &rg::archetypes::TextDocument::new(self.refmt(&StatusText::ALL).to_string())
185                .with_media_type(rg::components::MediaType::TEXT),
186        );
187
188        // TODO: include every useful number we can; make names more consistent
189        let &Self {
190            total_time,
191            chunk_info:
192                CsmUpdateInfo {
193                    total_time: chunk_total_time,
194                    ..
195                },
196            texture_info: _,
197            light_update_time,
198            light_update_count: _,
199        } = self;
200
201        destination.log(&"update_total_time".into(), &rg::milliseconds(total_time));
202        destination.log(
203            &"chunk_total_time".into(),
204            &rg::milliseconds(chunk_total_time),
205        );
206        destination.log(
207            &"light_update_time".into(),
208            &rg::milliseconds(light_update_time),
209        );
210    }
211}
212
213impl Fmt<StatusText> for SpaceUpdateInfo {
214    fn fmt(&self, fmt: &mut fmt::Formatter<'_>, fopt: &StatusText) -> fmt::Result {
215        let &SpaceUpdateInfo {
216            total_time: _, // we print this as summary info from the parent only
217            ref chunk_info,
218            ref texture_info,
219            light_update_time,
220            light_update_count,
221        } = self;
222
223        let light_update_time = light_update_time.refmt(fopt);
224
225        writeln!(fmt, "{}", chunk_info.refmt(fopt))?;
226        writeln!(
227            fmt,
228            "Light: {light_update_count:3} cubes in {light_update_time}"
229        )?;
230        write!(fmt, "{:#?}", texture_info.refmt(fopt))?;
231        Ok(())
232    }
233}
234
235/// Performance info about actually drawing a [`Space`] (excluding data updates).
236///
237/// Depending on the asynchrony of the renderer implementation, this may not be a
238/// complete accounting of time spent; in particular it is not guaranteed to include
239/// time spent by the GPU or waiting for the GPU.
240///
241/// This is intended to be displayed to the user as real-time diagnostic information,
242/// part of [`RenderInfo`].
243///
244/// [`Space`]: all_is_cubes::space::Space
245#[derive(Clone, Debug, Default, Eq, PartialEq)]
246#[non_exhaustive]
247pub struct SpaceDrawInfo {
248    /// Time taken to set up for drawing the space.
249    pub(crate) draw_init_time: Duration,
250    /// Time taken to draw chunks' opaque geometry
251    /// (and determine if they are visible to be drawn).
252    pub(crate) draw_opaque_chunks_time: Duration,
253    /// Time taken to draw instanced blocks' opaque geometry.
254    pub(crate) draw_opaque_blocks_time: Duration,
255    /// Time taken to draw chunks' transparent geometry
256    /// (and determine if they are visible to be drawn).
257    pub(crate) draw_transparent_time: Duration,
258    /// Time taken to finish rendering (currently this means dropping the render pass and writing
259    /// the instance buffer).
260    pub(crate) finalize_time: Duration,
261
262    /// Number of chunk meshes (not instances) drawn.
263    pub(crate) chunk_meshes_drawn: usize,
264    /// Number of chunks that contributed instances.
265    pub(crate) chunks_with_instances_drawn: usize,
266    /// Number of instanced block meshes drawn.
267    pub(crate) blocks_drawn: usize,
268    /// How many triangles were used to draw this frame.
269    pub(crate) triangles_drawn: usize,
270
271    pub(crate) flaws: Flaws,
272}
273
274impl Fmt<StatusText> for SpaceDrawInfo {
275    fn fmt(&self, fmt: &mut fmt::Formatter<'_>, fopt: &StatusText) -> fmt::Result {
276        let &SpaceDrawInfo {
277            draw_init_time,
278            draw_opaque_chunks_time,
279            draw_opaque_blocks_time,
280            draw_transparent_time,
281            finalize_time,
282            chunk_meshes_drawn,
283            chunks_with_instances_drawn,
284            blocks_drawn,
285            triangles_drawn,
286            flaws: _, // TODO: include or exclude?
287        } = self;
288
289        let draw_init_time = draw_init_time.refmt(fopt);
290        let draw_opaque_chunks_time = draw_opaque_chunks_time.refmt(fopt);
291        let draw_opaque_blocks_time = draw_opaque_blocks_time.refmt(fopt);
292        let draw_transparent_time = draw_transparent_time.refmt(fopt);
293        let finalize_time = finalize_time.refmt(fopt);
294
295        writeln!(
296            fmt,
297            "Draw init: {draw_init_time}  \
298            opaque chunks: {draw_opaque_chunks_time}  \
299            opaque blocks: {draw_opaque_blocks_time}  \
300            transparent chunks: {draw_transparent_time}  \
301            finalize: {finalize_time}",
302        )?;
303        writeln!(
304            fmt,
305            "Chunk meshes drawn: {chunk_meshes_drawn:3}  \
306            Block insts drawn: {blocks_drawn:3} in {chunks_with_instances_drawn:3} chunks  \
307            Triangles drawn: {triangles_drawn:7}",
308        )?;
309        Ok(())
310    }
311}
312
313/// Performance info about [`Block`] texture management.
314///
315/// [`Block`]: all_is_cubes::block::Block
316#[derive(Clone, Debug, Default, Eq, PartialEq)]
317pub struct BlockTextureInfo {
318    pub(crate) flushed: usize,
319    pub(crate) flush_time: Duration,
320    pub(crate) in_use_tiles: usize,
321    pub(crate) texels: crate::octree_alloc::Info,
322}
323
324impl ops::Add for BlockTextureInfo {
325    type Output = Self;
326    fn add(self, rhs: Self) -> Self::Output {
327        BlockTextureInfo {
328            flushed: self.flushed + rhs.flushed,
329            flush_time: self.flush_time + rhs.flush_time,
330            in_use_tiles: self.in_use_tiles + rhs.in_use_tiles,
331            texels: self.texels + rhs.texels,
332        }
333    }
334}
335
336impl Fmt<StatusText> for BlockTextureInfo {
337    fn fmt(&self, fmt: &mut fmt::Formatter<'_>, fopt: &StatusText) -> fmt::Result {
338        write!(
339            fmt,
340            "Textures: {}  Atlas: {}  Flushed: {:2} in {}",
341            self.in_use_tiles,
342            self.texels.refmt(fopt),
343            self.flushed,
344            self.flush_time.refmt(fopt)
345        )
346    }
347}