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, } impl Profiler { pub fn new(device: &wgpu::Device, render_pass_count: u32, pass_labels: Vec) -> 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 { let bytes = self.readback.get_mapped_range(..); let values = bytes .chunks_exact(8) .map(|bytes| u64::from_le_bytes(bytes.try_into().unwrap())) .collect::>(); 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, 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, } 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, // state: Arc, // labels: Vec, // timestamp_period: f32, // }