webgpu: Implement device lost (#32354)

* device lost promise should be init at creation of device object

* device lost impl

* lock for device poll

workaround for wgpu deadlocks

* expect

* Less lost reason reasoning in script
This commit is contained in:
Samson 2024-06-17 14:47:25 +02:00 committed by GitHub
parent 3381f2a704
commit cbc9304c20
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 224 additions and 5628 deletions

View file

@ -7,7 +7,7 @@
//! This is roughly based on <https://github.com/LucentFlux/wgpu-async/blob/1322c7e3fcdfc1865a472c7bbbf0e2e06dcf4da8/src/wgpu_future.rs>
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::{Arc, Mutex, MutexGuard};
use std::thread::JoinHandle;
use log::warn;
@ -40,14 +40,25 @@ pub(crate) struct Poller {
is_done: Arc<AtomicBool>,
/// Handle to the WGPU poller thread (to be used for unparking the thread)
handle: Option<JoinHandle<()>>,
/// Lock for device maintain calls (in poll_all_devices and queue_submit)
///
/// This is workaround for wgpu deadlocks: https://github.com/gfx-rs/wgpu/issues/5572
lock: Arc<Mutex<()>>,
}
#[inline]
fn poll_all_devices(global: &Arc<Global>, more_work: &mut bool, force_wait: bool) {
fn poll_all_devices(
global: &Arc<Global>,
more_work: &mut bool,
force_wait: bool,
lock: &Mutex<()>,
) {
let _guard = lock.lock().unwrap();
match global.poll_all_devices(force_wait) {
Ok(all_queue_empty) => *more_work = !all_queue_empty,
Err(e) => warn!("Poller thread got `{e}` on poll_all_devices."),
}
// drop guard
}
impl Poller {
@ -56,9 +67,11 @@ impl Poller {
let is_done = Arc::new(AtomicBool::new(false));
let work = work_count.clone();
let done = is_done.clone();
let lock = Arc::new(Mutex::new(()));
Self {
work_count,
is_done,
lock: Arc::clone(&lock),
handle: Some(
std::thread::Builder::new()
.name("WGPU poller".into())
@ -69,9 +82,9 @@ impl Poller {
// so every `ẁake` (even spurious) will do at least one poll.
// this is mostly useful for stuff that is deferred
// to maintain calls in wgpu (device resource destruction)
poll_all_devices(&global, &mut more_work, false);
poll_all_devices(&global, &mut more_work, false, &lock);
while more_work || work.load(Ordering::Acquire) != 0 {
poll_all_devices(&global, &mut more_work, true);
poll_all_devices(&global, &mut more_work, true, &lock);
}
std::thread::park(); //TODO: should we use timeout here
}
@ -101,6 +114,11 @@ impl Poller {
.thread()
.unpark();
}
/// Lock for device maintain calls (in poll_all_devices and queue_submit)
pub(crate) fn lock(&self) -> MutexGuard<()> {
self.lock.lock().unwrap()
}
}
impl Drop for Poller {