End-of-frame bookkeeping. Reads back GPU timestamps from the previous frame (non-blocking map), folds samples into rolling stats, and clears per-frame state for the next frame.
(&mut self, device: &wgpu::Device)
| 227 | /// previous frame (non-blocking map), folds samples into rolling |
| 228 | /// stats, and clears per-frame state for the next frame. |
| 229 | pub fn frame_end(&mut self, device: &wgpu::Device) { |
| 230 | if !self.enabled { |
| 231 | self.frame.clear(); |
| 232 | self.open_cpu.clear(); |
| 233 | self.next_query = 0; |
| 234 | self.pending_gpu.clear(); |
| 235 | return; |
| 236 | } |
| 237 | |
| 238 | if self.gpu_enabled && self.next_query > 0 { |
| 239 | if let Some(readback) = &self.readback_buffer { |
| 240 | let byte_count = (self.next_query as u64) * 8; |
| 241 | let slice = readback.slice(0..byte_count); |
| 242 | slice.map_async(wgpu::MapMode::Read, |_| {}); |
| 243 | let _ = device.poll(wgpu::PollType::Wait { submission_index: None, timeout: None }); |
| 244 | let data = slice.get_mapped_range().to_vec(); |
| 245 | readback.unmap(); |
| 246 | let period = self.timestamp_period_ns as f64; |
| 247 | let mut by_label: HashMap<&'static str, f64> = HashMap::new(); |
| 248 | for (label, b, e) in &self.pending_gpu { |
| 249 | let bo = (*b as usize) * 8; |
| 250 | let eo = (*e as usize) * 8; |
| 251 | if eo + 8 > data.len() { continue; } |
| 252 | let bt = u64::from_le_bytes(data[bo..bo+8].try_into().unwrap()); |
| 253 | let et = u64::from_le_bytes(data[eo..eo+8].try_into().unwrap()); |
| 254 | if et <= bt { continue; } |
| 255 | let us = (et - bt) as f64 * period / 1000.0; |
| 256 | *by_label.entry(*label).or_insert(0.0) += us; |
| 257 | } |
| 258 | for s in self.frame.iter_mut() { |
| 259 | if let Some(us) = by_label.remove(s.label) { s.gpu_us = Some(us); } |
| 260 | } |
| 261 | // GPU samples without a CPU counterpart — record them too. |
| 262 | for (label, us) in by_label { |
| 263 | self.frame.push(FrameSample { label, cpu_us: 0.0, gpu_us: Some(us) }); |
| 264 | } |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | self.frame_end_cpu(); |
| 269 | } |
| 270 | |
| 271 | /// CPU-only end-of-frame: histogram update + drain into rolling. |
| 272 | /// Split out so tests don't need a wgpu::Device. Production |