use ash::vk;
use impeller_hal::{Error, HalFence, Result};
use std::time::Duration;
pub struct VulkanFence {
device: ash::Device,
fence: vk::Fence,
pub(crate) command_pool: vk::CommandPool,
pub(crate) command_buffer: vk::CommandBuffer,
pub(crate) framebuffer: vk::Framebuffer,
pub(crate) view: vk::ImageView,
semaphore: Option<vk::Semaphore>,
export: Option<ash::khr::external_semaphore_fd::Device>,
pub(crate) retained: Vec<crate::render::StagedBuffer>,
pub(crate) bindings: Option<crate::sampling::Bindings>,
pub(crate) materials: Option<crate::materials::Materials>,
retired: bool,
}
impl VulkanFence {
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
device: ash::Device,
fence: vk::Fence,
command_pool: vk::CommandPool,
command_buffer: vk::CommandBuffer,
framebuffer: vk::Framebuffer,
view: vk::ImageView,
semaphore: Option<vk::Semaphore>,
export: Option<ash::khr::external_semaphore_fd::Device>,
) -> Self {
Self {
device,
fence,
command_pool,
command_buffer,
framebuffer,
view,
semaphore,
export,
retained: Vec::new(),
bindings: None,
materials: None,
retired: false,
}
}
pub fn is_exportable(&self) -> bool {
self.export.is_some() && self.semaphore.is_some()
}
pub(crate) fn retire(&mut self) {
if self.retired {
return;
}
self.retired = true;
unsafe {
let _ = self.device.wait_for_fences(&[self.fence], true, u64::MAX);
self.device.destroy_framebuffer(self.framebuffer, None);
self.device.destroy_image_view(self.view, None);
self.device
.free_command_buffers(self.command_pool, &[self.command_buffer]);
if let Some(semaphore) = self.semaphore.take() {
self.device.destroy_semaphore(semaphore, None);
}
self.device.destroy_fence(self.fence, None);
}
}
}
impl HalFence for VulkanFence {
fn is_signaled(&self) -> Result<bool> {
match unsafe { self.device.get_fence_status(self.fence) } {
Ok(signaled) => Ok(signaled),
Err(e) => Err(Error::Backend {
backend: "vulkan",
detail: format!("get_fence_status: {e:?}"),
}),
}
}
fn wait(&self, timeout: Duration) -> Result<bool> {
let nanos = u64::try_from(timeout.as_nanos()).unwrap_or(u64::MAX);
let result = unsafe { self.device.wait_for_fences(&[self.fence], true, nanos) };
match result {
Ok(()) => Ok(true),
Err(vk::Result::TIMEOUT) => Ok(false),
Err(vk::Result::ERROR_DEVICE_LOST) => Err(Error::DeviceLost),
Err(e) => Err(Error::Backend {
backend: "vulkan",
detail: format!("wait_for_fences: {e:?}"),
}),
}
}
#[cfg(unix)]
fn export_sync_file(&self) -> Result<std::os::fd::OwnedFd> {
use std::os::fd::FromRawFd;
let (Some(loader), Some(semaphore)) = (&self.export, self.semaphore) else {
return Err(Error::Unsupported(
"this submission has no exportable payload",
));
};
let info = vk::SemaphoreGetFdInfoKHR::default()
.semaphore(semaphore)
.handle_type(vk::ExternalSemaphoreHandleTypeFlags::SYNC_FD);
let raw = unsafe { loader.get_semaphore_fd(&info) }.map_err(|e| Error::Backend {
backend: "vulkan",
detail: format!("get_semaphore_fd: {e:?}"),
})?;
if raw < 0 {
return Err(Error::Unsupported(
"the fence had already signalled, so there is no sync_file to export",
));
}
Ok(unsafe { std::os::fd::OwnedFd::from_raw_fd(raw) })
}
}