waterui_preview_protocol/bench.rs
1//! Benchmark report wire format shared by `waterui-testing` and the `water` CLI.
2//!
3//! `#[waterui::bench]` tests run under `cargo nextest`; in full-run mode each
4//! bench serializes one [`BenchReport`] JSON file into the directory named by
5//! [`BENCH_REPORT_DIR_ENV`]. `water bench` sets the environment, runs nextest,
6//! and deserializes the same types, so field renames break the build instead of
7//! silently desynchronizing the two halves.
8
9use serde::{Deserialize, Serialize};
10
11/// Environment variable selecting the recorded frame count per measurement.
12///
13/// Setting any of the three run-shape variables switches benches from smoke
14/// mode into a full measurement run.
15pub const BENCH_SAMPLES_ENV: &str = "WATERUI_BENCH_SAMPLES";
16/// Environment variable selecting the unrecorded warmup frame count.
17pub const BENCH_WARMUPS_ENV: &str = "WATERUI_BENCH_WARMUPS";
18/// Environment variable selecting the independent measurement repetitions.
19pub const BENCH_REPETITIONS_ENV: &str = "WATERUI_BENCH_REPETITIONS";
20/// Environment variable naming the directory that receives one
21/// [`BenchReport`] JSON file per executed bench in full-run mode.
22pub const BENCH_REPORT_DIR_ENV: &str = "WATERUI_BENCH_REPORT_DIR";
23
24/// Environment variable capping every bench's p95 frame-time budget, in microseconds.
25pub const BENCH_MAX_P95_US_ENV: &str = "WATERUI_BENCH_MAX_P95_US";
26/// Environment variable capping every bench's mean frame-time budget, in microseconds.
27pub const BENCH_MAX_MEAN_US_ENV: &str = "WATERUI_BENCH_MAX_MEAN_US";
28/// Environment variable capping every bench's rebuild-ratio budget.
29pub const BENCH_MAX_REBUILD_RATIO_ENV: &str = "WATERUI_BENCH_MAX_REBUILD_RATIO";
30/// Environment variable capping every bench's compositor scene-layer budget.
31pub const BENCH_MAX_SCENE_LAYERS_ENV: &str = "WATERUI_BENCH_MAX_SCENE_LAYERS";
32/// Environment variable capping every bench's embedded GPU-surface-layer budget.
33pub const BENCH_MAX_GPU_SURFACE_LAYERS_ENV: &str = "WATERUI_BENCH_MAX_GPU_SURFACE_LAYERS";
34/// Environment variable capping every bench's Vello clip-layer budget.
35pub const BENCH_MAX_CLIP_LAYERS_ENV: &str = "WATERUI_BENCH_MAX_CLIP_LAYERS";
36
37/// File name of one bench's report inside the report directory.
38#[must_use]
39pub fn bench_report_file_name(crate_name: &str, bench_name: &str) -> String {
40 format!("{crate_name}__{bench_name}.json")
41}
42
43/// One executed bench: its identity, run shape, budgets, and measurements.
44#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct BenchReport {
46 /// Package that declared the bench.
47 pub crate_name: String,
48 /// Bench function name (without the `waterui_bench_` prefix).
49 pub bench_name: String,
50 /// Run shape the measurements were recorded with.
51 pub config: BenchRunConfig,
52 /// Effective budgets the run was judged against (attribute budgets merged
53 /// with environment caps), echoed so reporters can show headroom.
54 pub budgets: BenchBudgets,
55 /// Recorded measurements in insertion order.
56 pub measurements: Vec<PerfMeasurement>,
57}
58
59/// Frame-run shape of one bench execution.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
61pub struct BenchRunConfig {
62 /// Unrecorded frames run before sampling.
63 pub warmups: u32,
64 /// Recorded frames per measurement.
65 pub samples: u32,
66 /// Independent measurement repetitions.
67 pub repetitions: u32,
68}
69
70/// Optional per-metric ceilings applied to every measurement a bench records.
71#[derive(Debug, Clone, Copy, Default, PartialEq, Serialize, Deserialize)]
72pub struct BenchBudgets {
73 /// Maximum allowed 95th-percentile frame time, in microseconds.
74 pub max_p95_us: Option<u64>,
75 /// Maximum allowed mean frame time, in microseconds.
76 pub max_mean_us: Option<u64>,
77 /// Maximum allowed share of sampled frames that rebuilt (0.0..=1.0).
78 pub max_rebuild_ratio: Option<f64>,
79 /// Maximum allowed compositor scene layers submitted by one frame.
80 pub max_scene_layers: Option<u64>,
81 /// Maximum allowed embedded GPU surface layers submitted by one frame.
82 pub max_gpu_surface_layers: Option<u64>,
83 /// Maximum allowed Vello clip layers pushed by one frame.
84 pub max_clip_layers: Option<u64>,
85}
86
87/// Aggregated statistics of one perf measurement.
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct PerfMeasurement {
90 /// Scenario name.
91 pub name: String,
92 /// Recorded sample count.
93 pub samples: u64,
94 /// Mean frame time in microseconds.
95 pub mean_us: u64,
96 /// Median frame time in microseconds.
97 pub median_us: u64,
98 /// 95th-percentile frame time in microseconds.
99 pub p95_us: u64,
100 /// Minimum frame time in microseconds.
101 pub min_us: u64,
102 /// Maximum frame time in microseconds.
103 pub max_us: u64,
104 /// Frames that performed a structural rebuild.
105 pub rebuilt_frames: u64,
106 /// Frames that rendered.
107 pub rendered_frames: u64,
108 /// Frames that did no work.
109 pub idle_frames: u64,
110 /// Mean frame time of rendered frames in microseconds.
111 pub rendered_mean_us: u64,
112 /// 95th-percentile frame time of rendered frames in microseconds.
113 pub rendered_p95_us: u64,
114 /// Maximum frame time of rendered frames in microseconds.
115 pub rendered_max_us: u64,
116 /// Frames exceeding the 120 fps budget.
117 pub missed_120fps_frames: u64,
118 /// Frames exceeding the 60 fps budget.
119 pub missed_60fps_frames: u64,
120 /// Measurement-cache hits across the run.
121 pub measurement_cache_hits: u64,
122 /// Measurement-cache misses across the run.
123 pub measurement_cache_misses: u64,
124 /// Compositor scene layers submitted.
125 pub scene_layers: u64,
126 /// Vello scene layers submitted.
127 pub vello_scene_layers: u64,
128 /// GPU surface layers submitted.
129 pub gpu_surface_layers: u64,
130 /// Clip layers pushed.
131 pub clip_layers: u64,
132 /// Maximum clip depth reached.
133 pub max_clip_depth: u64,
134 /// Applied filter count.
135 pub applied_filter_count: u64,
136 /// Applied filter capture time in microseconds.
137 pub applied_filter_capture_us: u64,
138 /// Applied filter effect time in microseconds.
139 pub applied_filter_effect_us: u64,
140 /// Frame phase aggregates.
141 pub phases: PerfPhases,
142 /// Per-frame samples.
143 pub frames: Vec<PerfFrame>,
144}
145
146/// Frame phase aggregates of a perf measurement.
147#[derive(Debug, Clone, Default, Serialize, Deserialize)]
148pub struct PerfPhases {
149 /// Mean rebuild phase time in microseconds.
150 pub rebuild_mean_us: u64,
151 /// 95th-percentile rebuild phase time in microseconds.
152 pub rebuild_p95_us: u64,
153 /// Mean content-build phase time in microseconds.
154 pub build_content_mean_us: u64,
155 /// 95th-percentile content-build phase time in microseconds.
156 pub build_content_p95_us: u64,
157 /// Mean scene-dispatch phase time in microseconds.
158 pub scene_dispatch_mean_us: u64,
159 /// 95th-percentile scene-dispatch phase time in microseconds.
160 pub scene_dispatch_p95_us: u64,
161 /// Mean scene-finish phase time in microseconds.
162 pub scene_finish_mean_us: u64,
163 /// 95th-percentile scene-finish phase time in microseconds.
164 pub scene_finish_p95_us: u64,
165 /// Mean render phase time in microseconds.
166 pub render_mean_us: u64,
167 /// 95th-percentile render phase time in microseconds.
168 pub render_p95_us: u64,
169 /// Mean animation phase time in microseconds.
170 pub animation_mean_us: u64,
171 /// Mean input phase time in microseconds.
172 pub input_mean_us: u64,
173}
174
175/// One recorded perf frame.
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct PerfFrame {
178 /// Frame index within the measurement.
179 pub index: u64,
180 /// Total frame time in microseconds.
181 pub total_us: u64,
182 /// Rebuild phase time in microseconds.
183 pub rebuild_us: u64,
184 /// Content-build phase time in microseconds.
185 pub build_content_us: u64,
186 /// Scene-dispatch phase time in microseconds.
187 pub scene_dispatch_us: u64,
188 /// Scene-finish phase time in microseconds.
189 pub scene_finish_us: u64,
190 /// Render phase time in microseconds.
191 pub render_us: u64,
192 /// Surface acquire time in microseconds.
193 pub acquire_us: u64,
194 /// Present time in microseconds.
195 pub present_us: u64,
196 /// Animation phase time in microseconds.
197 pub animation_us: u64,
198 /// Input phase time in microseconds.
199 pub input_us: u64,
200 /// Executor drain time before the frame in microseconds.
201 pub executor_before_us: u64,
202 /// Executor drain time after the frame in microseconds.
203 pub executor_after_us: u64,
204 /// Whether the frame performed a structural rebuild.
205 pub rebuilt: bool,
206 /// Whether the frame rendered.
207 pub rendered: bool,
208 /// Whether the frame captured a snapshot.
209 pub captured_snapshot: bool,
210 /// Process CPU usage in percent.
211 pub cpu_percent: f64,
212 /// Process memory footprint in bytes.
213 pub memory_bytes: u64,
214 /// GPU frame time (acquire + render + present) in microseconds.
215 pub gpu_frame_us: u64,
216 /// Measurement-cache hits this frame.
217 pub measurement_cache_hits: u64,
218 /// Measurement-cache misses this frame.
219 pub measurement_cache_misses: u64,
220 /// Compositor scene layers this frame.
221 pub scene_layers: u64,
222 /// Vello scene layers this frame.
223 pub vello_scene_layers: u64,
224 /// GPU surface layers this frame.
225 pub gpu_surface_layers: u64,
226 /// Clip layers pushed this frame.
227 pub clip_layers: u64,
228 /// Maximum clip depth this frame.
229 pub max_clip_depth: u64,
230 /// Applied filter count this frame.
231 pub applied_filter_count: u64,
232 /// Applied filter capture time this frame in microseconds.
233 pub applied_filter_capture_us: u64,
234 /// Applied filter effect time this frame in microseconds.
235 pub applied_filter_effect_us: u64,
236}