use std::pin::Pin;
use std::slice;
use std::task::{Context, Poll};
use std::time::Duration;
use anyhow::Result;
use ash::prelude::VkResult;
use ash::vk;
use crate::Device;
use crate::pool::Poolable;
struct CleanupFnLink<'f> {
pub f: Box<dyn FnOnce() + 'f>,
pub next: Option<Box<CleanupFnLink<'f>>>,
}
pub trait FenceValue<T> {
fn value(&mut self) -> Option<T>;
}
#[derive(Derivative)]
#[derivative(Debug)]
pub struct Fence<T = ()> {
device: Device,
#[derivative(Debug = "ignore")]
first_cleanup_fn: Option<Box<CleanupFnLink<'static>>>,
value: Option<T>,
handle: vk::Fence,
wait_thread_spawned: bool,
}
unsafe impl<T> Send for Fence<T> {}
impl<T> Unpin for Fence<T> {}
pub type GpuFuture<T> = Fence<T>;
impl<T> FenceValue<T> for Fence<T> {
fn value(&mut self) -> Option<T> {
self.value.take()
}
}
impl Fence<()> {
pub fn attach_value<T>(mut self, value: T) -> Fence<T> {
let mut handle = vk::Fence::null();
std::mem::swap(&mut self.handle, &mut handle);
Fence::<T> {
handle,
first_cleanup_fn: self.first_cleanup_fn.take(),
device: self.device.clone(),
value: Some(value),
wait_thread_spawned: false,
}
}
}
impl<T> Fence<T> {
pub fn new(device: Device, signaled: bool) -> Result<Self, vk::Result> {
let info = vk::FenceCreateInfo {
s_type: vk::StructureType::FENCE_CREATE_INFO,
p_next: std::ptr::null(),
flags: if signaled {
vk::FenceCreateFlags::SIGNALED
} else {
vk::FenceCreateFlags::empty()
},
};
let handle = unsafe { device.create_fence(&info, None)? };
#[cfg(feature = "log-objects")]
trace!("Created new VkFence {handle:p}");
Ok(Fence {
handle,
device,
first_cleanup_fn: None,
value: None,
wait_thread_spawned: false,
})
}
#[deprecated(since = "0.10.0", note="`poll_rate` is ignored, use `new` instead")]
pub fn new_with_poll_rate(
device: Device,
signaled: bool,
_poll_rate: Duration,
) -> Result<Self, vk::Result> {
Self::new(device, signaled)
}
fn call_cleanup_chain(&mut self) {
let mut f = self.first_cleanup_fn.take();
while f.is_some() {
let func = f.take().unwrap();
(func.f)();
f = func.next
}
}
fn poll_status(&self) -> VkResult<bool> {
unsafe { self.device.get_fence_status(self.handle) }
}
pub(crate) unsafe fn wait_without_cleanup(&self) -> VkResult<()> {
self.device
.wait_for_fences(slice::from_ref(&self.handle), true, u64::MAX)
}
pub fn wait_and_yield(&mut self) -> Result<Option<T>> {
loop {
if self.poll_status()? {
break;
}
#[cfg(feature = "rayon")]
{
match rayon::yield_now() {
Some(rayon::Yield::Idle) => {
std::thread::yield_now();
}
_ => {}
}
}
#[cfg(not(feature = "rayon"))]
{
std::thread::yield_now();
}
}
self.call_cleanup_chain();
Ok(self.value())
}
pub fn wait(&mut self) -> Result<Option<T>> {
let result = unsafe { self.wait_without_cleanup() };
self.call_cleanup_chain();
Ok(result.map(|_| self.value())?)
}
pub fn reset(&self) -> VkResult<()> {
unsafe { self.device.reset_fences(slice::from_ref(&self.handle)) }
}
pub fn with_cleanup(mut self, f: impl FnOnce() + 'static) -> Self {
if self.first_cleanup_fn.is_some() {
let mut head = Box::new(CleanupFnLink {
f: Box::new(f),
next: None,
});
let fun = self.first_cleanup_fn.take().unwrap();
head.next = Some(fun);
self.first_cleanup_fn = Some(head);
self
} else {
self.first_cleanup_fn = Some(Box::new(CleanupFnLink {
f: Box::new(f),
next: None,
}));
self
}
}
pub unsafe fn handle(&self) -> vk::Fence {
self.handle
}
}
impl<T> std::future::Future for Fence<T> {
type Output = Option<T>;
fn poll(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
let status = unsafe { self.device.get_fence_status(self.handle).unwrap() };
if status {
self.call_cleanup_chain();
self.wait_thread_spawned = false;
return Poll::Ready(self.as_mut().value());
} else if !self.wait_thread_spawned {
let waker = ctx.waker().clone();
let device = self.device.clone();
let fence_handle = self.handle;
std::thread::spawn(move || {
unsafe {
device
.wait_for_fences(slice::from_ref(&fence_handle), true, u64::MAX)
.unwrap();
}
waker.wake();
});
}
Poll::Pending
}
}
impl<T> Drop for Fence<T> {
fn drop(&mut self) {
#[cfg(feature = "log-objects")]
trace!("Destroying VkFence {:p}", self.handle);
unsafe {
self.device.destroy_fence(self.handle, None);
}
}
}
impl<T> Poolable for Fence<T> {
type Key = ();
fn on_release(&mut self) {
self.reset().unwrap();
self.value = None;
self.first_cleanup_fn = None;
}
}