use std::mem;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::time::Duration;
use buffer::BufferAccess;
use command_buffer::submit::SubmitAnyBuilder;
use command_buffer::submit::SubmitCommandBufferBuilder;
use device::Device;
use device::DeviceOwned;
use device::Queue;
use image::ImageAccess;
use image::ImageLayout;
use sync::AccessCheckError;
use sync::AccessFlagBits;
use sync::Fence;
use sync::FlushError;
use sync::GpuFuture;
use sync::PipelineStages;
#[inline]
pub fn then_signal_fence<F>(future: F, behavior: FenceSignalFutureBehavior) -> FenceSignalFuture<F>
where F: GpuFuture
{
let device = future.device().clone();
assert!(future.queue().is_some());
let fence = Fence::from_pool(device.clone()).unwrap();
FenceSignalFuture {
device: device,
state: Mutex::new(FenceSignalFutureState::Pending(future, fence)),
behavior: behavior,
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FenceSignalFutureBehavior {
Continue,
Block {
timeout: Option<Duration>,
},
}
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished \
processing the submission"]
pub struct FenceSignalFuture<F>
where F: GpuFuture
{
state: Mutex<FenceSignalFutureState<F>>,
device: Arc<Device>,
behavior: FenceSignalFutureBehavior,
}
enum FenceSignalFutureState<F> {
Pending(F, Fence),
PartiallyFlushed(F, Fence),
Flushed(F, Fence),
Cleaned,
Poisoned,
}
impl<F> FenceSignalFuture<F>
where F: GpuFuture
{
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FlushError> {
let mut state = self.state.lock().unwrap();
self.flush_impl(&mut state)?;
match mem::replace(&mut *state, FenceSignalFutureState::Cleaned) {
FenceSignalFutureState::Flushed(previous, fence) => {
fence.wait(timeout)?;
unsafe {
previous.signal_finished();
}
Ok(())
},
FenceSignalFutureState::Cleaned => Ok(()),
_ => unreachable!(),
}
}
}
impl<F> FenceSignalFuture<F>
where F: GpuFuture
{
#[inline]
fn cleanup_finished_impl(&self) {
let mut state = self.state.lock().unwrap();
match *state {
FenceSignalFutureState::Flushed(ref mut prev, ref fence) => {
match fence.wait(Some(Duration::from_secs(0))) {
Ok(()) => unsafe {
prev.signal_finished()
},
Err(_) => {
prev.cleanup_finished();
return;
},
}
},
FenceSignalFutureState::Pending(ref mut prev, _) => {
prev.cleanup_finished();
return;
},
FenceSignalFutureState::PartiallyFlushed(ref mut prev, _) => {
prev.cleanup_finished();
return;
},
_ => return,
};
*state = FenceSignalFutureState::Cleaned;
}
fn flush_impl(&self, state: &mut MutexGuard<FenceSignalFutureState<F>>)
-> Result<(), FlushError> {
unsafe {
let old_state = mem::replace(&mut **state, FenceSignalFutureState::Poisoned);
let (previous, fence, partially_flushed) = match old_state {
FenceSignalFutureState::Pending(prev, fence) => {
(prev, fence, false)
},
FenceSignalFutureState::PartiallyFlushed(prev, fence) => {
(prev, fence, true)
},
other => {
**state = other;
return Ok(());
},
};
let queue = previous.queue().unwrap().clone();
enum OutcomeErr<E> {
Partial(E),
Full(E),
}
let result = match previous.build_submission()? {
SubmitAnyBuilder::Empty => {
debug_assert!(!partially_flushed);
let mut b = SubmitCommandBufferBuilder::new();
b.set_fence_signal(&fence);
b.submit(&queue).map_err(|err| OutcomeErr::Full(err.into()))
},
SubmitAnyBuilder::SemaphoresWait(sem) => {
debug_assert!(!partially_flushed);
let b: SubmitCommandBufferBuilder = sem.into();
debug_assert!(!b.has_fence());
b.submit(&queue).map_err(|err| OutcomeErr::Full(err.into()))
},
SubmitAnyBuilder::CommandBuffer(mut cb_builder) => {
debug_assert!(!partially_flushed);
assert!(!cb_builder.has_fence());
cb_builder.set_fence_signal(&fence);
cb_builder
.submit(&queue)
.map_err(|err| OutcomeErr::Full(err.into()))
},
SubmitAnyBuilder::BindSparse(mut sparse) => {
debug_assert!(!partially_flushed);
assert!(!sparse.has_fence());
sparse.set_fence_signal(&fence);
sparse
.submit(&queue)
.map_err(|err| OutcomeErr::Full(err.into()))
},
SubmitAnyBuilder::QueuePresent(present) => {
let intermediary_result = if partially_flushed {
Ok(())
} else {
present.submit(&queue)
};
match intermediary_result {
Ok(()) => {
let mut b = SubmitCommandBufferBuilder::new();
b.set_fence_signal(&fence);
b.submit(&queue).map_err(|err| OutcomeErr::Partial(err.into()))
},
Err(err) => {
Err(OutcomeErr::Full(err.into()))
},
}
},
};
match result {
Ok(()) => {
**state = FenceSignalFutureState::Flushed(previous, fence);
Ok(())
},
Err(OutcomeErr::Partial(err)) => {
**state = FenceSignalFutureState::PartiallyFlushed(previous, fence);
Err(err)
},
Err(OutcomeErr::Full(err)) => {
**state = FenceSignalFutureState::Pending(previous, fence);
Err(err)
},
}
}
}
}
impl<F> FenceSignalFutureState<F> {
#[inline]
fn get_prev(&self) -> Option<&F> {
match *self {
FenceSignalFutureState::Pending(ref prev, _) => Some(prev),
FenceSignalFutureState::PartiallyFlushed(ref prev, _) => Some(prev),
FenceSignalFutureState::Flushed(ref prev, _) => Some(prev),
FenceSignalFutureState::Cleaned => None,
FenceSignalFutureState::Poisoned => None,
}
}
}
unsafe impl<F> GpuFuture for FenceSignalFuture<F>
where F: GpuFuture
{
#[inline]
fn cleanup_finished(&mut self) {
self.cleanup_finished_impl()
}
#[inline]
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
let mut state = self.state.lock().unwrap();
self.flush_impl(&mut state)?;
match *state {
FenceSignalFutureState::Flushed(_, ref fence) => {
match self.behavior {
FenceSignalFutureBehavior::Block { timeout } => {
fence.wait(timeout)?;
},
FenceSignalFutureBehavior::Continue => (),
}
},
FenceSignalFutureState::Cleaned |
FenceSignalFutureState::Poisoned => (),
FenceSignalFutureState::Pending(_, _) => unreachable!(),
FenceSignalFutureState::PartiallyFlushed(_, _) => unreachable!(),
}
Ok(SubmitAnyBuilder::Empty)
}
#[inline]
fn flush(&self) -> Result<(), FlushError> {
let mut state = self.state.lock().unwrap();
self.flush_impl(&mut state)
}
#[inline]
unsafe fn signal_finished(&self) {
let state = self.state.lock().unwrap();
match *state {
FenceSignalFutureState::Flushed(ref prev, _) => {
prev.signal_finished();
},
FenceSignalFutureState::Cleaned |
FenceSignalFutureState::Poisoned => (),
_ => unreachable!(),
}
}
#[inline]
fn queue_change_allowed(&self) -> bool {
match self.behavior {
FenceSignalFutureBehavior::Continue => {
let state = self.state.lock().unwrap();
if state.get_prev().is_some() {
false
} else {
true
}
},
FenceSignalFutureBehavior::Block { .. } => {
true
},
}
}
#[inline]
fn queue(&self) -> Option<Arc<Queue>> {
let state = self.state.lock().unwrap();
if let Some(prev) = state.get_prev() {
prev.queue()
} else {
None
}
}
#[inline]
fn check_buffer_access(
&self, buffer: &dyn BufferAccess, exclusive: bool, queue: &Queue)
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError> {
let state = self.state.lock().unwrap();
if let Some(previous) = state.get_prev() {
previous.check_buffer_access(buffer, exclusive, queue)
} else {
Err(AccessCheckError::Unknown)
}
}
#[inline]
fn check_image_access(&self, image: &dyn ImageAccess, layout: ImageLayout, exclusive: bool,
queue: &Queue)
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError> {
let state = self.state.lock().unwrap();
if let Some(previous) = state.get_prev() {
previous.check_image_access(image, layout, exclusive, queue)
} else {
Err(AccessCheckError::Unknown)
}
}
}
unsafe impl<F> DeviceOwned for FenceSignalFuture<F>
where F: GpuFuture
{
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl<F> Drop for FenceSignalFuture<F>
where F: GpuFuture
{
fn drop(&mut self) {
let mut state = self.state.lock().unwrap();
let _ = self.flush_impl(&mut state);
match mem::replace(&mut *state, FenceSignalFutureState::Cleaned) {
FenceSignalFutureState::Flushed(previous, fence) => {
fence.wait(None).unwrap();
unsafe {
previous.signal_finished();
}
},
FenceSignalFutureState::Cleaned => {
},
FenceSignalFutureState::Poisoned => {
},
FenceSignalFutureState::Pending(_, _) |
FenceSignalFutureState::PartiallyFlushed(_, _) => {
},
}
}
}
unsafe impl<F> GpuFuture for Arc<FenceSignalFuture<F>>
where F: GpuFuture
{
#[inline]
fn cleanup_finished(&mut self) {
self.cleanup_finished_impl()
}
#[inline]
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
(**self).build_submission()
}
#[inline]
fn flush(&self) -> Result<(), FlushError> {
(**self).flush()
}
#[inline]
unsafe fn signal_finished(&self) {
(**self).signal_finished()
}
#[inline]
fn queue_change_allowed(&self) -> bool {
(**self).queue_change_allowed()
}
#[inline]
fn queue(&self) -> Option<Arc<Queue>> {
(**self).queue()
}
#[inline]
fn check_buffer_access(
&self, buffer: &dyn BufferAccess, exclusive: bool, queue: &Queue)
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError> {
(**self).check_buffer_access(buffer, exclusive, queue)
}
#[inline]
fn check_image_access(&self, image: &dyn ImageAccess, layout: ImageLayout, exclusive: bool,
queue: &Queue)
-> Result<Option<(PipelineStages, AccessFlagBits)>, AccessCheckError> {
(**self).check_image_access(image, layout, exclusive, queue)
}
}