Files
yawn/core/render_graph/profiler.rs
2026-08-24 00:27:03 +05:30

128 lines
3.5 KiB
Rust

use serde::Serialize;
pub struct Profiler {
pub query_set: wgpu::QuerySet,
pub resolve: wgpu::Buffer,
pub readback: wgpu::Buffer,
pub query_count: u32,
pub pass_labels: Vec<String>,
}
impl Profiler {
pub fn new(device: &wgpu::Device, render_pass_count: u32, pass_labels: Vec<String>) -> Self {
let query_count = render_pass_count * 2;
let size = u64::from(query_count) * 8;
Self {
query_set: device.create_query_set(&wgpu::QuerySetDescriptor {
label: Some("frame-profile"),
ty: wgpu::QueryType::Timestamp,
count: query_count,
}),
resolve: device.create_buffer(&wgpu::BufferDescriptor {
label: Some("frame-profile-resolve"),
size,
usage: wgpu::BufferUsages::QUERY_RESOLVE | wgpu::BufferUsages::COPY_SRC,
mapped_at_creation: false,
}),
readback: device.create_buffer(&wgpu::BufferDescriptor {
label: Some("frame-profile-readback"),
size,
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ,
mapped_at_creation: false,
}),
query_count,
pass_labels,
}
}
pub fn encode_query_set(&self, encoder: &mut wgpu::CommandEncoder) {
encoder.resolve_query_set(&self.query_set, 0..self.query_count, &self.resolve, 0);
encoder.copy_buffer_to_buffer(
&self.resolve,
0,
&self.readback,
0,
u64::from(self.query_count) * 8,
);
}
pub fn read_passes(self) -> Vec<Pass> {
let bytes = self.readback.get_mapped_range(..);
let values = bytes
.chunks_exact(8)
.map(|bytes| u64::from_le_bytes(bytes.try_into().unwrap()))
.collect::<Vec<_>>();
let profile = self
.pass_labels
.into_iter()
.zip(values.chunks_exact(2))
.map(|(label, timestamps)| Pass {
name: label,
milliseconds: timestamps[1].saturating_sub(timestamps[0]) as f64
* f64::from(self.timestamp_period)
/ 1_000_000.0,
})
.collect();
drop(bytes);
self.readback.unmap();
profile
}
pub async fn read_render_pass_data(&self) -> Result<Vec<Pass>, wgpu::BufferMapError> {
self.readback
.map_async(wgpu::MapMode::Read, .., |result| {
result.and(self.read_passes());
})
.await?;
}
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct FrameData {
pub frame: u32,
pub milliseconds: f64,
pub readback_milliseconds: f64,
pub adapter: String,
pub canvas: Canvas,
pub passes: Vec<Pass>,
}
impl std::fmt::Display for FrameData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
serde_json::to_string(self)
.map_err(|_| std::fmt::Error)?
.fmt(f)
}
}
#[derive(Serialize)]
pub struct Canvas {
pub width: u32,
pub height: u32,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Pass {
pub name: String,
pub milliseconds: f64,
}
pub enum ProfileState {
Detached,
Attached,
Ready(FrameData),
}
// pub struct ProfileMap {
// readback: Arc<wgpu::Buffer>,
// state: Arc<AtomicU8>,
// labels: Vec<String>,
// timestamp_period: f32,
// }