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 super::{AccessCheckError, FlushError, GpuFuture};
11 use crate::{
12 buffer::Buffer,
13 command_buffer::{SemaphoreSubmitInfo, SubmitInfo},
14 device::{Device, DeviceOwned, Queue, QueueFlags},
15 image::{sys::Image, ImageLayout},
16 swapchain::Swapchain,
17 sync::{
18 fence::Fence,
19 future::{AccessError, SubmitAnyBuilder},
20 PipelineStages,
21 },
22 DeviceSize, OomError,
23 };
24 use parking_lot::{Mutex, MutexGuard};
25 use std::{
26 future::Future,
27 mem::replace,
28 ops::Range,
29 pin::Pin,
30 sync::Arc,
31 task::{Context, Poll},
32 thread,
33 time::Duration,
34 };
35
36 /// Builds a new fence signal future.
then_signal_fence<F>(future: F, behavior: FenceSignalFutureBehavior) -> FenceSignalFuture<F> where F: GpuFuture,37 pub fn then_signal_fence<F>(future: F, behavior: FenceSignalFutureBehavior) -> FenceSignalFuture<F>
38 where
39 F: GpuFuture,
40 {
41 let device = future.device().clone();
42
43 assert!(future.queue().is_some()); // TODO: document
44
45 let fence = Arc::new(Fence::from_pool(device.clone()).unwrap());
46 FenceSignalFuture {
47 device,
48 state: Mutex::new(FenceSignalFutureState::Pending(future, fence)),
49 behavior,
50 }
51 }
52
53 /// Describes the behavior of the future if you submit something after it.
54 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
55 pub enum FenceSignalFutureBehavior {
56 /// Continue execution on the same queue.
57 Continue,
58 /// Wait for the fence to be signalled before submitting any further operation.
59 #[allow(dead_code)] // TODO: why is this never constructed?
60 Block {
61 /// How long to block the current thread.
62 timeout: Option<Duration>,
63 },
64 }
65
66 /// Represents a fence being signaled after a previous event.
67 ///
68 /// Contrary to most other future types, it is possible to block the current thread until the event
69 /// happens. This is done by calling the `wait()` function.
70 ///
71 /// This can also be done through Rust's Async system by simply `.await`ing this object. Note though
72 /// that (due to the Vulkan API fence design) this will spin to check the fence, rather than
73 /// blocking in the driver. Therefore if you have a long-running task, blocking may be less
74 /// CPU intense (depending on the driver's implementation).
75 ///
76 /// Also note that the `GpuFuture` trait is implemented on `Arc<FenceSignalFuture<_>>`.
77 /// This means that you can put this future in an `Arc` and keep a copy of it somewhere in order
78 /// to know when the execution reached that point.
79 ///
80 /// ```
81 /// use std::sync::Arc;
82 /// use vulkano::sync::GpuFuture;
83 ///
84 /// # let future: Box<dyn GpuFuture> = return;
85 /// // Assuming you have a chain of operations, like this:
86 /// // let future = ...
87 /// // .then_execute(foo)
88 /// // .then_execute(bar)
89 ///
90 /// // You can signal a fence at this point of the chain, and put the future in an `Arc`.
91 /// let fence_signal = Arc::new(future.then_signal_fence());
92 ///
93 /// // And then continue the chain:
94 /// // fence_signal.clone()
95 /// // .then_execute(baz)
96 /// // .then_execute(qux)
97 ///
98 /// // Later you can wait until you reach the point of `fence_signal`:
99 /// fence_signal.wait(None).unwrap();
100 /// ```
101 #[must_use = "Dropping this object will immediately block the thread until the GPU has finished \
102 processing the submission"]
103 pub struct FenceSignalFuture<F>
104 where
105 F: GpuFuture,
106 {
107 // Current state. See the docs of `FenceSignalFutureState`.
108 state: Mutex<FenceSignalFutureState<F>>,
109 // The device of the future.
110 device: Arc<Device>,
111 behavior: FenceSignalFutureBehavior,
112 }
113
114 // This future can be in three different states: pending (ie. newly-created), submitted (ie. the
115 // command that submits the fence has been submitted), or cleaned (ie. the previous future has
116 // been dropped).
117 enum FenceSignalFutureState<F> {
118 // Newly-created. Not submitted yet.
119 Pending(F, Arc<Fence>),
120
121 // Partially submitted to the queue. Only happens in situations where submitting requires two
122 // steps, and when the first step succeeded while the second step failed.
123 //
124 // Note that if there's ever a submit operation that needs three steps we will need to rework
125 // this code, as it was designed for two-step operations only.
126 PartiallyFlushed(F, Arc<Fence>),
127
128 // Submitted to the queue.
129 Flushed(F, Arc<Fence>),
130
131 // The submission is finished. The previous future and the fence have been cleaned.
132 Cleaned,
133
134 // A function panicked while the state was being modified. Should never happen.
135 Poisoned,
136 }
137
138 impl<F> FenceSignalFuture<F>
139 where
140 F: GpuFuture,
141 {
142 /// Returns true if the fence is signaled by the GPU.
is_signaled(&self) -> Result<bool, OomError>143 pub fn is_signaled(&self) -> Result<bool, OomError> {
144 let state = self.state.lock();
145
146 match &*state {
147 FenceSignalFutureState::Pending(_, fence)
148 | FenceSignalFutureState::PartiallyFlushed(_, fence)
149 | FenceSignalFutureState::Flushed(_, fence) => fence.is_signaled(),
150 FenceSignalFutureState::Cleaned => Ok(true),
151 FenceSignalFutureState::Poisoned => unreachable!(),
152 }
153 }
154
155 /// Blocks the current thread until the fence is signaled by the GPU. Performs a flush if
156 /// necessary.
157 ///
158 /// If `timeout` is `None`, then the wait is infinite. Otherwise the thread will unblock after
159 /// the specified timeout has elapsed and an error will be returned.
160 ///
161 /// If the wait is successful, this function also cleans any resource locked by previous
162 /// submissions.
wait(&self, timeout: Option<Duration>) -> Result<(), FlushError>163 pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FlushError> {
164 let mut state = self.state.lock();
165
166 self.flush_impl(&mut state)?;
167
168 match replace(&mut *state, FenceSignalFutureState::Cleaned) {
169 FenceSignalFutureState::Flushed(previous, fence) => {
170 fence.wait(timeout)?;
171 unsafe {
172 previous.signal_finished();
173 }
174 Ok(())
175 }
176 FenceSignalFutureState::Cleaned => Ok(()),
177 _ => unreachable!(),
178 }
179 }
180 }
181
182 impl<F> FenceSignalFuture<F>
183 where
184 F: GpuFuture,
185 {
186 // Implementation of `cleanup_finished`, but takes a `&self` instead of a `&mut self`.
187 // This is an external function so that we can also call it from an `Arc<FenceSignalFuture>`.
cleanup_finished_impl(&self)188 fn cleanup_finished_impl(&self) {
189 let mut state = self.state.lock();
190
191 match *state {
192 FenceSignalFutureState::Flushed(ref mut prev, ref fence) => {
193 match fence.wait(Some(Duration::from_secs(0))) {
194 Ok(()) => {
195 unsafe { prev.signal_finished() }
196 *state = FenceSignalFutureState::Cleaned;
197 }
198 Err(_) => {
199 prev.cleanup_finished();
200 }
201 }
202 }
203 FenceSignalFutureState::Pending(ref mut prev, _) => {
204 prev.cleanup_finished();
205 }
206 FenceSignalFutureState::PartiallyFlushed(ref mut prev, _) => {
207 prev.cleanup_finished();
208 }
209 _ => (),
210 }
211 }
212
213 // Implementation of `flush`. You must lock the state and pass the mutex guard here.
flush_impl( &self, state: &mut MutexGuard<'_, FenceSignalFutureState<F>>, ) -> Result<(), FlushError>214 fn flush_impl(
215 &self,
216 state: &mut MutexGuard<'_, FenceSignalFutureState<F>>,
217 ) -> Result<(), FlushError> {
218 unsafe {
219 // In this function we temporarily replace the current state with `Poisoned` at the
220 // beginning, and we take care to always put back a value into `state` before
221 // returning (even in case of error).
222 let old_state = replace(&mut **state, FenceSignalFutureState::Poisoned);
223
224 let (previous, new_fence, partially_flushed) = match old_state {
225 FenceSignalFutureState::Pending(prev, fence) => (prev, fence, false),
226 FenceSignalFutureState::PartiallyFlushed(prev, fence) => (prev, fence, true),
227 other => {
228 // We were already flushed in the past, or we're already poisoned. Don't do
229 // anything.
230 **state = other;
231 return Ok(());
232 }
233 };
234
235 // TODO: meh for unwrap
236 let queue = previous.queue().unwrap();
237
238 // There are three possible outcomes for the flush operation: success, partial success
239 // in which case `result` will contain `Err(OutcomeErr::Partial)`, or total failure
240 // in which case `result` will contain `Err(OutcomeErr::Full)`.
241 enum OutcomeErr<E> {
242 Partial(E),
243 Full(E),
244 }
245 let result = match previous.build_submission()? {
246 SubmitAnyBuilder::Empty => {
247 debug_assert!(!partially_flushed);
248
249 queue
250 .with(|mut q| {
251 q.submit_unchecked([Default::default()], Some(new_fence.clone()))
252 })
253 .map_err(|err| OutcomeErr::Full(err.into()))
254 }
255 SubmitAnyBuilder::SemaphoresWait(semaphores) => {
256 debug_assert!(!partially_flushed);
257
258 queue
259 .with(|mut q| {
260 q.submit_unchecked(
261 [SubmitInfo {
262 wait_semaphores: semaphores
263 .into_iter()
264 .map(|semaphore| {
265 SemaphoreSubmitInfo {
266 // TODO: correct stages ; hard
267 stages: PipelineStages::ALL_COMMANDS,
268 ..SemaphoreSubmitInfo::semaphore(semaphore)
269 }
270 })
271 .collect(),
272 ..Default::default()
273 }],
274 None,
275 )
276 })
277 .map_err(|err| OutcomeErr::Full(err.into()))
278 }
279 SubmitAnyBuilder::CommandBuffer(submit_info, fence) => {
280 debug_assert!(!partially_flushed);
281 // The assert below could technically be a debug assertion as it is part of the
282 // safety contract of the trait. However it is easy to get this wrong if you
283 // write a custom implementation, and if so the consequences would be
284 // disastrous and hard to debug. Therefore we prefer to just use a regular
285 // assertion.
286 assert!(fence.is_none());
287
288 queue
289 .with(|mut q| {
290 q.submit_with_future(
291 submit_info,
292 Some(new_fence.clone()),
293 &previous,
294 &queue,
295 )
296 })
297 .map_err(OutcomeErr::Full)
298 }
299 SubmitAnyBuilder::BindSparse(bind_infos, fence) => {
300 debug_assert!(!partially_flushed);
301 // Same remark as `CommandBuffer`.
302 assert!(fence.is_none());
303 debug_assert!(queue.device().physical_device().queue_family_properties()
304 [queue.queue_family_index() as usize]
305 .queue_flags
306 .intersects(QueueFlags::SPARSE_BINDING));
307
308 queue
309 .with(|mut q| q.bind_sparse_unchecked(bind_infos, Some(new_fence.clone())))
310 .map_err(|err| OutcomeErr::Full(err.into()))
311 }
312 SubmitAnyBuilder::QueuePresent(present_info) => {
313 let intermediary_result = if partially_flushed {
314 Ok(())
315 } else {
316 // VUID-VkPresentIdKHR-presentIds-04999
317 for swapchain_info in &present_info.swapchain_infos {
318 if swapchain_info.present_id.map_or(false, |present_id| {
319 !swapchain_info.swapchain.try_claim_present_id(present_id)
320 }) {
321 return Err(FlushError::PresentIdLessThanOrEqual);
322 }
323
324 match previous.check_swapchain_image_acquired(
325 &swapchain_info.swapchain,
326 swapchain_info.image_index,
327 true,
328 ) {
329 Ok(_) => (),
330 Err(AccessCheckError::Unknown) => {
331 return Err(AccessError::SwapchainImageNotAcquired.into())
332 }
333 Err(AccessCheckError::Denied(e)) => return Err(e.into()),
334 }
335 }
336
337 queue
338 .with(|mut q| q.present_unchecked(present_info))?
339 .map(|r| r.map(|_| ()))
340 .fold(Ok(()), Result::and)
341 };
342
343 match intermediary_result {
344 Ok(()) => queue
345 .with(|mut q| {
346 q.submit_unchecked([Default::default()], Some(new_fence.clone()))
347 })
348 .map_err(|err| OutcomeErr::Partial(err.into())),
349 Err(err) => Err(OutcomeErr::Full(err.into())),
350 }
351 }
352 };
353
354 // Restore the state before returning.
355 match result {
356 Ok(()) => {
357 **state = FenceSignalFutureState::Flushed(previous, new_fence);
358 Ok(())
359 }
360 Err(OutcomeErr::Partial(err)) => {
361 **state = FenceSignalFutureState::PartiallyFlushed(previous, new_fence);
362 Err(err)
363 }
364 Err(OutcomeErr::Full(err)) => {
365 **state = FenceSignalFutureState::Pending(previous, new_fence);
366 Err(err)
367 }
368 }
369 }
370 }
371 }
372
373 impl<F> Future for FenceSignalFuture<F>
374 where
375 F: GpuFuture,
376 {
377 type Output = Result<(), OomError>;
378
poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>379 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
380 // Implement through fence
381 let state = self.state.lock();
382
383 match &*state {
384 FenceSignalFutureState::Pending(_, fence)
385 | FenceSignalFutureState::PartiallyFlushed(_, fence)
386 | FenceSignalFutureState::Flushed(_, fence) => fence.poll_impl(cx),
387 FenceSignalFutureState::Cleaned => Poll::Ready(Ok(())),
388 FenceSignalFutureState::Poisoned => unreachable!(),
389 }
390 }
391 }
392
393 impl<F> FenceSignalFutureState<F> {
get_prev(&self) -> Option<&F>394 fn get_prev(&self) -> Option<&F> {
395 match self {
396 FenceSignalFutureState::Pending(prev, _) => Some(prev),
397 FenceSignalFutureState::PartiallyFlushed(prev, _) => Some(prev),
398 FenceSignalFutureState::Flushed(prev, _) => Some(prev),
399 FenceSignalFutureState::Cleaned => None,
400 FenceSignalFutureState::Poisoned => None,
401 }
402 }
403 }
404
405 unsafe impl<F> GpuFuture for FenceSignalFuture<F>
406 where
407 F: GpuFuture,
408 {
cleanup_finished(&mut self)409 fn cleanup_finished(&mut self) {
410 self.cleanup_finished_impl()
411 }
412
build_submission(&self) -> Result<SubmitAnyBuilder, FlushError>413 unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
414 let mut state = self.state.lock();
415 self.flush_impl(&mut state)?;
416
417 match &*state {
418 FenceSignalFutureState::Flushed(_, fence) => match self.behavior {
419 FenceSignalFutureBehavior::Block { timeout } => {
420 fence.wait(timeout)?;
421 }
422 FenceSignalFutureBehavior::Continue => (),
423 },
424 FenceSignalFutureState::Cleaned | FenceSignalFutureState::Poisoned => (),
425 FenceSignalFutureState::Pending(_, _) => unreachable!(),
426 FenceSignalFutureState::PartiallyFlushed(_, _) => unreachable!(),
427 }
428
429 Ok(SubmitAnyBuilder::Empty)
430 }
431
flush(&self) -> Result<(), FlushError>432 fn flush(&self) -> Result<(), FlushError> {
433 let mut state = self.state.lock();
434 self.flush_impl(&mut state)
435 }
436
signal_finished(&self)437 unsafe fn signal_finished(&self) {
438 let state = self.state.lock();
439 match *state {
440 FenceSignalFutureState::Flushed(ref prev, _) => {
441 prev.signal_finished();
442 }
443 FenceSignalFutureState::Cleaned | FenceSignalFutureState::Poisoned => (),
444 _ => unreachable!(),
445 }
446 }
447
queue_change_allowed(&self) -> bool448 fn queue_change_allowed(&self) -> bool {
449 match self.behavior {
450 FenceSignalFutureBehavior::Continue => {
451 let state = self.state.lock();
452 state.get_prev().is_none()
453 }
454 FenceSignalFutureBehavior::Block { .. } => true,
455 }
456 }
457
queue(&self) -> Option<Arc<Queue>>458 fn queue(&self) -> Option<Arc<Queue>> {
459 let state = self.state.lock();
460 if let Some(prev) = state.get_prev() {
461 prev.queue()
462 } else {
463 None
464 }
465 }
466
check_buffer_access( &self, buffer: &Buffer, range: Range<DeviceSize>, exclusive: bool, queue: &Queue, ) -> Result<(), AccessCheckError>467 fn check_buffer_access(
468 &self,
469 buffer: &Buffer,
470 range: Range<DeviceSize>,
471 exclusive: bool,
472 queue: &Queue,
473 ) -> Result<(), AccessCheckError> {
474 let state = self.state.lock();
475 if let Some(previous) = state.get_prev() {
476 previous.check_buffer_access(buffer, range, exclusive, queue)
477 } else {
478 Err(AccessCheckError::Unknown)
479 }
480 }
481
check_image_access( &self, image: &Image, range: Range<DeviceSize>, exclusive: bool, expected_layout: ImageLayout, queue: &Queue, ) -> Result<(), AccessCheckError>482 fn check_image_access(
483 &self,
484 image: &Image,
485 range: Range<DeviceSize>,
486 exclusive: bool,
487 expected_layout: ImageLayout,
488 queue: &Queue,
489 ) -> Result<(), AccessCheckError> {
490 let state = self.state.lock();
491 if let Some(previous) = state.get_prev() {
492 previous.check_image_access(image, range, exclusive, expected_layout, queue)
493 } else {
494 Err(AccessCheckError::Unknown)
495 }
496 }
497
498 #[inline]
check_swapchain_image_acquired( &self, swapchain: &Swapchain, image_index: u32, _before: bool, ) -> Result<(), AccessCheckError>499 fn check_swapchain_image_acquired(
500 &self,
501 swapchain: &Swapchain,
502 image_index: u32,
503 _before: bool,
504 ) -> Result<(), AccessCheckError> {
505 if let Some(previous) = self.state.lock().get_prev() {
506 previous.check_swapchain_image_acquired(swapchain, image_index, false)
507 } else {
508 Err(AccessCheckError::Unknown)
509 }
510 }
511 }
512
513 unsafe impl<F> DeviceOwned for FenceSignalFuture<F>
514 where
515 F: GpuFuture,
516 {
device(&self) -> &Arc<Device>517 fn device(&self) -> &Arc<Device> {
518 &self.device
519 }
520 }
521
522 impl<F> Drop for FenceSignalFuture<F>
523 where
524 F: GpuFuture,
525 {
drop(&mut self)526 fn drop(&mut self) {
527 if thread::panicking() {
528 return;
529 }
530
531 let mut state = self.state.lock();
532
533 // We ignore any possible error while submitting for now. Problems are handled below.
534 let _ = self.flush_impl(&mut state);
535
536 match replace(&mut *state, FenceSignalFutureState::Cleaned) {
537 FenceSignalFutureState::Flushed(previous, fence) => {
538 // This is a normal situation. Submitting worked.
539 // TODO: handle errors?
540 fence.wait(None).unwrap();
541 unsafe {
542 previous.signal_finished();
543 }
544 }
545 FenceSignalFutureState::Cleaned => {
546 // Also a normal situation. The user called `cleanup_finished()` before dropping.
547 }
548 FenceSignalFutureState::Poisoned => {
549 // The previous future was already dropped and blocked the current queue.
550 }
551 FenceSignalFutureState::Pending(_, _)
552 | FenceSignalFutureState::PartiallyFlushed(_, _) => {
553 // Flushing produced an error. There's nothing more we can do except drop the
554 // previous future and let it block the current queue.
555 }
556 }
557 }
558 }
559
560 unsafe impl<F> GpuFuture for Arc<FenceSignalFuture<F>>
561 where
562 F: GpuFuture,
563 {
cleanup_finished(&mut self)564 fn cleanup_finished(&mut self) {
565 self.cleanup_finished_impl()
566 }
567
build_submission(&self) -> Result<SubmitAnyBuilder, FlushError>568 unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, FlushError> {
569 // Note that this is sound because we always return `SubmitAnyBuilder::Empty`. See the
570 // documentation of `build_submission`.
571 (**self).build_submission()
572 }
573
flush(&self) -> Result<(), FlushError>574 fn flush(&self) -> Result<(), FlushError> {
575 (**self).flush()
576 }
577
signal_finished(&self)578 unsafe fn signal_finished(&self) {
579 (**self).signal_finished()
580 }
581
queue_change_allowed(&self) -> bool582 fn queue_change_allowed(&self) -> bool {
583 (**self).queue_change_allowed()
584 }
585
queue(&self) -> Option<Arc<Queue>>586 fn queue(&self) -> Option<Arc<Queue>> {
587 (**self).queue()
588 }
589
check_buffer_access( &self, buffer: &Buffer, range: Range<DeviceSize>, exclusive: bool, queue: &Queue, ) -> Result<(), AccessCheckError>590 fn check_buffer_access(
591 &self,
592 buffer: &Buffer,
593 range: Range<DeviceSize>,
594 exclusive: bool,
595 queue: &Queue,
596 ) -> Result<(), AccessCheckError> {
597 (**self).check_buffer_access(buffer, range, exclusive, queue)
598 }
599
check_image_access( &self, image: &Image, range: Range<DeviceSize>, exclusive: bool, expected_layout: ImageLayout, queue: &Queue, ) -> Result<(), AccessCheckError>600 fn check_image_access(
601 &self,
602 image: &Image,
603 range: Range<DeviceSize>,
604 exclusive: bool,
605 expected_layout: ImageLayout,
606 queue: &Queue,
607 ) -> Result<(), AccessCheckError> {
608 (**self).check_image_access(image, range, exclusive, expected_layout, queue)
609 }
610
611 #[inline]
check_swapchain_image_acquired( &self, swapchain: &Swapchain, image_index: u32, before: bool, ) -> Result<(), AccessCheckError>612 fn check_swapchain_image_acquired(
613 &self,
614 swapchain: &Swapchain,
615 image_index: u32,
616 before: bool,
617 ) -> Result<(), AccessCheckError> {
618 (**self).check_swapchain_image_acquired(swapchain, image_index, before)
619 }
620 }
621