blob: 829860ba61dca928bebcf6e706883a7aac5ca43f [file] [log] [blame]
// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
use crate::buffer::BufferAccess;
use crate::command_buffer::submit::SubmitAnyBuilder;
use crate::command_buffer::submit::SubmitCommandBufferBuilder;
use crate::command_buffer::submit::SubmitSemaphoresWaitBuilder;
use crate::device::Device;
use crate::device::DeviceOwned;
use crate::device::Queue;
use crate::image::ImageAccess;
use crate::image::ImageLayout;
use crate::sync::AccessCheckError;
use crate::sync::AccessFlags;
use crate::sync::FlushError;
use crate::sync::GpuFuture;
use crate::sync::PipelineStages;
use crate::sync::Semaphore;
/// Builds a new semaphore signal future.
#[inline]
pub fn then_signal_semaphore<F>(future: F) -> SemaphoreSignalFuture<F>
where
F: GpuFuture,
{
let device = future.device().clone();
assert!(future.queue().is_some()); // TODO: document
SemaphoreSignalFuture {
previous: future,
semaphore: Semaphore::from_pool(device).unwrap(),
wait_submitted: Mutex::new(false),
finished: AtomicBool::new(false),
}
}
/// Represents a semaphore being signaled after a previous event.
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished \
processing the submission"]
pub struct SemaphoreSignalFuture<F>
where
F: GpuFuture,
{
previous: F,
semaphore: Semaphore,
// True if the signaling command has already been submitted.
// If flush is called multiple times, we want to block so that only one flushing is executed.
// Therefore we use a `Mutex<bool>` and not an `AtomicBool`.
wait_submitted: Mutex<bool>,
finished: AtomicBool,
}
unsafe impl<F> GpuFuture for SemaphoreSignalFuture<F>
where
F: GpuFuture,
{
#[inline]
fn cleanup_finished(&mut self) {
self.previous.cleanup_finished();
}
#[inline]
unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
// Flushing the signaling part, since it must always be submitted before the waiting part.
self.flush()?;
let mut sem = SubmitSemaphoresWaitBuilder::new();
sem.add_wait_semaphore(&self.semaphore);
Ok(SubmitAnyBuilder::SemaphoresWait(sem))
}
fn flush(&self) -> Result<(), FlushError> {
unsafe {
let mut wait_submitted = self.wait_submitted.lock().unwrap();
if *wait_submitted {
return Ok(());
}
let queue = self.previous.queue().unwrap().clone();
match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => {
let mut builder = SubmitCommandBufferBuilder::new();
builder.add_signal_semaphore(&self.semaphore);
builder.submit(&queue)?;
}
SubmitAnyBuilder::SemaphoresWait(sem) => {
let mut builder: SubmitCommandBufferBuilder = sem.into();
builder.add_signal_semaphore(&self.semaphore);
builder.submit(&queue)?;
}
SubmitAnyBuilder::CommandBuffer(mut builder) => {
debug_assert_eq!(builder.num_signal_semaphores(), 0);
builder.add_signal_semaphore(&self.semaphore);
builder.submit(&queue)?;
}
SubmitAnyBuilder::BindSparse(_) => {
unimplemented!() // TODO: how to do that?
/*debug_assert_eq!(builder.num_signal_semaphores(), 0);
builder.add_signal_semaphore(&self.semaphore);
builder.submit(&queue)?;*/
}
SubmitAnyBuilder::QueuePresent(present) => {
present.submit(&queue)?;
let mut builder = SubmitCommandBufferBuilder::new();
builder.add_signal_semaphore(&self.semaphore);
builder.submit(&queue)?; // FIXME: problematic because if we return an error and flush() is called again, then we'll submit the present twice
}
};
// Only write `true` here in order to try again next time if an error occurs.
*wait_submitted = true;
Ok(())
}
}
#[inline]
unsafe fn signal_finished(&self) {
debug_assert!(*self.wait_submitted.lock().unwrap());
self.finished.store(true, Ordering::SeqCst);
self.previous.signal_finished();
}
#[inline]
fn queue_change_allowed(&self) -> bool {
true
}
#[inline]
fn queue(&self) -> Option<Arc<Queue>> {
self.previous.queue()
}
#[inline]
fn check_buffer_access(
&self,
buffer: &dyn BufferAccess,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.previous
.check_buffer_access(buffer, exclusive, queue)
.map(|_| None)
}
#[inline]
fn check_image_access(
&self,
image: &dyn ImageAccess,
layout: ImageLayout,
exclusive: bool,
queue: &Queue,
) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
self.previous
.check_image_access(image, layout, exclusive, queue)
.map(|_| None)
}
}
unsafe impl<F> DeviceOwned for SemaphoreSignalFuture<F>
where
F: GpuFuture,
{
#[inline]
fn device(&self) -> &Arc<Device> {
self.semaphore.device()
}
}
impl<F> Drop for SemaphoreSignalFuture<F>
where
F: GpuFuture,
{
fn drop(&mut self) {
unsafe {
if !*self.finished.get_mut() {
// TODO: handle errors?
self.flush().unwrap();
// Block until the queue finished.
self.queue().unwrap().wait().unwrap();
self.previous.signal_finished();
}
}
}
}