use std::error;
use std::fmt;
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::time::Duration;
use smallvec::SmallVec;
use device::Device;
use device::DeviceOwned;
use Error;
use OomError;
use SafeDeref;
use Success;
use VulkanObject;
use VulkanPointers;
use check_errors;
use vk;
#[derive(Debug)]
pub struct Fence<D = Arc<Device>> where D: SafeDeref<Target = Device> {
fence: vk::Fence,
device: D,
signaled: AtomicBool,
}
impl<D> Fence<D> where D: SafeDeref<Target = Device> {
#[inline]
pub fn new(device: D) -> Result<Fence<D>, OomError> {
Fence::new_impl(device, false)
}
#[inline]
pub fn signaled(device: D) -> Result<Fence<D>, OomError> {
Fence::new_impl(device, true)
}
fn new_impl(device: D, signaled: bool) -> Result<Fence<D>, OomError> {
let fence = unsafe {
let infos = vk::FenceCreateInfo {
sType: vk::STRUCTURE_TYPE_FENCE_CREATE_INFO,
pNext: ptr::null(),
flags: if signaled { vk::FENCE_CREATE_SIGNALED_BIT } else { 0 },
};
let vk = device.pointers();
let mut output = mem::uninitialized();
try!(check_errors(vk.CreateFence(device.internal_object(), &infos,
ptr::null(), &mut output)));
output
};
Ok(Fence {
fence: fence,
device: device,
signaled: AtomicBool::new(signaled),
})
}
#[inline]
pub fn ready(&self) -> Result<bool, OomError> {
unsafe {
if self.signaled.load(Ordering::Relaxed) { return Ok(true); }
let vk = self.device.pointers();
let result = try!(check_errors(vk.GetFenceStatus(self.device.internal_object(),
self.fence)));
match result {
Success::Success => {
self.signaled.store(true, Ordering::Relaxed);
Ok(true)
},
Success::NotReady => Ok(false),
_ => unreachable!()
}
}
}
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceWaitError> {
unsafe {
if self.signaled.load(Ordering::Relaxed) { return Ok(()); }
let timeout_ns = if let Some(timeout) = timeout {
timeout.as_secs().saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
} else {
u64::max_value()
};
let vk = self.device.pointers();
let r = try!(check_errors(vk.WaitForFences(self.device.internal_object(), 1,
&self.fence, vk::TRUE, timeout_ns)));
match r {
Success::Success => {
self.signaled.store(true, Ordering::Relaxed);
Ok(())
},
Success::Timeout => {
Err(FenceWaitError::Timeout)
},
_ => unreachable!()
}
}
}
pub fn multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceWaitError>
where I: IntoIterator<Item = &'a Fence<D>>, D: 'a
{
let mut device: Option<&Device> = None;
let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().filter_map(|fence| {
match &mut device {
dev @ &mut None => *dev = Some(&*fence.device),
&mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
_ => panic!("Tried to wait for multiple fences that didn't belong to the \
same device"),
};
if fence.signaled.load(Ordering::Relaxed) {
None
} else {
Some(fence.fence)
}
}).collect();
let timeout_ns = if let Some(timeout) = timeout {
timeout.as_secs().saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
} else {
u64::max_value()
};
let r = if let Some(device) = device {
unsafe {
let vk = device.pointers();
try!(check_errors(vk.WaitForFences(device.internal_object(), fences.len() as u32,
fences.as_ptr(), vk::TRUE, timeout_ns)))
}
} else {
return Ok(());
};
match r {
Success::Success => Ok(()),
Success::Timeout => Err(FenceWaitError::Timeout),
_ => unreachable!()
}
}
#[inline]
pub fn reset(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.ResetFences(self.device.internal_object(), 1, &self.fence);
self.signaled.store(false, Ordering::Relaxed);
}
}
pub fn multi_reset<'a, I>(iter: I)
where I: IntoIterator<Item = &'a mut Fence<D>>, D: 'a
{
let mut device: Option<&Device> = None;
let fences: SmallVec<[vk::Fence; 8]> = iter.into_iter().map(|fence| {
match &mut device {
dev @ &mut None => *dev = Some(&*fence.device),
&mut Some(ref dev) if &**dev as *const Device == &*fence.device as *const Device => {},
_ => panic!("Tried to reset multiple fences that didn't belong to the same device"),
};
fence.signaled.store(false, Ordering::Relaxed);
fence.fence
}).collect();
if let Some(device) = device {
unsafe {
let vk = device.pointers();
vk.ResetFences(device.internal_object(), fences.len() as u32, fences.as_ptr());
}
}
}
}
unsafe impl DeviceOwned for Fence {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
unsafe impl<D> VulkanObject for Fence<D> where D: SafeDeref<Target = Device> {
type Object = vk::Fence;
#[inline]
fn internal_object(&self) -> vk::Fence {
self.fence
}
}
impl<D> Drop for Fence<D> where D: SafeDeref<Target = Device> {
#[inline]
fn drop(&mut self) {
unsafe {
let vk = self.device.pointers();
vk.DestroyFence(self.device.internal_object(), self.fence, ptr::null());
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FenceWaitError {
OomError(OomError),
Timeout,
DeviceLostError,
}
impl error::Error for FenceWaitError {
#[inline]
fn description(&self) -> &str {
match *self {
FenceWaitError::OomError(_) => "no memory available",
FenceWaitError::Timeout => "the timeout has been reached",
FenceWaitError::DeviceLostError => "the device was lost",
}
}
#[inline]
fn cause(&self) -> Option<&error::Error> {
match *self {
FenceWaitError::OomError(ref err) => Some(err),
_ => None
}
}
}
impl fmt::Display for FenceWaitError {
#[inline]
fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(fmt, "{}", error::Error::description(self))
}
}
impl From<Error> for FenceWaitError {
#[inline]
fn from(err: Error) -> FenceWaitError {
match err {
Error::OutOfHostMemory => FenceWaitError::OomError(From::from(err)),
Error::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)),
Error::DeviceLost => FenceWaitError::DeviceLostError,
_ => panic!("Unexpected error value: {}", err as i32)
}
}
}
#[cfg(test)]
mod tests {
use std::time::Duration;
use sync::Fence;
#[test]
fn fence_create() {
let (device, _) = gfx_dev_and_queue!();
let fence = Fence::new(device.clone()).unwrap();
assert!(!fence.ready().unwrap());
}
#[test]
fn fence_create_signaled() {
let (device, _) = gfx_dev_and_queue!();
let fence = Fence::signaled(device.clone()).unwrap();
assert!(fence.ready().unwrap());
}
#[test]
fn fence_signaled_wait() {
let (device, _) = gfx_dev_and_queue!();
let fence = Fence::signaled(device.clone()).unwrap();
fence.wait(Some(Duration::new(0, 10))).unwrap();
}
#[test]
fn fence_reset() {
let (device, _) = gfx_dev_and_queue!();
let mut fence = Fence::signaled(device.clone()).unwrap();
fence.reset();
assert!(!fence.ready().unwrap());
}
#[test]
#[should_panic(expected = "Tried to wait for multiple fences that didn't belong to the same device")]
fn multiwait_different_devices() {
let (device1, _) = gfx_dev_and_queue!();
let (device2, _) = gfx_dev_and_queue!();
let fence1 = Fence::signaled(device1.clone()).unwrap();
let fence2 = Fence::signaled(device2.clone()).unwrap();
let _ = Fence::multi_wait([&fence1, &fence2].iter().cloned(), Some(Duration::new(0, 10)));
}
#[test]
#[should_panic(expected = "Tried to reset multiple fences that didn't belong to the same device")]
fn multireset_different_devices() {
use std::iter::once;
let (device1, _) = gfx_dev_and_queue!();
let (device2, _) = gfx_dev_and_queue!();
let mut fence1 = Fence::signaled(device1.clone()).unwrap();
let mut fence2 = Fence::signaled(device2.clone()).unwrap();
let _ = Fence::multi_reset(once(&mut fence1).chain(once(&mut fence2)));
}
}