aboutsummaryrefslogtreecommitdiff
path: root/src/sync/future/semaphore_signal.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sync/future/semaphore_signal.rs')
-rw-r--r--src/sync/future/semaphore_signal.rs198
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();
+ }
+ }
+ }
+}