GPU: bounded readbacks block on the GPU instead of sleeping between polls

#139's bounded read_buffer polled the device without waiting and slept up
to 10 ms between polls, adding up to 10 ms to every readback (the desktop
viewer reads every GPU frame back): the gpu crate's tests took 227 s
against 119 s before. It now waits with PollType::Wait and the remaining
deadline as its timeout (a timeout still retires the context), then takes
the result, giving another thread that polled the same device up to 1 ms
to deliver its callback. The tests take 71–79 s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bflatastic 2026-10-07 07:52:38 +07:00
parent bfabc72be1
commit 80dff0fb0e
1 changed files with 5 additions and 2 deletions

View File

@ -628,14 +628,17 @@ impl GpuContext {
});
loop {
self.readbacks.check()?;
self.poll_readbacks(wgpu::PollType::Poll)?;
let remaining = deadline.saturating_duration_since(std::time::Instant::now());
if remaining.is_zero() {
let error = "GPU readback receive timed out; context retired".to_string();
self.readbacks.retire(error.clone());
return Err(error);
}
match rx.recv_timeout(remaining.min(std::time::Duration::from_millis(10))) {
// Block until the GPU is done (bounded by the deadline: a timeout retires the
// context) rather than polling and sleeping, which adds latency to every readback.
self.poll_readbacks(wgpu::PollType::Wait { submission_index: None, timeout: Some(remaining) })?;
// The callback may be running on another thread that polled the same device.
match rx.recv_timeout(remaining.min(std::time::Duration::from_millis(1))) {
Ok(result) => return result,
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {}
Err(e) => return Err(format!("GPU readback completion channel: {e}")),