Skip to main content

vulkano/command_buffer/
traits.rs

1use super::{
2    CommandBuffer, CommandBufferInheritanceInfo, CommandBufferResourcesUsage, CommandBufferState,
3    CommandBufferSubmitInfo, CommandBufferUsage, SecondaryCommandBufferResourcesUsage,
4    SemaphoreSubmitInfo, SubmitInfo,
5};
6use crate::{
7    buffer::Buffer,
8    device::{Device, DeviceOwned, Queue},
9    image::{Image, ImageLayout},
10    swapchain::Swapchain,
11    sync::{
12        future::{
13            now, queue_submit, AccessCheckError, AccessError, GpuFuture, NowFuture,
14            SubmitAnyBuilder,
15        },
16        PipelineStages,
17    },
18    DeviceSize, SafeDeref, Validated, ValidationError, VulkanError, VulkanObject,
19};
20use parking_lot::{Mutex, MutexGuard};
21use std::{
22    borrow::Cow,
23    error::Error,
24    fmt::{Debug, Display, Error as FmtError, Formatter},
25    ops::Range,
26    sync::{
27        atomic::{AtomicBool, Ordering},
28        Arc,
29    },
30    thread,
31};
32
33pub unsafe trait PrimaryCommandBufferAbstract:
34    VulkanObject<Handle = ash::vk::CommandBuffer> + DeviceOwned + Send + Sync
35{
36    /// Returns the underlying raw command buffer.
37    fn as_raw(&self) -> &CommandBuffer;
38
39    /// Returns the queue family index of this command buffer.
40    fn queue_family_index(&self) -> u32;
41
42    /// Returns the usage of this command buffer.
43    fn usage(&self) -> CommandBufferUsage;
44
45    /// Executes this command buffer on a queue.
46    ///
47    /// This function returns an object that implements the [`GpuFuture`] trait. See the
48    /// documentation of the [`future`][crate::sync::future] module for more information.
49    ///
50    /// The command buffer is not actually executed until you call [`flush()`][GpuFuture::flush] on
51    /// the future.  You are encouraged to chain together as many futures as possible prior to
52    /// calling [`flush()`][GpuFuture::flush]. In order to know when the future has completed, call
53    /// one of [`then_signal_fence()`][GpuFuture::then_signal_fence] or
54    /// [`then_signal_semaphore()`][GpuFuture::then_signal_semaphore]. You can do both together
55    /// with [`then_signal_fence_and_flush()`][GpuFuture::then_signal_fence_and_flush] or
56    /// [`then_signal_semaphore_and_flush()`][GpuFuture::then_signal_semaphore_and_flush],
57    /// respectively.
58    ///
59    /// > **Note**: In the future this function may return `-> impl GpuFuture` instead of a
60    /// > concrete type.
61    ///
62    /// > **Note**: This is just a shortcut for `execute_after(vulkano::sync::now(), queue)`.
63    ///
64    /// # Panics
65    ///
66    /// - Panics if the device of the command buffer is not the same as the device of the future.
67    #[inline]
68    fn execute(
69        self: Arc<Self>,
70        queue: Arc<Queue>,
71    ) -> Result<CommandBufferExecFuture<NowFuture>, CommandBufferExecError>
72    where
73        Self: Sized + 'static,
74    {
75        let device = queue.device().clone();
76        self.execute_after(now(device), queue)
77    }
78
79    /// Executes the command buffer after an existing future.
80    ///
81    /// This function returns an object that implements the [`GpuFuture`] trait. See the
82    /// documentation of the [`future`][crate::sync::future] module for more information.
83    ///
84    /// The command buffer is not actually executed until you call [`flush()`][GpuFuture::flush] on
85    /// the future.  You are encouraged to chain together as many futures as possible prior to
86    /// calling [`flush()`][GpuFuture::flush]. In order to know when the future has completed, call
87    /// one of [`then_signal_fence()`][GpuFuture::then_signal_fence] or
88    /// [`then_signal_semaphore()`][GpuFuture::then_signal_semaphore]. You can do both together
89    /// with [`then_signal_fence_and_flush()`][GpuFuture::then_signal_fence_and_flush] or
90    /// [`then_signal_semaphore_and_flush()`][GpuFuture::then_signal_semaphore_and_flush],
91    /// respectively.
92    ///
93    /// > **Note**: In the future this function may return `-> impl GpuFuture` instead of a
94    /// > concrete type.
95    ///
96    /// This function requires the `'static` lifetime to be on the command buffer. This is because
97    /// this function returns a `CommandBufferExecFuture` whose job is to lock resources and keep
98    /// them alive while they are in use by the GPU. If `'static` wasn't required, you could call
99    /// `std::mem::forget` on that object and "unlock" these resources. For more information about
100    /// this problem, search the web for "rust thread scoped leakpocalypse".
101    ///
102    /// # Panics
103    ///
104    /// - Panics if the device of the command buffer is not the same as the device of the future.
105    fn execute_after<F>(
106        self: Arc<Self>,
107        future: F,
108        queue: Arc<Queue>,
109    ) -> Result<CommandBufferExecFuture<F>, CommandBufferExecError>
110    where
111        Self: Sized + 'static,
112        F: GpuFuture,
113    {
114        assert_eq!(self.device().handle(), future.device().handle());
115
116        if !future.queue_change_allowed() {
117            assert_eq!(future.queue().unwrap(), queue);
118        }
119
120        Ok(CommandBufferExecFuture {
121            previous: future,
122            command_buffer: self,
123            queue,
124            submitted: Mutex::new(false),
125            finished: AtomicBool::new(false),
126        })
127    }
128
129    #[doc(hidden)]
130    fn state(&self) -> MutexGuard<'_, CommandBufferState>;
131
132    #[doc(hidden)]
133    fn resources_usage(&self) -> &CommandBufferResourcesUsage;
134}
135
136impl Debug for dyn PrimaryCommandBufferAbstract {
137    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
138        Debug::fmt(&self.handle(), f)
139    }
140}
141
142unsafe impl<T> PrimaryCommandBufferAbstract for T
143where
144    T: VulkanObject<Handle = ash::vk::CommandBuffer> + SafeDeref + Send + Sync,
145    T::Target: PrimaryCommandBufferAbstract,
146{
147    fn as_raw(&self) -> &CommandBuffer {
148        (**self).as_raw()
149    }
150
151    fn queue_family_index(&self) -> u32 {
152        (**self).queue_family_index()
153    }
154
155    fn usage(&self) -> CommandBufferUsage {
156        (**self).usage()
157    }
158
159    fn state(&self) -> MutexGuard<'_, CommandBufferState> {
160        (**self).state()
161    }
162
163    fn resources_usage(&self) -> &CommandBufferResourcesUsage {
164        (**self).resources_usage()
165    }
166}
167
168pub unsafe trait SecondaryCommandBufferAbstract:
169    VulkanObject<Handle = ash::vk::CommandBuffer> + DeviceOwned + Send + Sync
170{
171    /// Returns the underlying raw command buffer.
172    fn as_raw(&self) -> &CommandBuffer;
173
174    /// Returns the usage of this command buffer.
175    fn usage(&self) -> CommandBufferUsage;
176
177    /// Returns a `CommandBufferInheritance` value describing the properties that the command
178    /// buffer inherits from its parent primary command buffer.
179    fn inheritance_info(&self) -> &CommandBufferInheritanceInfo;
180
181    /// Checks whether this command buffer is allowed to be recorded to a command buffer,
182    /// and if so locks it.
183    ///
184    /// If you call this function, then you should call `unlock` afterwards.
185    fn lock_record(&self) -> Result<(), Box<ValidationError>>;
186
187    /// Unlocks the command buffer. Should be called once for each call to `lock_record`.
188    ///
189    /// # Safety
190    ///
191    /// Must not be called if you haven't called `lock_record` before.
192    unsafe fn unlock(&self);
193
194    #[doc(hidden)]
195    fn resources_usage(&self) -> &SecondaryCommandBufferResourcesUsage;
196}
197
198unsafe impl<T> SecondaryCommandBufferAbstract for T
199where
200    T: VulkanObject<Handle = ash::vk::CommandBuffer> + SafeDeref + Send + Sync,
201    T::Target: SecondaryCommandBufferAbstract,
202{
203    fn as_raw(&self) -> &CommandBuffer {
204        (**self).as_raw()
205    }
206
207    fn usage(&self) -> CommandBufferUsage {
208        (**self).usage()
209    }
210
211    fn inheritance_info(&self) -> &CommandBufferInheritanceInfo {
212        (**self).inheritance_info()
213    }
214
215    fn lock_record(&self) -> Result<(), Box<ValidationError>> {
216        (**self).lock_record()
217    }
218
219    unsafe fn unlock(&self) {
220        unsafe { (**self).unlock() };
221    }
222
223    fn resources_usage(&self) -> &SecondaryCommandBufferResourcesUsage {
224        (**self).resources_usage()
225    }
226}
227
228/// Represents a command buffer being executed by the GPU and the moment when the execution
229/// finishes.
230#[derive(Debug)]
231#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
232pub struct CommandBufferExecFuture<F>
233where
234    F: GpuFuture,
235{
236    previous: F,
237    command_buffer: Arc<dyn PrimaryCommandBufferAbstract>,
238    queue: Arc<Queue>,
239    // True if the command buffer has already been submitted.
240    // If flush is called multiple times, we want to block so that only one flushing is executed.
241    // Therefore we use a `Mutex<bool>` and not an `AtomicBool`.
242    submitted: Mutex<bool>,
243    finished: AtomicBool,
244}
245
246impl<F> CommandBufferExecFuture<F>
247where
248    F: GpuFuture,
249{
250    // Implementation of `build_submission`. Doesn't check whenever the future was already flushed.
251    // You must make sure to not submit same command buffer multiple times.
252    unsafe fn build_submission_impl(&self) -> Result<SubmitAnyBuilder, Validated<VulkanError>> {
253        Ok(match unsafe { self.previous.build_submission() }? {
254            SubmitAnyBuilder::Empty => SubmitAnyBuilder::CommandBuffer(
255                SubmitInfo {
256                    command_buffers: vec![CommandBufferSubmitInfo::new(
257                        self.command_buffer.clone(),
258                    )],
259                    ..Default::default()
260                },
261                None,
262            ),
263            SubmitAnyBuilder::SemaphoresWait(semaphores) => {
264                SubmitAnyBuilder::CommandBuffer(
265                    SubmitInfo {
266                        wait_semaphores: semaphores
267                            .into_iter()
268                            .map(|semaphore| {
269                                SemaphoreSubmitInfo {
270                                    // TODO: correct stages ; hard
271                                    stages: PipelineStages::ALL_COMMANDS,
272                                    ..SemaphoreSubmitInfo::new(semaphore)
273                                }
274                            })
275                            .collect(),
276                        command_buffers: vec![CommandBufferSubmitInfo::new(
277                            self.command_buffer.clone(),
278                        )],
279                        ..Default::default()
280                    },
281                    None,
282                )
283            }
284            SubmitAnyBuilder::CommandBuffer(mut submit_info, fence) => {
285                // FIXME: add pipeline barrier
286                submit_info
287                    .command_buffers
288                    .push(CommandBufferSubmitInfo::new(self.command_buffer.clone()));
289                SubmitAnyBuilder::CommandBuffer(submit_info, fence)
290            }
291            SubmitAnyBuilder::QueuePresent(_) | SubmitAnyBuilder::BindSparse(_, _) => {
292                unimplemented!() // TODO:
293                                 /*present.submit();     // TODO: wrong
294                                 let mut builder = SubmitCommandBufferBuilder::new();
295                                 builder.add_command_buffer(self.command_buffer.inner());
296                                 SubmitAnyBuilder::CommandBuffer(builder)*/
297            }
298        })
299    }
300}
301
302unsafe impl<F> GpuFuture for CommandBufferExecFuture<F>
303where
304    F: GpuFuture,
305{
306    fn cleanup_finished(&mut self) {
307        self.previous.cleanup_finished();
308    }
309
310    unsafe fn build_submission(&self) -> Result<SubmitAnyBuilder, Validated<VulkanError>> {
311        if *self.submitted.lock() {
312            return Ok(SubmitAnyBuilder::Empty);
313        }
314
315        unsafe { self.build_submission_impl() }
316    }
317
318    fn flush(&self) -> Result<(), Validated<VulkanError>> {
319        let mut submitted = self.submitted.lock();
320
321        if *submitted {
322            return Ok(());
323        }
324
325        match unsafe { self.build_submission_impl() }? {
326            SubmitAnyBuilder::Empty => {}
327            SubmitAnyBuilder::CommandBuffer(submit_info, fence) => {
328                unsafe { queue_submit(&self.queue, submit_info, fence, &self.previous) }.unwrap();
329            }
330            _ => unreachable!(),
331        };
332
333        // Only write `true` here in order to try again next time if we failed to submit.
334        *submitted = true;
335        Ok(())
336    }
337
338    unsafe fn signal_finished(&self) {
339        if !self.finished.swap(true, Ordering::SeqCst) {
340            let resource_usage = self.command_buffer.resources_usage();
341
342            for usage in &resource_usage.buffers {
343                let mut state = usage.buffer.state();
344
345                for (range, range_usage) in usage.ranges.iter() {
346                    if range_usage.mutable {
347                        unsafe { state.gpu_write_unlock(range.clone()) };
348                    } else {
349                        unsafe { state.gpu_read_unlock(range.clone()) };
350                    }
351                }
352            }
353
354            for usage in &resource_usage.images {
355                let mut state = usage.image.state();
356
357                for (range, range_usage) in usage.ranges.iter() {
358                    if range_usage.mutable {
359                        unsafe { state.gpu_write_unlock(range.clone()) };
360                    } else {
361                        unsafe { state.gpu_read_unlock(range.clone()) };
362                    }
363                }
364            }
365
366            unsafe { self.command_buffer.state().set_submit_finished() };
367        }
368
369        unsafe { self.previous.signal_finished() };
370    }
371
372    fn queue_change_allowed(&self) -> bool {
373        false
374    }
375
376    fn queue(&self) -> Option<Arc<Queue>> {
377        Some(self.queue.clone())
378    }
379
380    fn check_buffer_access(
381        &self,
382        buffer: &Buffer,
383        range: Range<DeviceSize>,
384        exclusive: bool,
385        queue: &Queue,
386    ) -> Result<(), AccessCheckError> {
387        let resources_usage = self.command_buffer.resources_usage();
388        let usage = match resources_usage.buffer_indices.get(buffer) {
389            Some(&index) => &resources_usage.buffers[index],
390            None => return Err(AccessCheckError::Unknown),
391        };
392
393        // TODO: check the queue family
394
395        let result = usage
396            .ranges
397            .range(&range)
398            .try_fold((), |_, (_range, range_usage)| {
399                if !range_usage.mutable && exclusive {
400                    Err(AccessCheckError::Unknown)
401                } else {
402                    Ok(())
403                }
404            });
405
406        match result {
407            Ok(()) => Ok(()),
408            Err(AccessCheckError::Denied(err)) => Err(AccessCheckError::Denied(err)),
409            Err(AccessCheckError::Unknown) => self
410                .previous
411                .check_buffer_access(buffer, range, exclusive, queue),
412        }
413    }
414
415    fn check_image_access(
416        &self,
417        image: &Image,
418        range: Range<DeviceSize>,
419        exclusive: bool,
420        expected_layout: ImageLayout,
421        queue: &Queue,
422    ) -> Result<(), AccessCheckError> {
423        let resources_usage = self.command_buffer.resources_usage();
424        let usage = match resources_usage.image_indices.get(image) {
425            Some(&index) => &resources_usage.images[index],
426            None => return Err(AccessCheckError::Unknown),
427        };
428
429        // TODO: check the queue family
430
431        let result = usage
432            .ranges
433            .range(&range)
434            .try_fold((), |_, (_range, range_usage)| {
435                if expected_layout != ImageLayout::Undefined
436                    && range_usage.final_layout != expected_layout
437                {
438                    return Err(AccessCheckError::Denied(
439                        AccessError::UnexpectedImageLayout {
440                            allowed: range_usage.final_layout,
441                            requested: expected_layout,
442                        },
443                    ));
444                }
445
446                if !range_usage.mutable && exclusive {
447                    Err(AccessCheckError::Unknown)
448                } else {
449                    Ok(())
450                }
451            });
452
453        match result {
454            Ok(()) => Ok(()),
455            Err(AccessCheckError::Denied(err)) => Err(AccessCheckError::Denied(err)),
456            Err(AccessCheckError::Unknown) => {
457                self.previous
458                    .check_image_access(image, range, exclusive, expected_layout, queue)
459            }
460        }
461    }
462
463    #[inline]
464    fn check_swapchain_image_acquired(
465        &self,
466        swapchain: &Swapchain,
467        image_index: u32,
468        _before: bool,
469    ) -> Result<(), AccessCheckError> {
470        self.previous
471            .check_swapchain_image_acquired(swapchain, image_index, false)
472    }
473}
474
475unsafe impl<F> DeviceOwned for CommandBufferExecFuture<F>
476where
477    F: GpuFuture,
478{
479    fn device(&self) -> &Arc<Device> {
480        self.command_buffer.device()
481    }
482}
483
484impl<F> Drop for CommandBufferExecFuture<F>
485where
486    F: GpuFuture,
487{
488    fn drop(&mut self) {
489        if !*self.finished.get_mut() && !thread::panicking() {
490            // TODO: handle errors?
491            self.flush().unwrap();
492            // Block until the queue finished.
493            self.queue.with(|mut q| q.wait_idle()).unwrap();
494
495            unsafe {
496                self.signal_finished();
497            }
498        }
499    }
500}
501
502/// Error that can happen when attempting to execute a command buffer.
503#[derive(Clone, Debug, PartialEq, Eq)]
504pub enum CommandBufferExecError {
505    /// Access to a resource has been denied.
506    AccessError {
507        error: AccessError,
508        command_name: Cow<'static, str>,
509        command_param: Cow<'static, str>,
510        command_offset: usize,
511    },
512
513    /// The command buffer or one of the secondary command buffers it executes was created with the
514    /// "one time submit" flag, but has already been submitted it the past.
515    OneTimeSubmitAlreadySubmitted,
516
517    /// The command buffer or one of the secondary command buffers it executes is already in use by
518    /// the GPU and was not created with the "concurrent" flag.
519    ExclusiveAlreadyInUse,
520    // TODO: missing entries (eg. wrong queue family, secondary command buffer)
521}
522
523impl Error for CommandBufferExecError {
524    fn source(&self) -> Option<&(dyn Error + 'static)> {
525        match self {
526            CommandBufferExecError::AccessError { error, .. } => Some(error),
527            _ => None,
528        }
529    }
530}
531
532impl Display for CommandBufferExecError {
533    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
534        match self {
535            CommandBufferExecError::AccessError {
536                error: _,
537                command_name,
538                command_offset,
539                command_param,
540            } => write!(
541                f,
542                "access to a resource has been denied on command {} (offset: {}, param: {})",
543                command_name, command_offset, command_param,
544            ),
545            CommandBufferExecError::OneTimeSubmitAlreadySubmitted => write!(
546                f,
547                "the command buffer or one of the secondary command buffers it executes was \
548                created with the \"one time submit\" flag, but has already been submitted in \
549                the past",
550            ),
551            CommandBufferExecError::ExclusiveAlreadyInUse => write!(
552                f,
553                "the command buffer or one of the secondary command buffers it executes is \
554                already in use was not created with the \"concurrent\" flag"
555            ),
556        }
557    }
558}