use std::collections::VecDeque;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use alpha2_gpu::{Capabilities, CapabilityTier};
use crate::{
PipelineCacheStats, ReadbackBufferPoolStats, TexturePoolStats, UniformBufferPoolStats,
UploadTexturePoolStats,
};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum TimingSource {
GpuTimestamp,
CpuEncoding,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct PassTiming {
pub label: &'static str,
pub nanoseconds: u64,
pub source: TimingSource,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AdapterDiagnostics {
pub name: String,
pub backend: String,
pub driver: String,
pub driver_info: String,
pub tier: CapabilityTier,
pub timestamp_queries: bool,
}
#[derive(Clone, Debug, PartialEq)]
pub struct RenderDiagnostics {
pub adapter: AdapterDiagnostics,
pub passes: Vec<PassTiming>,
pub textures: TexturePoolStats,
pub upload_textures: UploadTexturePoolStats,
pub readback_buffers: ReadbackBufferPoolStats,
pub uniform_buffers: UniformBufferPoolStats,
pub pipelines: PipelineCacheStats,
pub result_cache: crate::ResultCacheStats,
pub presented_frames: u64,
pub dropped_frames: u64,
pub repeated_frames: u64,
pub decoder_wait: Duration,
pub max_filter_samples: u32,
pub motion_blur_subframes: u32,
pub degradations: Vec<crate::CapabilityDiagnostic>,
pub graph_nodes: u64,
pub graph_passes: u64,
pub graph_submissions: u64,
pub source_submissions: u64,
pub source_conversions: u64,
pub yuv_repacks: u64,
pub readback_submissions: u64,
pub frame_submissions: u64,
pub render_passes: u64,
pub copy_operations: u64,
pub copy_bytes: u64,
}
#[derive(Clone, Debug, PartialEq)]
pub struct BenchmarkMetadata {
pub project: String,
pub alpha2_version: &'static str,
pub adapter: AdapterDiagnostics,
pub dimensions: (u32, u32),
pub resolution_scale: f32,
pub max_filter_samples: u32,
pub motion_blur_subframes: u32,
pub texture_budget_bytes: u64,
}
const PASS_HISTORY: usize = 256;
#[derive(Debug, Default)]
pub(crate) struct DiagnosticsState {
passes: VecDeque<PassTiming>,
timing_epoch: u64,
presented_frames: u64,
dropped_frames: u64,
repeated_frames: u64,
decoder_wait: Duration,
graph_nodes: u64,
graph_passes: u64,
graph_submissions: u64,
source_submissions: u64,
source_conversions: u64,
yuv_repacks: u64,
readback_submissions: u64,
frame_submissions: u64,
render_passes: u64,
copy_operations: u64,
copy_bytes: u64,
}
pub(crate) type SharedDiagnostics = Arc<Mutex<DiagnosticsState>>;
#[derive(Copy, Clone)]
pub(crate) struct ResourceDiagnostics {
pub(crate) textures: TexturePoolStats,
pub(crate) upload_textures: UploadTexturePoolStats,
pub(crate) readback_buffers: ReadbackBufferPoolStats,
pub(crate) uniform_buffers: UniformBufferPoolStats,
pub(crate) pipelines: PipelineCacheStats,
pub(crate) result_cache: crate::ResultCacheStats,
}
pub(crate) fn timing_epoch(state: &SharedDiagnostics) -> u64 {
state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.timing_epoch
}
pub(crate) fn record_pass(
state: &SharedDiagnostics,
epoch: u64,
label: &'static str,
duration: Duration,
source: TimingSource,
) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if state.timing_epoch != epoch {
return;
}
if state.passes.len() == PASS_HISTORY {
state.passes.pop_front();
}
state.passes.push_back(PassTiming {
label,
nanoseconds: u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX),
source,
});
}
pub(crate) fn record_graph_work(
state: &SharedDiagnostics,
nodes: u64,
render_passes: u64,
standalone_submission: bool,
) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.graph_nodes = state.graph_nodes.saturating_add(nodes);
state.graph_passes = state.graph_passes.saturating_add(render_passes);
if standalone_submission {
state.graph_submissions = state.graph_submissions.saturating_add(1);
}
state.render_passes = state.render_passes.saturating_add(render_passes);
}
pub(crate) fn record_source_work(
state: &SharedDiagnostics,
render_passes: u64,
standalone_submission: bool,
conversion: bool,
) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.source_conversions = state
.source_conversions
.saturating_add(u64::from(conversion));
if standalone_submission {
state.source_submissions = state.source_submissions.saturating_add(1);
}
state.render_passes = state.render_passes.saturating_add(render_passes);
}
pub(crate) fn record_readback_work(
state: &SharedDiagnostics,
render_passes: u64,
copy_operations: u64,
copy_bytes: u64,
standalone_submission: bool,
) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
if standalone_submission {
state.readback_submissions = state.readback_submissions.saturating_add(1);
}
state.render_passes = state.render_passes.saturating_add(render_passes);
state.copy_operations = state.copy_operations.saturating_add(copy_operations);
state.copy_bytes = state.copy_bytes.saturating_add(copy_bytes);
}
pub(crate) fn record_frame_submission(state: &SharedDiagnostics) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.frame_submissions = state.frame_submissions.saturating_add(1);
}
pub(crate) fn end_timing_run(state: &SharedDiagnostics) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.passes.clear();
state.timing_epoch = state.timing_epoch.wrapping_add(1);
}
pub(crate) fn snapshot(
state: &SharedDiagnostics,
capabilities: &Capabilities,
resources: &ResourceDiagnostics,
max_filter_samples: u32,
motion_blur_subframes: u32,
degradations: Vec<crate::CapabilityDiagnostic>,
) -> RenderDiagnostics {
let state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
RenderDiagnostics {
adapter: AdapterDiagnostics {
name: capabilities.adapter_name.clone(),
backend: capabilities.backend.clone(),
driver: capabilities.driver.clone(),
driver_info: capabilities.driver_info.clone(),
tier: capabilities.tier,
timestamp_queries: capabilities.timestamp_queries,
},
passes: state.passes.iter().copied().collect(),
textures: resources.textures,
upload_textures: resources.upload_textures,
readback_buffers: resources.readback_buffers,
uniform_buffers: resources.uniform_buffers,
pipelines: resources.pipelines,
result_cache: resources.result_cache,
presented_frames: state.presented_frames,
dropped_frames: state.dropped_frames,
repeated_frames: state.repeated_frames,
decoder_wait: state.decoder_wait,
max_filter_samples,
motion_blur_subframes,
degradations,
graph_nodes: state.graph_nodes,
graph_passes: state.graph_passes,
graph_submissions: state.graph_submissions,
source_submissions: state.source_submissions,
source_conversions: state.source_conversions,
yuv_repacks: state.yuv_repacks,
readback_submissions: state.readback_submissions,
frame_submissions: state.frame_submissions,
render_passes: state.render_passes,
copy_operations: state.copy_operations,
copy_bytes: state.copy_bytes,
}
}
pub(crate) fn record_yuv_repack(state: &SharedDiagnostics) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.yuv_repacks = state.yuv_repacks.saturating_add(1);
}
pub(crate) fn record_frame(state: &SharedDiagnostics, presented: bool, repeated: bool) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.presented_frames += u64::from(presented);
state.dropped_frames += u64::from(!presented);
state.repeated_frames += u64::from(repeated);
}
pub(crate) fn record_decoder_wait(state: &SharedDiagnostics, duration: Duration) {
let mut state = state
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner);
state.decoder_wait = state.decoder_wait.saturating_add(duration);
}