use std::future::Future;
use std::ops::DerefMut;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use std::task::{Context, Poll, Waker};
use wgpu::{Device, Maintain};
#[cfg(not(target_arch = "wasm32"))]
use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug)]
pub(crate) struct PollLoop {
#[cfg(not(target_arch = "wasm32"))]
has_work: Arc<AtomicBool>,
#[cfg(not(target_arch = "wasm32"))]
is_done: Arc<AtomicBool>,
#[cfg(not(target_arch = "wasm32"))]
handle: std::thread::JoinHandle<()>,
}
impl PollLoop {
pub(crate) fn new(device: Arc<Device>) -> Self {
#[cfg(target_arch = "wasm32")]
{
let _ = device;
Self {}
}
#[cfg(not(target_arch = "wasm32"))]
{
let has_work = Arc::new(AtomicBool::new(false));
let is_done = Arc::new(AtomicBool::new(false));
let locally_has_work = has_work.clone();
let locally_is_done = is_done.clone();
Self {
has_work,
is_done,
handle: std::thread::spawn(move || {
while !locally_is_done.load(Ordering::Acquire) {
while locally_has_work.swap(false, Ordering::AcqRel) {
device.poll(Maintain::Wait);
}
std::thread::park();
}
}),
}
}
}
pub(crate) fn start_polling(&self) {
#[cfg(not(target_arch = "wasm32"))]
{
self.has_work.store(true, Ordering::Release);
self.handle.thread().unpark()
}
}
}
impl Drop for PollLoop {
fn drop(&mut self) {
#[cfg(not(target_arch = "wasm32"))]
{
self.is_done.store(true, Ordering::Release);
self.handle.thread().unpark()
}
}
}
struct WgpuFutureSharedState<T> {
result: Option<T>,
waker: Option<Waker>,
}
pub struct WgpuFuture<T> {
device: Arc<Device>,
poll_loop: Arc<PollLoop>,
state: Arc<Mutex<WgpuFutureSharedState<T>>>,
}
impl<T: Send + 'static> WgpuFuture<T> {
pub(crate) fn new(device: Arc<Device>, poll_loop: Arc<PollLoop>) -> Self {
Self {
device,
poll_loop,
state: Arc::new(Mutex::new(WgpuFutureSharedState {
result: None,
waker: None,
})),
}
}
pub(crate) fn callback(&self) -> Box<dyn FnOnce(T) + Send> {
let shared_state = self.state.clone();
return Box::new(move |res: T| {
let mut lock = shared_state
.lock()
.expect("wgpu future was poisoned on complete");
let shared_state = lock.deref_mut();
shared_state.result = Some(res);
if let Some(waker) = shared_state.waker.take() {
waker.wake()
}
});
}
}
impl<T> Future for WgpuFuture<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.device.poll(Maintain::Poll);
{
let mut lock = self.state.lock().expect("wgpu future was poisoned on poll");
if let Some(res) = lock.result.take() {
return Poll::Ready(res);
}
lock.waker = Some(cx.waker().clone());
}
self.poll_loop.start_polling();
return Poll::Pending;
}
}