WIP: cross-platform
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
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,
|
||||
// }
|
||||
Reference in New Issue
Block a user