diff options
Diffstat (limited to 'src/sync/future/semaphore_signal.rs')
-rw-r--r-- | src/sync/future/semaphore_signal.rs | 198 |
1 files changed, 198 insertions, 0 deletions
diff --git a/src/sync/future/semaphore_signal.rs b/src/sync/future/semaphore_signal.rs new file mode 100644 index 0000000..829860b --- /dev/null +++ b/src/sync/future/semaphore_signal.rs @@ -0,0 +1,198 @@ +// 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(); + } + } + } +} |