vulkano/
deferred.rs

1//! Operations on the host that can be deferred.
2//!
3//! You typically pass a [`DeferredOperation`] object as part of a call to another function that
4//! performs some potentially time-consuming work. The operation will then not be performed
5//! immediately, but is put on hold. You must then call [`join`] repeatedly on one or more threads
6//! to make the operation progress, until it is complete.
7//!
8//! [`join`]: DeferredOperation::join
9
10use crate::{
11    device::{Device, DeviceOwned},
12    instance::InstanceOwnedDebugWrapper,
13    Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanObject,
14};
15use std::{mem::MaybeUninit, ptr, sync::Arc};
16
17/// An operation on the host that has been deferred.
18///
19/// The object cannot be dropped while an operation is pending. If it is dropped
20/// prematurely, the current thread will block to wait for the operation to finish.
21#[derive(Debug)]
22pub struct DeferredOperation {
23    device: InstanceOwnedDebugWrapper<Arc<Device>>,
24    handle: ash::vk::DeferredOperationKHR,
25}
26
27impl DeferredOperation {
28    /// Creates a new `DeferredOperation`.
29    ///
30    /// The [`khr_deferred_host_operations`] extension must be enabled on the device.
31    ///
32    /// [`khr_deferred_host_operations`]: crate::device::DeviceExtensions::khr_deferred_host_operations
33    #[inline]
34    pub fn new(device: Arc<Device>) -> Result<Arc<Self>, Validated<VulkanError>> {
35        Self::validate_new(&device)?;
36
37        Ok(unsafe { Self::new_unchecked(device) }?)
38    }
39
40    fn validate_new(device: &Device) -> Result<(), Box<ValidationError>> {
41        if !device.enabled_extensions().khr_deferred_host_operations {
42            return Err(Box::new(ValidationError {
43                requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
44                    "khr_deferred_host_operations",
45                )])]),
46                ..Default::default()
47            }));
48        }
49
50        Ok(())
51    }
52
53    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
54    pub unsafe fn new_unchecked(device: Arc<Device>) -> Result<Arc<Self>, VulkanError> {
55        let handle = {
56            let fns = device.fns();
57            let mut output = MaybeUninit::uninit();
58            unsafe {
59                (fns.khr_deferred_host_operations
60                    .create_deferred_operation_khr)(
61                    device.handle(),
62                    ptr::null(),
63                    output.as_mut_ptr(),
64                )
65            }
66            .result()
67            .map_err(VulkanError::from)?;
68            unsafe { output.assume_init() }
69        };
70
71        Ok(unsafe { Self::from_handle(device, handle) })
72    }
73
74    /// Creates a new `DeferredOperation` from a raw object handle.
75    ///
76    /// # Safety
77    ///
78    /// - `handle` must be a valid Vulkan object handle created from `device`.
79    #[inline]
80    pub unsafe fn from_handle(
81        device: Arc<Device>,
82        handle: ash::vk::DeferredOperationKHR,
83    ) -> Arc<Self> {
84        Arc::new(Self {
85            device: InstanceOwnedDebugWrapper(device),
86            handle,
87        })
88    }
89
90    /// Executes a portion of the operation on the current thread.
91    pub fn join(&self) -> Result<DeferredOperationJoinStatus, VulkanError> {
92        let fns = self.device.fns();
93        let result = unsafe {
94            (fns.khr_deferred_host_operations.deferred_operation_join_khr)(
95                self.device.handle(),
96                self.handle,
97            )
98        };
99
100        match result {
101            ash::vk::Result::SUCCESS => Ok(DeferredOperationJoinStatus::Complete),
102            ash::vk::Result::THREAD_DONE_KHR => Ok(DeferredOperationJoinStatus::ThreadDone),
103            ash::vk::Result::THREAD_IDLE_KHR => Ok(DeferredOperationJoinStatus::ThreadIdle),
104            err => Err(VulkanError::from(err)),
105        }
106    }
107
108    /// Returns the result of the operation, or `None` if the operation is not yet complete.
109    pub fn result(&self) -> Option<Result<(), VulkanError>> {
110        let fns = self.device.fns();
111        let result = unsafe {
112            (fns.khr_deferred_host_operations
113                .get_deferred_operation_result_khr)(self.device.handle(), self.handle)
114        };
115
116        match result {
117            ash::vk::Result::NOT_READY => None,
118            ash::vk::Result::SUCCESS => Some(Ok(())),
119            err => Some(Err(VulkanError::from(err))),
120        }
121    }
122
123    /// Waits for the operation to complete, then returns its result.
124    pub fn wait(&self) -> Result<Result<(), VulkanError>, VulkanError> {
125        // Based on example code on the extension's spec page.
126
127        // Call `join` until we get `Complete` or `ThreadDone`.
128        loop {
129            match self.join()? {
130                DeferredOperationJoinStatus::Complete => {
131                    break;
132                }
133                DeferredOperationJoinStatus::ThreadDone => {
134                    std::thread::yield_now();
135                    break;
136                }
137                DeferredOperationJoinStatus::ThreadIdle => {}
138            }
139        }
140
141        // Call `result` until we get `Some`.
142        loop {
143            if let Some(result) = self.result() {
144                return Ok(result);
145            }
146
147            std::thread::yield_now();
148        }
149    }
150
151    /// The maximum number of threads that could usefully execute the operation at this point in
152    /// its execution, or zero if the operation is complete.
153    ///
154    /// Returns `None` if no exact number of threads can be calculated.
155    pub fn max_concurrency(&self) -> Option<u32> {
156        let fns = self.device.fns();
157        let result = unsafe {
158            (fns.khr_deferred_host_operations
159                .get_deferred_operation_max_concurrency_khr)(
160                self.device.handle(), self.handle
161            )
162        };
163
164        (result != u32::MAX).then_some(result)
165    }
166}
167
168impl Drop for DeferredOperation {
169    #[inline]
170    fn drop(&mut self) {
171        let _ = self.wait(); // Ignore errors
172
173        let fns = self.device.fns();
174        unsafe {
175            (fns.khr_deferred_host_operations
176                .destroy_deferred_operation_khr)(
177                self.device.handle(), self.handle, ptr::null()
178            )
179        };
180    }
181}
182
183unsafe impl VulkanObject for DeferredOperation {
184    type Handle = ash::vk::DeferredOperationKHR;
185
186    #[inline]
187    fn handle(&self) -> Self::Handle {
188        self.handle
189    }
190}
191
192unsafe impl DeviceOwned for DeferredOperation {
193    #[inline]
194    fn device(&self) -> &Arc<Device> {
195        &self.device
196    }
197}
198
199/// The status of the operation after [`join`] returns.
200///
201/// [`join`]: DeferredOperation::join
202#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
203pub enum DeferredOperationJoinStatus {
204    /// The operation completed.
205    Complete,
206
207    /// The operation did not complete yet,
208    /// but there is no more work to be done on the current thread.
209    ThreadDone,
210
211    /// The operation did not complete yet,
212    /// and there may be work to do on the current thread in the future.
213    ThreadIdle,
214}