use crate::{
device::{Device, DeviceOwned},
instance::InstanceOwnedDebugWrapper,
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanObject,
};
use std::{mem::MaybeUninit, ptr, sync::Arc};
#[derive(Debug)]
pub struct DeferredOperation {
device: InstanceOwnedDebugWrapper<Arc<Device>>,
handle: ash::vk::DeferredOperationKHR,
}
impl DeferredOperation {
#[inline]
pub fn new(device: Arc<Device>) -> Result<Arc<Self>, Validated<VulkanError>> {
Self::validate_new(&device)?;
unsafe { Ok(Self::new_unchecked(device)?) }
}
fn validate_new(device: &Device) -> Result<(), Box<ValidationError>> {
if !device.enabled_extensions().khr_deferred_host_operations {
return Err(Box::new(ValidationError {
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"khr_deferred_host_operations",
)])]),
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(device: Arc<Device>) -> Result<Arc<Self>, VulkanError> {
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.khr_deferred_host_operations
.create_deferred_operation_khr)(
device.handle(), ptr::null(), output.as_mut_ptr()
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
Ok(Self::from_handle(device, handle))
}
#[inline]
pub unsafe fn from_handle(
device: Arc<Device>,
handle: ash::vk::DeferredOperationKHR,
) -> Arc<Self> {
Arc::new(Self {
device: InstanceOwnedDebugWrapper(device),
handle,
})
}
pub fn join(&self) -> Result<DeferredOperationJoinStatus, VulkanError> {
let result = unsafe {
let fns = self.device.fns();
(fns.khr_deferred_host_operations.deferred_operation_join_khr)(
self.device.handle(),
self.handle,
)
};
match result {
ash::vk::Result::SUCCESS => Ok(DeferredOperationJoinStatus::Complete),
ash::vk::Result::THREAD_DONE_KHR => Ok(DeferredOperationJoinStatus::ThreadDone),
ash::vk::Result::THREAD_IDLE_KHR => Ok(DeferredOperationJoinStatus::ThreadIdle),
err => Err(VulkanError::from(err)),
}
}
pub fn result(&self) -> Option<Result<(), VulkanError>> {
let result = unsafe {
let fns = self.device.fns();
(fns.khr_deferred_host_operations
.get_deferred_operation_result_khr)(self.device.handle(), self.handle)
};
match result {
ash::vk::Result::NOT_READY => None,
ash::vk::Result::SUCCESS => Some(Ok(())),
err => Some(Err(VulkanError::from(err))),
}
}
pub fn wait(&self) -> Result<Result<(), VulkanError>, VulkanError> {
loop {
match self.join()? {
DeferredOperationJoinStatus::Complete => {
break;
}
DeferredOperationJoinStatus::ThreadDone => {
std::thread::yield_now();
break;
}
DeferredOperationJoinStatus::ThreadIdle => {}
}
}
loop {
if let Some(result) = self.result() {
return Ok(result);
}
std::thread::yield_now();
}
}
pub fn max_concurrency(&self) -> Option<u32> {
let result = unsafe {
let fns = self.device.fns();
(fns.khr_deferred_host_operations
.get_deferred_operation_max_concurrency_khr)(
self.device.handle(), self.handle
)
};
(result != u32::MAX).then_some(result)
}
}
impl Drop for DeferredOperation {
#[inline]
fn drop(&mut self) {
let _ = self.wait();
unsafe {
let fns = self.device.fns();
(fns.khr_deferred_host_operations
.destroy_deferred_operation_khr)(
self.device.handle(), self.handle, ptr::null()
);
}
}
}
unsafe impl VulkanObject for DeferredOperation {
type Handle = ash::vk::DeferredOperationKHR;
#[inline]
fn handle(&self) -> Self::Handle {
self.handle
}
}
unsafe impl DeviceOwned for DeferredOperation {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DeferredOperationJoinStatus {
Complete,
ThreadDone,
ThreadIdle,
}