use super::{AccessCheckError, GpuFuture};
use crate::{
buffer::Buffer,
command_buffer::{SemaphoreSubmitInfo, SubmitInfo},
device::{Device, DeviceOwned, Queue, QueueFlags},
image::{Image, ImageLayout},
swapchain::Swapchain,
sync::{
fence::Fence,
future::{AccessError, SubmitAnyBuilder},
PipelineStages,
},
DeviceSize, Validated, ValidationError, VulkanError,
};
use parking_lot::{Mutex, MutexGuard};
use std::{
future::Future,
mem::replace,
ops::Range,
pin::Pin,
sync::Arc,
task::{Context, Poll},
thread,
time::Duration,
};
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 = Arc::new(Fence::from_pool(device.clone()).unwrap());
FenceSignalFuture {
device,
state: Mutex::new(FenceSignalFutureState::Pending(future, fence)),
behavior,
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FenceSignalFutureBehavior {
Continue,
#[allow(dead_code)] 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, Arc<Fence>),
PartiallyFlushed(F, Arc<Fence>),
Flushed(F, Arc<Fence>),
Cleaned,
Poisoned,
}
impl<F> FenceSignalFuture<F>
where
F: GpuFuture,
{
pub fn is_signaled(&self) -> Result<bool, VulkanError> {
let state = self.state.lock();
match &*state {
FenceSignalFutureState::Pending(_, fence)
| FenceSignalFutureState::PartiallyFlushed(_, fence)
| FenceSignalFutureState::Flushed(_, fence) => fence.is_signaled(),
FenceSignalFutureState::Cleaned => Ok(true),
FenceSignalFutureState::Poisoned => unreachable!(),
}
}
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock();
self.flush_impl(&mut state)?;
match 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,
{
fn cleanup_finished_impl(&self) {
let mut state = self.state.lock();
match *state {
FenceSignalFutureState::Flushed(ref mut prev, ref fence) => {
if fence.wait(Some(Duration::from_secs(0))).is_ok() {
unsafe { prev.signal_finished() }
*state = FenceSignalFutureState::Cleaned;
} else {
prev.cleanup_finished();
}
}
FenceSignalFutureState::Pending(ref mut prev, _) => {
prev.cleanup_finished();
}
FenceSignalFutureState::PartiallyFlushed(ref mut prev, _) => {
prev.cleanup_finished();
}
_ => (),
}
}
fn flush_impl(
&self,
state: &mut MutexGuard<'_, FenceSignalFutureState<F>>,
) -> Result<(), Validated<VulkanError>> {
unsafe {
let old_state = replace(&mut **state, FenceSignalFutureState::Poisoned);
let (previous, new_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();
enum OutcomeErr<E> {
Partial(E),
Full(E),
}
let result = match previous.build_submission()? {
SubmitAnyBuilder::Empty => {
debug_assert!(!partially_flushed);
queue
.with(|mut q| {
q.submit_unchecked([Default::default()], Some(new_fence.clone()))
})
.map_err(|err| OutcomeErr::Full(err.into()))
}
SubmitAnyBuilder::SemaphoresWait(semaphores) => {
debug_assert!(!partially_flushed);
queue
.with(|mut q| {
q.submit_unchecked(
[SubmitInfo {
wait_semaphores: semaphores
.into_iter()
.map(|semaphore| {
SemaphoreSubmitInfo {
stages: PipelineStages::ALL_COMMANDS,
..SemaphoreSubmitInfo::semaphore(semaphore)
}
})
.collect(),
..Default::default()
}],
None,
)
})
.map_err(|err| OutcomeErr::Full(err.into()))
}
SubmitAnyBuilder::CommandBuffer(submit_info, fence) => {
debug_assert!(!partially_flushed);
assert!(fence.is_none());
queue
.with(|mut q| {
q.submit_with_future(
submit_info,
Some(new_fence.clone()),
&previous,
&queue,
)
})
.map_err(OutcomeErr::Full)
}
SubmitAnyBuilder::BindSparse(bind_infos, fence) => {
debug_assert!(!partially_flushed);
assert!(fence.is_none());
debug_assert!(queue.device().physical_device().queue_family_properties()
[queue.queue_family_index() as usize]
.queue_flags
.intersects(QueueFlags::SPARSE_BINDING));
queue
.with(|mut q| q.bind_sparse_unchecked(bind_infos, Some(new_fence.clone())))
.map_err(|err| OutcomeErr::Full(err.into()))
}
SubmitAnyBuilder::QueuePresent(present_info) => {
if partially_flushed {
queue
.with(|mut q| {
q.submit_unchecked([Default::default()], Some(new_fence.clone()))
})
.map_err(|err| OutcomeErr::Partial(err.into()))
} else {
for swapchain_info in &present_info.swapchain_infos {
if swapchain_info.present_id.map_or(false, |present_id| {
!swapchain_info.swapchain.try_claim_present_id(present_id)
}) {
return Err(Box::new(ValidationError {
problem: "the provided `present_id` was not greater than any \
`present_id` passed previously for the same swapchain"
.into(),
vuids: &["VUID-VkPresentIdKHR-presentIds-04999"],
..Default::default()
})
.into());
}
match previous.check_swapchain_image_acquired(
&swapchain_info.swapchain,
swapchain_info.image_index,
true,
) {
Ok(_) => (),
Err(AccessCheckError::Unknown) => {
return Err(Box::new(ValidationError::from_error(
AccessError::SwapchainImageNotAcquired,
))
.into());
}
Err(AccessCheckError::Denied(err)) => {
return Err(Box::new(ValidationError::from_error(err)).into());
}
}
}
let intermediary_result = queue
.with(|mut q| q.present_unchecked(present_info))?
.map(|r| r.map(|_| ()))
.fold(Ok(()), Result::and);
match intermediary_result {
Ok(()) => queue
.with(|mut q| {
q.submit_unchecked(
[Default::default()],
Some(new_fence.clone()),
)
})
.map_err(|err| OutcomeErr::Partial(err.into())),
Err(err) => Err(OutcomeErr::Full(err.into())),
}
}
}
};
match result {
Ok(()) => {
**state = FenceSignalFutureState::Flushed(previous, new_fence);
Ok(())
}
Err(OutcomeErr::Partial(err)) => {
**state = FenceSignalFutureState::PartiallyFlushed(previous, new_fence);
Err(err)
}
Err(OutcomeErr::Full(err)) => {
**state = FenceSignalFutureState::Pending(previous, new_fence);
Err(err)
}
}
}
}
}
impl<F> Future for FenceSignalFuture<F>
where
F: GpuFuture,
{
type Output = Result<(), VulkanError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let state = self.state.lock();
match &*state {
FenceSignalFutureState::Pending(_, fence)
| FenceSignalFutureState::PartiallyFlushed(_, fence)
| FenceSignalFutureState::Flushed(_, fence) => fence.poll_impl(cx),
FenceSignalFutureState::Cleaned => Poll::Ready(Ok(())),
FenceSignalFutureState::Poisoned => unreachable!(),
}
}
}
impl<F> FenceSignalFutureState<F> {
fn get_prev(&self) -> Option<&F> {
match self {
FenceSignalFutureState::Pending(prev, _) => Some(prev),
FenceSignalFutureState::PartiallyFlushed(prev, _) => Some(prev),
FenceSignalFutureState::Flushed(prev, _) => Some(prev),
FenceSignalFutureState::Cleaned => None,
FenceSignalFutureState::Poisoned => None,
}
}
}
unsafe impl<F> GpuFuture for FenceSignalFuture<F>
where
F: GpuFuture,
{
fn cleanup_finished(&mut self) {
self.cleanup_finished_impl()
}
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Validated<VulkanError>> {
let mut state = self.state.lock();
self.flush_impl(&mut state)?;
match &*state {
FenceSignalFutureState::Flushed(_, 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)
}
fn flush(&self) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock();
self.flush_impl(&mut state)
}
unsafe fn signal_finished(&self) {
let state = self.state.lock();
match *state {
FenceSignalFutureState::Flushed(ref prev, _) => {
prev.signal_finished();
}
FenceSignalFutureState::Cleaned | FenceSignalFutureState::Poisoned => (),
_ => unreachable!(),
}
}
fn queue_change_allowed(&self) -> bool {
match self.behavior {
FenceSignalFutureBehavior::Continue => {
let state = self.state.lock();
state.get_prev().is_none()
}
FenceSignalFutureBehavior::Block { .. } => true,
}
}
fn queue(&self) -> Option<Arc<Queue>> {
let state = self.state.lock();
if let Some(prev) = state.get_prev() {
prev.queue()
} else {
None
}
}
fn check_buffer_access(
&self,
buffer: &Buffer,
range: Range<DeviceSize>,
exclusive: bool,
queue: &Queue,
) -> Result<(), AccessCheckError> {
let state = self.state.lock();
if let Some(previous) = state.get_prev() {
previous.check_buffer_access(buffer, range, exclusive, queue)
} else {
Err(AccessCheckError::Unknown)
}
}
fn check_image_access(
&self,
image: &Image,
range: Range<DeviceSize>,
exclusive: bool,
expected_layout: ImageLayout,
queue: &Queue,
) -> Result<(), AccessCheckError> {
let state = self.state.lock();
if let Some(previous) = state.get_prev() {
previous.check_image_access(image, range, exclusive, expected_layout, queue)
} else {
Err(AccessCheckError::Unknown)
}
}
#[inline]
fn check_swapchain_image_acquired(
&self,
swapchain: &Swapchain,
image_index: u32,
_before: bool,
) -> Result<(), AccessCheckError> {
if let Some(previous) = self.state.lock().get_prev() {
previous.check_swapchain_image_acquired(swapchain, image_index, false)
} else {
Err(AccessCheckError::Unknown)
}
}
}
unsafe impl<F> DeviceOwned for FenceSignalFuture<F>
where
F: GpuFuture,
{
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl<F> Drop for FenceSignalFuture<F>
where
F: GpuFuture,
{
fn drop(&mut self) {
if thread::panicking() {
return;
}
let mut state = self.state.lock();
let _ = self.flush_impl(&mut state);
match 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,
{
fn cleanup_finished(&mut self) {
self.cleanup_finished_impl()
}
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Validated<VulkanError>> {
(**self).build_submission()
}
fn flush(&self) -> Result<(), Validated<VulkanError>> {
(**self).flush()
}
unsafe fn signal_finished(&self) {
(**self).signal_finished()
}
fn queue_change_allowed(&self) -> bool {
(**self).queue_change_allowed()
}
fn queue(&self) -> Option<Arc<Queue>> {
(**self).queue()
}
fn check_buffer_access(
&self,
buffer: &Buffer,
range: Range<DeviceSize>,
exclusive: bool,
queue: &Queue,
) -> Result<(), AccessCheckError> {
(**self).check_buffer_access(buffer, range, exclusive, queue)
}
fn check_image_access(
&self,
image: &Image,
range: Range<DeviceSize>,
exclusive: bool,
expected_layout: ImageLayout,
queue: &Queue,
) -> Result<(), AccessCheckError> {
(**self).check_image_access(image, range, exclusive, expected_layout, queue)
}
#[inline]
fn check_swapchain_image_acquired(
&self,
swapchain: &Swapchain,
image_index: u32,
before: bool,
) -> Result<(), AccessCheckError> {
(**self).check_swapchain_image_acquired(swapchain, image_index, before)
}
}