1use std::collections::VecDeque;
4use std::sync::{Arc, Mutex};
5use std::time::Duration;
6
7use alpha2_gpu::{Capabilities, CapabilityTier};
8
9use crate::{
10 PipelineCacheStats, ReadbackBufferPoolStats, TexturePoolStats, UniformBufferPoolStats,
11 UploadTexturePoolStats,
12};
13
14#[derive(Copy, Clone, Debug, Eq, PartialEq)]
16pub enum TimingSource {
17 GpuTimestamp,
19 CpuEncoding,
21}
22
23#[derive(Copy, Clone, Debug, Eq, PartialEq)]
27pub struct PassTiming {
28 pub label: &'static str,
30 pub nanoseconds: u64,
32 pub source: TimingSource,
34}
35
36#[derive(Clone, Debug, Eq, PartialEq)]
38pub struct AdapterDiagnostics {
39 pub name: String,
41 pub backend: String,
43 pub driver: String,
45 pub driver_info: String,
47 pub tier: CapabilityTier,
49 pub timestamp_queries: bool,
51}
52
53#[derive(Clone, Debug, PartialEq)]
55pub struct RenderDiagnostics {
56 pub adapter: AdapterDiagnostics,
58 pub passes: Vec<PassTiming>,
60 pub textures: TexturePoolStats,
62 pub upload_textures: UploadTexturePoolStats,
64 pub readback_buffers: ReadbackBufferPoolStats,
66 pub uniform_buffers: UniformBufferPoolStats,
68 pub pipelines: PipelineCacheStats,
70 pub result_cache: crate::ResultCacheStats,
72 pub presented_frames: u64,
74 pub dropped_frames: u64,
76 pub repeated_frames: u64,
78 pub decoder_wait: Duration,
80 pub max_filter_samples: u32,
83 pub motion_blur_subframes: u32,
85 pub degradations: Vec<crate::CapabilityDiagnostic>,
88 pub graph_nodes: u64,
90 pub graph_passes: u64,
92 pub graph_submissions: u64,
94 pub source_submissions: u64,
96 pub source_conversions: u64,
99 pub yuv_repacks: u64,
102 pub readback_submissions: u64,
104 pub frame_submissions: u64,
106 pub render_passes: u64,
108 pub copy_operations: u64,
110 pub copy_bytes: u64,
112}
113
114#[derive(Clone, Debug, PartialEq)]
116pub struct BenchmarkMetadata {
117 pub project: String,
119 pub alpha2_version: &'static str,
121 pub adapter: AdapterDiagnostics,
123 pub dimensions: (u32, u32),
125 pub resolution_scale: f32,
127 pub max_filter_samples: u32,
129 pub motion_blur_subframes: u32,
131 pub texture_budget_bytes: u64,
133}
134
135const PASS_HISTORY: usize = 256;
137
138#[derive(Debug, Default)]
139pub(crate) struct DiagnosticsState {
140 passes: VecDeque<PassTiming>,
141 timing_epoch: u64,
145 presented_frames: u64,
146 dropped_frames: u64,
147 repeated_frames: u64,
148 decoder_wait: Duration,
149 graph_nodes: u64,
150 graph_passes: u64,
151 graph_submissions: u64,
152 source_submissions: u64,
153 source_conversions: u64,
154 yuv_repacks: u64,
155 readback_submissions: u64,
156 frame_submissions: u64,
157 render_passes: u64,
158 copy_operations: u64,
159 copy_bytes: u64,
160}
161
162pub(crate) type SharedDiagnostics = Arc<Mutex<DiagnosticsState>>;
163
164#[derive(Copy, Clone)]
165pub(crate) struct ResourceDiagnostics {
166 pub(crate) textures: TexturePoolStats,
167 pub(crate) upload_textures: UploadTexturePoolStats,
168 pub(crate) readback_buffers: ReadbackBufferPoolStats,
169 pub(crate) uniform_buffers: UniformBufferPoolStats,
170 pub(crate) pipelines: PipelineCacheStats,
171 pub(crate) result_cache: crate::ResultCacheStats,
172}
173
174pub(crate) fn timing_epoch(state: &SharedDiagnostics) -> u64 {
177 state
178 .lock()
179 .unwrap_or_else(std::sync::PoisonError::into_inner)
180 .timing_epoch
181}
182
183pub(crate) fn record_pass(
188 state: &SharedDiagnostics,
189 epoch: u64,
190 label: &'static str,
191 duration: Duration,
192 source: TimingSource,
193) {
194 let mut state = state
195 .lock()
196 .unwrap_or_else(std::sync::PoisonError::into_inner);
197 if state.timing_epoch != epoch {
198 return;
199 }
200 if state.passes.len() == PASS_HISTORY {
201 state.passes.pop_front();
202 }
203 state.passes.push_back(PassTiming {
204 label,
205 nanoseconds: u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX),
206 source,
207 });
208}
209
210pub(crate) fn record_graph_work(
211 state: &SharedDiagnostics,
212 nodes: u64,
213 render_passes: u64,
214 standalone_submission: bool,
215) {
216 let mut state = state
217 .lock()
218 .unwrap_or_else(std::sync::PoisonError::into_inner);
219 state.graph_nodes = state.graph_nodes.saturating_add(nodes);
220 state.graph_passes = state.graph_passes.saturating_add(render_passes);
221 if standalone_submission {
222 state.graph_submissions = state.graph_submissions.saturating_add(1);
223 }
224 state.render_passes = state.render_passes.saturating_add(render_passes);
225}
226
227pub(crate) fn record_source_work(
228 state: &SharedDiagnostics,
229 render_passes: u64,
230 standalone_submission: bool,
231 conversion: bool,
232) {
233 let mut state = state
234 .lock()
235 .unwrap_or_else(std::sync::PoisonError::into_inner);
236 state.source_conversions = state
237 .source_conversions
238 .saturating_add(u64::from(conversion));
239 if standalone_submission {
240 state.source_submissions = state.source_submissions.saturating_add(1);
241 }
242 state.render_passes = state.render_passes.saturating_add(render_passes);
243}
244
245pub(crate) fn record_readback_work(
246 state: &SharedDiagnostics,
247 render_passes: u64,
248 copy_operations: u64,
249 copy_bytes: u64,
250 standalone_submission: bool,
251) {
252 let mut state = state
253 .lock()
254 .unwrap_or_else(std::sync::PoisonError::into_inner);
255 if standalone_submission {
256 state.readback_submissions = state.readback_submissions.saturating_add(1);
257 }
258 state.render_passes = state.render_passes.saturating_add(render_passes);
259 state.copy_operations = state.copy_operations.saturating_add(copy_operations);
260 state.copy_bytes = state.copy_bytes.saturating_add(copy_bytes);
261}
262
263pub(crate) fn record_frame_submission(state: &SharedDiagnostics) {
264 let mut state = state
265 .lock()
266 .unwrap_or_else(std::sync::PoisonError::into_inner);
267 state.frame_submissions = state.frame_submissions.saturating_add(1);
268}
269
270pub(crate) fn end_timing_run(state: &SharedDiagnostics) {
273 let mut state = state
274 .lock()
275 .unwrap_or_else(std::sync::PoisonError::into_inner);
276 state.passes.clear();
277 state.timing_epoch = state.timing_epoch.wrapping_add(1);
278}
279
280pub(crate) fn snapshot(
281 state: &SharedDiagnostics,
282 capabilities: &Capabilities,
283 resources: &ResourceDiagnostics,
284 max_filter_samples: u32,
285 motion_blur_subframes: u32,
286 degradations: Vec<crate::CapabilityDiagnostic>,
287) -> RenderDiagnostics {
288 let state = state
289 .lock()
290 .unwrap_or_else(std::sync::PoisonError::into_inner);
291 RenderDiagnostics {
292 adapter: AdapterDiagnostics {
293 name: capabilities.adapter_name.clone(),
294 backend: capabilities.backend.clone(),
295 driver: capabilities.driver.clone(),
296 driver_info: capabilities.driver_info.clone(),
297 tier: capabilities.tier,
298 timestamp_queries: capabilities.timestamp_queries,
299 },
300 passes: state.passes.iter().copied().collect(),
301 textures: resources.textures,
302 upload_textures: resources.upload_textures,
303 readback_buffers: resources.readback_buffers,
304 uniform_buffers: resources.uniform_buffers,
305 pipelines: resources.pipelines,
306 result_cache: resources.result_cache,
307 presented_frames: state.presented_frames,
308 dropped_frames: state.dropped_frames,
309 repeated_frames: state.repeated_frames,
310 decoder_wait: state.decoder_wait,
311 max_filter_samples,
312 motion_blur_subframes,
313 degradations,
314 graph_nodes: state.graph_nodes,
315 graph_passes: state.graph_passes,
316 graph_submissions: state.graph_submissions,
317 source_submissions: state.source_submissions,
318 source_conversions: state.source_conversions,
319 yuv_repacks: state.yuv_repacks,
320 readback_submissions: state.readback_submissions,
321 frame_submissions: state.frame_submissions,
322 render_passes: state.render_passes,
323 copy_operations: state.copy_operations,
324 copy_bytes: state.copy_bytes,
325 }
326}
327
328pub(crate) fn record_yuv_repack(state: &SharedDiagnostics) {
329 let mut state = state
330 .lock()
331 .unwrap_or_else(std::sync::PoisonError::into_inner);
332 state.yuv_repacks = state.yuv_repacks.saturating_add(1);
333}
334
335pub(crate) fn record_frame(state: &SharedDiagnostics, presented: bool, repeated: bool) {
336 let mut state = state
337 .lock()
338 .unwrap_or_else(std::sync::PoisonError::into_inner);
339 state.presented_frames += u64::from(presented);
340 state.dropped_frames += u64::from(!presented);
341 state.repeated_frames += u64::from(repeated);
342}
343
344pub(crate) fn record_decoder_wait(state: &SharedDiagnostics, duration: Duration) {
345 let mut state = state
346 .lock()
347 .unwrap_or_else(std::sync::PoisonError::into_inner);
348 state.decoder_wait = state.decoder_wait.saturating_add(duration);
349}