128 lines
3.5 KiB
Rust
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,
|
|
// }
|