• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use std::mem;
11 use std::sync::Arc;
12 use std::sync::Mutex;
13 use std::sync::MutexGuard;
14 use std::time::Duration;
15 
16 use crate::buffer::BufferAccess;
17 use crate::command_buffer::submit::SubmitAnyBuilder;
18 use crate::command_buffer::submit::SubmitCommandBufferBuilder;
19 use crate::device::Device;
20 use crate::device::DeviceOwned;
21 use crate::device::Queue;
22 use crate::image::ImageAccess;
23 use crate::image::ImageLayout;
24 use crate::sync::AccessCheckError;
25 use crate::sync::AccessFlags;
26 use crate::sync::Fence;
27 use crate::sync::FlushError;
28 use crate::sync::GpuFuture;
29 use crate::sync::PipelineStages;
30 
31 /// Builds a new fence signal future.
32 #[inline]
then_signal_fence<F>(future: F, behavior: FenceSignalFutureBehavior) -> FenceSignalFuture<F> where F: GpuFuture,33 pub fn then_signal_fence<F>(future: F, behavior: FenceSignalFutureBehavior) -> FenceSignalFuture<F>
34 where
35     F: GpuFuture,
36 {
37     let device = future.device().clone();
38 
39     assert!(future.queue().is_some()); // TODO: document
40 
41     let fence = Fence::from_pool(device.clone()).unwrap();
42     FenceSignalFuture {
43         device: device,
44         state: Mutex::new(FenceSignalFutureState::Pending(future, fence)),
45         behavior: behavior,
46     }
47 }
48 
49 /// Describes the behavior of the future if you submit something after it.
50 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
51 pub enum FenceSignalFutureBehavior {
52     /// Continue execution on the same queue.
53     Continue,
54     /// Wait for the fence to be signalled before submitting any further operation.
55     Block {
56         /// How long to block the current thread.
57         timeout: Option<Duration>,
58     },
59 }
60 
61 /// Represents a fence being signaled after a previous event.
62 ///
63 /// Contrary to most other future types, it is possible to block the current thread until the event
64 /// happens. This is done by calling the `wait()` function.
65 ///
66 /// Also note that the `GpuFuture` trait is implemented on `Arc<FenceSignalFuture<_>>`.
67 /// This means that you can put this future in an `Arc` and keep a copy of it somewhere in order
68 /// to know when the execution reached that point.
69 ///
70 /// ```
71 /// use std::sync::Arc;
72 /// use vulkano::sync::GpuFuture;
73 ///
74 /// # let future: Box<GpuFuture> = return;
75 /// // Assuming you have a chain of operations, like this:
76 /// // let future = ...
77 /// //      .then_execute(foo)
78 /// //      .then_execute(bar)
79 ///
80 /// // You can signal a fence at this point of the chain, and put the future in an `Arc`.
81 /// let fence_signal = Arc::new(future.then_signal_fence());
82 ///
83 /// // And then continue the chain:
84 /// // fence_signal.clone()
85 /// //      .then_execute(baz)
86 /// //      .then_execute(qux)
87 ///
88 /// // Later you can wait until you reach the point of `fence_signal`:
89 /// fence_signal.wait(None).unwrap();
90 /// ```
91 #[must_use = "Dropping this object will immediately block the thread until the GPU has finished \
92               processing the submission"]
93 pub struct FenceSignalFuture<F>
94 where
95     F: GpuFuture,
96 {
97     // Current state. See the docs of `FenceSignalFutureState`.
98     state: Mutex<FenceSignalFutureState<F>>,
99     // The device of the future.
100     device: Arc<Device>,
101     behavior: FenceSignalFutureBehavior,
102 }
103 
104 // This future can be in three different states: pending (ie. newly-created), submitted (ie. the
105 // command that submits the fence has been submitted), or cleaned (ie. the previous future has
106 // been dropped).
107 enum FenceSignalFutureState<F> {
108     // Newly-created. Not submitted yet.
109     Pending(F, Fence),
110 
111     // Partially submitted to the queue. Only happens in situations where submitting requires two
112     // steps, and when the first step succeeded while the second step failed.
113     //
114     // Note that if there's ever a submit operation that needs three steps we will need to rework
115     // this code, as it was designed for two-step operations only.
116     PartiallyFlushed(F, Fence),
117 
118     // Submitted to the queue.
119     Flushed(F, Fence),
120 
121     // The submission is finished. The previous future and the fence have been cleaned.
122     Cleaned,
123 
124     // A function panicked while the state was being modified. Should never happen.
125     Poisoned,
126 }
127 
128 impl<F> FenceSignalFuture<F>
129 where
130     F: GpuFuture,
131 {
132     /// Blocks the current thread until the fence is signaled by the GPU. Performs a flush if
133     /// necessary.
134     ///
135     /// If `timeout` is `None`, then the wait is infinite. Otherwise the thread will unblock after
136     /// the specified timeout has elapsed and an error will be returned.
137     ///
138     /// If the wait is successful, this function also cleans any resource locked by previous
139     /// submissions.
wait(&self, timeout: Option<Duration>) -> Result<(), FlushError>140     pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FlushError> {
141         let mut state = self.state.lock().unwrap();
142 
143         self.flush_impl(&mut state)?;
144 
145         match mem::replace(&mut *state, FenceSignalFutureState::Cleaned) {
146             FenceSignalFutureState::Flushed(previous, fence) => {
147                 fence.wait(timeout)?;
148                 unsafe {
149                     previous.signal_finished();
150                 }
151                 Ok(())
152             }
153             FenceSignalFutureState::Cleaned => Ok(()),
154             _ => unreachable!(),
155         }
156     }
157 }
158 
159 impl<F> FenceSignalFuture<F>
160 where
161     F: GpuFuture,
162 {
163     // Implementation of `cleanup_finished`, but takes a `&self` instead of a `&mut self`.
164     // This is an external function so that we can also call it from an `Arc<FenceSignalFuture>`.
165     #[inline]
cleanup_finished_impl(&self)166     fn cleanup_finished_impl(&self) {
167         let mut state = self.state.lock().unwrap();
168 
169         match *state {
170             FenceSignalFutureState::Flushed(ref mut prev, ref fence) => {
171                 match fence.wait(Some(Duration::from_secs(0))) {
172                     Ok(()) => unsafe { prev.signal_finished() },
173                     Err(_) => {
174                         prev.cleanup_finished();
175                         return;
176                     }
177                 }
178             }
179             FenceSignalFutureState::Pending(ref mut prev, _) => {
180                 prev.cleanup_finished();
181                 return;
182             }
183             FenceSignalFutureState::PartiallyFlushed(ref mut prev, _) => {
184                 prev.cleanup_finished();
185                 return;
186             }
187             _ => return,
188         };
189 
190         // This code can only be reached if we're already flushed and waiting on the fence
191         // succeeded.
192         *state = FenceSignalFutureState::Cleaned;
193     }
194 
195     // Implementation of `flush`. You must lock the state and pass the mutex guard here.
flush_impl( &self, state: &mut MutexGuard<FenceSignalFutureState<F>>, ) -> Result<(), FlushError>196     fn flush_impl(
197         &self,
198         state: &mut MutexGuard<FenceSignalFutureState<F>>,
199     ) -> Result<(), FlushError> {
200         unsafe {
201             // In this function we temporarily replace the current state with `Poisoned` at the
202             // beginning, and we take care to always put back a value into `state` before
203             // returning (even in case of error).
204             let old_state = mem::replace(&mut **state, FenceSignalFutureState::Poisoned);
205 
206             let (previous, fence, partially_flushed) = match old_state {
207                 FenceSignalFutureState::Pending(prev, fence) => (prev, fence, false),
208                 FenceSignalFutureState::PartiallyFlushed(prev, fence) => (prev, fence, true),
209                 other => {
210                     // We were already flushed in the past, or we're already poisoned. Don't do
211                     // anything.
212                     **state = other;
213                     return Ok(());
214                 }
215             };
216 
217             // TODO: meh for unwrap
218             let queue = previous.queue().unwrap().clone();
219 
220             // There are three possible outcomes for the flush operation: success, partial success
221             // in which case `result` will contain `Err(OutcomeErr::Partial)`, or total failure
222             // in which case `result` will contain `Err(OutcomeErr::Full)`.
223             enum OutcomeErr<E> {
224                 Partial(E),
225                 Full(E),
226             }
227             let result = match previous.build_submission()? {
228                 SubmitAnyBuilder::Empty => {
229                     debug_assert!(!partially_flushed);
230                     let mut b = SubmitCommandBufferBuilder::new();
231                     b.set_fence_signal(&fence);
232                     b.submit(&queue).map_err(|err| OutcomeErr::Full(err.into()))
233                 }
234                 SubmitAnyBuilder::SemaphoresWait(sem) => {
235                     debug_assert!(!partially_flushed);
236                     let b: SubmitCommandBufferBuilder = sem.into();
237                     debug_assert!(!b.has_fence());
238                     b.submit(&queue).map_err(|err| OutcomeErr::Full(err.into()))
239                 }
240                 SubmitAnyBuilder::CommandBuffer(mut cb_builder) => {
241                     debug_assert!(!partially_flushed);
242                     // The assert below could technically be a debug assertion as it is part of the
243                     // safety contract of the trait. However it is easy to get this wrong if you
244                     // write a custom implementation, and if so the consequences would be
245                     // disastrous and hard to debug. Therefore we prefer to just use a regular
246                     // assertion.
247                     assert!(!cb_builder.has_fence());
248                     cb_builder.set_fence_signal(&fence);
249                     cb_builder
250                         .submit(&queue)
251                         .map_err(|err| OutcomeErr::Full(err.into()))
252                 }
253                 SubmitAnyBuilder::BindSparse(mut sparse) => {
254                     debug_assert!(!partially_flushed);
255                     // Same remark as `CommandBuffer`.
256                     assert!(!sparse.has_fence());
257                     sparse.set_fence_signal(&fence);
258                     sparse
259                         .submit(&queue)
260                         .map_err(|err| OutcomeErr::Full(err.into()))
261                 }
262                 SubmitAnyBuilder::QueuePresent(present) => {
263                     let intermediary_result = if partially_flushed {
264                         Ok(())
265                     } else {
266                         present.submit(&queue)
267                     };
268                     match intermediary_result {
269                         Ok(()) => {
270                             let mut b = SubmitCommandBufferBuilder::new();
271                             b.set_fence_signal(&fence);
272                             b.submit(&queue)
273                                 .map_err(|err| OutcomeErr::Partial(err.into()))
274                         }
275                         Err(err) => Err(OutcomeErr::Full(err.into())),
276                     }
277                 }
278             };
279 
280             // Restore the state before returning.
281             match result {
282                 Ok(()) => {
283                     **state = FenceSignalFutureState::Flushed(previous, fence);
284                     Ok(())
285                 }
286                 Err(OutcomeErr::Partial(err)) => {
287                     **state = FenceSignalFutureState::PartiallyFlushed(previous, fence);
288                     Err(err)
289                 }
290                 Err(OutcomeErr::Full(err)) => {
291                     **state = FenceSignalFutureState::Pending(previous, fence);
292                     Err(err)
293                 }
294             }
295         }
296     }
297 }
298 
299 impl<F> FenceSignalFutureState<F> {
300     #[inline]
get_prev(&self) -> Option<&F>301     fn get_prev(&self) -> Option<&F> {
302         match *self {
303             FenceSignalFutureState::Pending(ref prev, _) => Some(prev),
304             FenceSignalFutureState::PartiallyFlushed(ref prev, _) => Some(prev),
305             FenceSignalFutureState::Flushed(ref prev, _) => Some(prev),
306             FenceSignalFutureState::Cleaned => None,
307             FenceSignalFutureState::Poisoned => None,
308         }
309     }
310 }
311 
312 unsafe impl<F> GpuFuture for FenceSignalFuture<F>
313 where
314     F: GpuFuture,
315 {
316     #[inline]
cleanup_finished(&mut self)317     fn cleanup_finished(&mut self) {
318         self.cleanup_finished_impl()
319     }
320 
321     #[inline]
build_submission(&self) -> Result<SubmitAnyBuilder, FlushError>322     unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
323         let mut state = self.state.lock().unwrap();
324         self.flush_impl(&mut state)?;
325 
326         match *state {
327             FenceSignalFutureState::Flushed(_, ref fence) => match self.behavior {
328                 FenceSignalFutureBehavior::Block { timeout } => {
329                     fence.wait(timeout)?;
330                 }
331                 FenceSignalFutureBehavior::Continue => (),
332             },
333             FenceSignalFutureState::Cleaned | FenceSignalFutureState::Poisoned => (),
334             FenceSignalFutureState::Pending(_, _) => unreachable!(),
335             FenceSignalFutureState::PartiallyFlushed(_, _) => unreachable!(),
336         }
337 
338         Ok(SubmitAnyBuilder::Empty)
339     }
340 
341     #[inline]
flush(&self) -> Result<(), FlushError>342     fn flush(&self) -> Result<(), FlushError> {
343         let mut state = self.state.lock().unwrap();
344         self.flush_impl(&mut state)
345     }
346 
347     #[inline]
signal_finished(&self)348     unsafe fn signal_finished(&self) {
349         let state = self.state.lock().unwrap();
350         match *state {
351             FenceSignalFutureState::Flushed(ref prev, _) => {
352                 prev.signal_finished();
353             }
354             FenceSignalFutureState::Cleaned | FenceSignalFutureState::Poisoned => (),
355             _ => unreachable!(),
356         }
357     }
358 
359     #[inline]
queue_change_allowed(&self) -> bool360     fn queue_change_allowed(&self) -> bool {
361         match self.behavior {
362             FenceSignalFutureBehavior::Continue => {
363                 let state = self.state.lock().unwrap();
364                 if state.get_prev().is_some() {
365                     false
366                 } else {
367                     true
368                 }
369             }
370             FenceSignalFutureBehavior::Block { .. } => true,
371         }
372     }
373 
374     #[inline]
queue(&self) -> Option<Arc<Queue>>375     fn queue(&self) -> Option<Arc<Queue>> {
376         let state = self.state.lock().unwrap();
377         if let Some(prev) = state.get_prev() {
378             prev.queue()
379         } else {
380             None
381         }
382     }
383 
384     #[inline]
check_buffer_access( &self, buffer: &dyn BufferAccess, exclusive: bool, queue: &Queue, ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError>385     fn check_buffer_access(
386         &self,
387         buffer: &dyn BufferAccess,
388         exclusive: bool,
389         queue: &Queue,
390     ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
391         let state = self.state.lock().unwrap();
392         if let Some(previous) = state.get_prev() {
393             previous.check_buffer_access(buffer, exclusive, queue)
394         } else {
395             Err(AccessCheckError::Unknown)
396         }
397     }
398 
399     #[inline]
check_image_access( &self, image: &dyn ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue, ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError>400     fn check_image_access(
401         &self,
402         image: &dyn ImageAccess,
403         layout: ImageLayout,
404         exclusive: bool,
405         queue: &Queue,
406     ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
407         let state = self.state.lock().unwrap();
408         if let Some(previous) = state.get_prev() {
409             previous.check_image_access(image, layout, exclusive, queue)
410         } else {
411             Err(AccessCheckError::Unknown)
412         }
413     }
414 }
415 
416 unsafe impl<F> DeviceOwned for FenceSignalFuture<F>
417 where
418     F: GpuFuture,
419 {
420     #[inline]
device(&self) -> &Arc<Device>421     fn device(&self) -> &Arc<Device> {
422         &self.device
423     }
424 }
425 
426 impl<F> Drop for FenceSignalFuture<F>
427 where
428     F: GpuFuture,
429 {
drop(&mut self)430     fn drop(&mut self) {
431         let mut state = self.state.lock().unwrap();
432 
433         // We ignore any possible error while submitting for now. Problems are handled below.
434         let _ = self.flush_impl(&mut state);
435 
436         match mem::replace(&mut *state, FenceSignalFutureState::Cleaned) {
437             FenceSignalFutureState::Flushed(previous, fence) => {
438                 // This is a normal situation. Submitting worked.
439                 // TODO: handle errors?
440                 fence.wait(None).unwrap();
441                 unsafe {
442                     previous.signal_finished();
443                 }
444             }
445             FenceSignalFutureState::Cleaned => {
446                 // Also a normal situation. The user called `cleanup_finished()` before dropping.
447             }
448             FenceSignalFutureState::Poisoned => {
449                 // The previous future was already dropped and blocked the current queue.
450             }
451             FenceSignalFutureState::Pending(_, _)
452             | FenceSignalFutureState::PartiallyFlushed(_, _) => {
453                 // Flushing produced an error. There's nothing more we can do except drop the
454                 // previous future and let it block the current queue.
455             }
456         }
457     }
458 }
459 
460 unsafe impl<F> GpuFuture for Arc<FenceSignalFuture<F>>
461 where
462     F: GpuFuture,
463 {
464     #[inline]
cleanup_finished(&mut self)465     fn cleanup_finished(&mut self) {
466         self.cleanup_finished_impl()
467     }
468 
469     #[inline]
build_submission(&self) -> Result<SubmitAnyBuilder, FlushError>470     unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
471         // Note that this is sound because we always return `SubmitAnyBuilder::Empty`. See the
472         // documentation of `build_submission`.
473         (**self).build_submission()
474     }
475 
476     #[inline]
flush(&self) -> Result<(), FlushError>477     fn flush(&self) -> Result<(), FlushError> {
478         (**self).flush()
479     }
480 
481     #[inline]
signal_finished(&self)482     unsafe fn signal_finished(&self) {
483         (**self).signal_finished()
484     }
485 
486     #[inline]
queue_change_allowed(&self) -> bool487     fn queue_change_allowed(&self) -> bool {
488         (**self).queue_change_allowed()
489     }
490 
491     #[inline]
queue(&self) -> Option<Arc<Queue>>492     fn queue(&self) -> Option<Arc<Queue>> {
493         (**self).queue()
494     }
495 
496     #[inline]
check_buffer_access( &self, buffer: &dyn BufferAccess, exclusive: bool, queue: &Queue, ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError>497     fn check_buffer_access(
498         &self,
499         buffer: &dyn BufferAccess,
500         exclusive: bool,
501         queue: &Queue,
502     ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
503         (**self).check_buffer_access(buffer, exclusive, queue)
504     }
505 
506     #[inline]
check_image_access( &self, image: &dyn ImageAccess, layout: ImageLayout, exclusive: bool, queue: &Queue, ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError>507     fn check_image_access(
508         &self,
509         image: &dyn ImageAccess,
510         layout: ImageLayout,
511         exclusive: bool,
512         queue: &Queue,
513     ) -> Result<Option<(PipelineStages, AccessFlags)>, AccessCheckError> {
514         (**self).check_image_access(image, layout, exclusive, queue)
515     }
516 }
517