example_render_log.rs

 1//! `RenderLog` — a diagnostic panel that records which components re-render
 2//! and when, letting you observe GPUI's caching behaviour in real time.
 3
 4use std::time::Instant;
 5
 6use gpui::{App, Context, Entity, IntoViewElement, Window, div, hsla, prelude::*, px};
 7
 8// ---------------------------------------------------------------------------
 9// RenderLog entity
10// ---------------------------------------------------------------------------
11
12pub struct RenderLog {
13    entries: Vec<RenderLogEntry>,
14    start_time: Instant,
15}
16
17struct RenderLogEntry {
18    component: &'static str,
19    timestamp: Instant,
20}
21
22impl RenderLog {
23    pub fn new(_cx: &mut Context<Self>) -> Self {
24        Self {
25            entries: Vec::new(),
26            start_time: Instant::now(),
27        }
28    }
29
30    /// Record that `component` rendered. Does **not** call `cx.notify()` — the
31    /// panel updates passively the next time its parent re-renders, which avoids
32    /// an infinite invalidation loop.
33    pub fn log(&mut self, component: &'static str) {
34        self.entries.push(RenderLogEntry {
35            component,
36            timestamp: Instant::now(),
37        });
38        if self.entries.len() > 50 {
39            self.entries.drain(0..self.entries.len() - 50);
40        }
41    }
42}
43
44// ---------------------------------------------------------------------------
45// RenderLogPanel — stateless ComponentView that displays the log
46// ---------------------------------------------------------------------------
47
48#[derive(Hash, IntoViewElement)]
49pub struct RenderLogPanel {
50    log: Entity<RenderLog>,
51}
52
53impl RenderLogPanel {
54    pub fn new(log: Entity<RenderLog>) -> Self {
55        Self { log }
56    }
57}
58
59impl gpui::ComponentView for RenderLogPanel {
60    fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
61        let log = self.log.read(cx);
62        let start = log.start_time;
63
64        div()
65            .flex()
66            .flex_col()
67            .gap(px(1.))
68            .p(px(8.))
69            .bg(hsla(0., 0., 0.12, 1.))
70            .rounded(px(4.))
71            .max_h(px(180.))
72            .overflow_hidden()
73            .child(
74                div()
75                    .text_xs()
76                    .text_color(hsla(0., 0., 0.55, 1.))
77                    .mb(px(4.))
78                    .child("Render log (most recent 20)"),
79            )
80            .children(
81                log.entries
82                    .iter()
83                    .rev()
84                    .take(20)
85                    .collect::<Vec<_>>()
86                    .into_iter()
87                    .rev()
88                    .map(|entry| {
89                        let elapsed = entry.timestamp.duration_since(start);
90                        let secs = elapsed.as_secs_f64();
91                        div()
92                            .text_xs()
93                            .text_color(hsla(120. / 360., 0.7, 0.65, 1.))
94                            .child(format!("{:<20} +{:.1}s", entry.component, secs))
95                    }),
96            )
97    }
98}