返回 CodeWhale
render.rs
1 //! Bounded, dependency-free measurements of actual terminal-buffer rendering.
2 use codewhale_ratatui::{
3 Caps, Message, MotionMode, NativeComposer, OceanColumn, Paint, PostureBar, SessionList,
4 SessionRow, TerminalShell, Theme, Workbar, WorkbarPanel, WorkbarRow, color::ColorDepth,
5 detect::Appearance, gallery,
6 };
7 use ratatui::{buffer::Buffer, layout::Rect};
8 use std::{
9 hint::black_box,
10 time::{Duration, Instant},
11 };
12
13 fn measure(name: &str, samples: usize, batch: usize, mut render: impl FnMut()) {
14 for _ in 0..batch {
15 render();
16 }
17 let mut timings = Vec::with_capacity(samples);
18 for _ in 0..samples {
19 let start = Instant::now();
20 for _ in 0..batch {
21 render();
22 }
23 timings.push(start.elapsed().as_secs_f64() * 1_000_000.0 / batch as f64);
24 }
25 timings.sort_by(f64::total_cmp);
26 println!(
27 "{name:32} {:12.3} {:12.3}",
28 timings[samples / 2],
29 timings[(samples * 95).div_ceil(100).saturating_sub(1)]
30 );
31 }
32
33 fn prepared(
34 name: &str,
35 component: &dyn Paint,
36 area: Rect,
37 theme: &Theme,
38 samples: usize,
39 batch: usize,
40 ) {
41 let mut buf = Buffer::empty(area);
42 measure(name, samples, batch, || {
43 buf.reset();
44 component.paint(area, &mut buf, theme);
45 black_box(&buf);
46 });
47 }
48
49 fn main() {
50 let quick = std::env::args().any(|arg| arg == "--quick");
51 let (samples, batch) = if quick { (5, 2) } else { (31, 20) };
52 let theme = Theme::new(Caps {
53 depth: ColorDepth::TrueColor,
54 ascii: false,
55 appearance: Appearance::Dark,
56 })
57 .tui();
58 println!("Codewhale rendering · native Underwater · {samples} samples × {batch} paints");
59 println!("{:32} {:>12} {:>12}", "scenario", "median µs", "p95 µs");
60 let composer = NativeComposer::new("Review the Unicode text: 海洋 e\u{301} 👩‍💻").focused(true);
61 prepared(
62 "composer / 112×5",
63 &composer,
64 Rect::new(0, 0, 112, 5),
65 &theme,
66 samples,
67 batch,
68 );
69 let dock = Workbar::new(
70 WorkbarPanel::Tasks,
71 (0..10)
72 .map(|i| WorkbarRow::new(i.to_string(), "Review the native component"))
73 .collect(),
74 );
75 prepared(
76 "workbar / 112×5",
77 &dock,
78 Rect::new(0, 0, 112, 5),
79 &theme,
80 samples,
81 batch,
82 );
83 for count in [10, 100, 10_000] {
84 let sessions = SessionList::new(
85 (0..count)
86 .map(|i| SessionRow::new(format!("{i:08x}"), "A saved Codewhale session"))
87 .collect(),
88 );
89 prepared(
90 &format!("sessions {count} / 112×38"),
91 &sessions,
92 Rect::new(0, 0, 112, 38),
93 &theme,
94 samples,
95 batch,
96 );
97 }
98 let entries = gallery::entries();
99 let work = entries
100 .iter()
101 .find(|entry| entry.name == "showcase-work")
102 .expect("native showcase-work fixture");
103 let area = Rect::new(0, 0, 112, 38);
104 let mut buf = Buffer::empty(area);
105 let native_theme = gallery::theme_for(work, &theme);
106 // Build foregrounds before the ocean pass: the full gallery fixture
107 // already finishes its grounds, which would skip most work on reapply.
108 let shell = TerminalShell::new(5).workbar_rows(5);
109 let regions = shell.areas(area);
110 shell.paint(area, &mut buf, &theme);
111 let body = (0..28)
112 .map(|i| format!("Review {i}: Keep the native composer, dock and 海洋 text in view."))
113 .collect::<Vec<_>>()
114 .join("\n");
115 Message::native(&body).paint(regions.conversation, &mut buf, &theme);
116 composer.paint(regions.composer, &mut buf, &theme);
117 PostureBar::new("ask").paint(regions.posture, &mut buf, &theme);
118 dock.paint(regions.workbar, &mut buf, &theme);
119 let foreground = buf.clone();
120 let ocean = OceanColumn::new(Duration::from_millis(22_500), MotionMode::Full);
121 measure("ocean + fixture clone / 112×38", samples, batch, || {
122 let mut buf = foreground.clone();
123 ocean.apply(area, &mut buf, &theme);
124 black_box(&buf);
125 });
126 measure("native work / construct + paint", samples, batch, || {
127 buf.reset();
128 (work.draw)(area, &mut buf, &native_theme);
129 black_box(&buf);
130 });
131 }
132
132 lines RUST