use crate::{
check_errors,
device::{Device, DeviceOwned},
Error, OomError, Version, VulkanObject,
};
use std::{
fmt,
fs::File,
hash::{Hash, Hasher},
mem::MaybeUninit,
ops::BitOr,
ptr,
sync::Arc,
};
#[derive(Debug)]
pub struct Semaphore {
handle: ash::vk::Semaphore,
device: Arc<Device>,
must_put_in_pool: bool,
export_handle_types: ExternalSemaphoreHandleTypes,
}
impl Semaphore {
pub fn new(
device: Arc<Device>,
create_info: SemaphoreCreateInfo,
) -> Result<Semaphore, SemaphoreCreationError> {
let SemaphoreCreateInfo {
export_handle_types,
_ne: _,
} = create_info;
let instance = device.instance();
if export_handle_types != ExternalSemaphoreHandleTypes::none() {
if !(device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_external_semaphore)
{
return Err(SemaphoreCreationError::MissingExtension(
"khr_external_semaphore",
));
}
if (export_handle_types.opaque_fd
|| export_handle_types.opaque_win32
|| export_handle_types.opaque_win32_kmt
|| export_handle_types.d3d12_fence
|| export_handle_types.sync_fd)
&& !(instance.api_version() >= Version::V1_1
|| instance
.enabled_extensions()
.khr_external_semaphore_capabilities)
{
return Err(SemaphoreCreationError::MissingExtension(
"khr_external_semaphore_capabilities",
));
}
}
let mut create_info = ash::vk::SemaphoreCreateInfo::builder();
let mut export_semaphore_create_info =
if export_handle_types != ExternalSemaphoreHandleTypes::none() {
Some(ash::vk::ExportSemaphoreCreateInfo {
handle_types: export_handle_types.into(),
..Default::default()
})
} else {
None
};
if let Some(info) = export_semaphore_create_info.as_mut() {
create_info = create_info.push_next(info);
}
let handle = unsafe {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
check_errors(fns.v1_0.create_semaphore(
device.internal_object(),
&create_info.build(),
ptr::null(),
output.as_mut_ptr(),
))?;
output.assume_init()
};
Ok(Semaphore {
device,
handle,
must_put_in_pool: false,
export_handle_types,
})
}
pub fn from_pool(device: Arc<Device>) -> Result<Semaphore, SemaphoreCreationError> {
let handle = device.semaphore_pool().lock().unwrap().pop();
let semaphore = match handle {
Some(handle) => Semaphore {
device,
handle,
must_put_in_pool: true,
export_handle_types: ExternalSemaphoreHandleTypes::none(),
},
None => {
let mut semaphore = Semaphore::new(device, Default::default())?;
semaphore.must_put_in_pool = true;
semaphore
}
};
Ok(semaphore)
}
pub unsafe fn export_opaque_fd(&self) -> Result<File, SemaphoreExportError> {
let fns = self.device.fns();
if !self.export_handle_types.opaque_fd {
return Err(SemaphoreExportError::HandleTypeNotSupported {
handle_type: ExternalSemaphoreHandleType::OpaqueFd,
});
}
assert!(self.device.enabled_extensions().khr_external_semaphore);
assert!(self.device.enabled_extensions().khr_external_semaphore_fd);
#[cfg(not(unix))]
unreachable!("`khr_external_semaphore_fd` was somehow enabled on a non-Unix system");
#[cfg(unix)]
{
use std::os::unix::io::FromRawFd;
let fd = {
let info = ash::vk::SemaphoreGetFdInfoKHR {
semaphore: self.handle,
handle_type: ash::vk::ExternalSemaphoreHandleTypeFlagsKHR::OPAQUE_FD,
..Default::default()
};
let mut output = MaybeUninit::uninit();
check_errors(fns.khr_external_semaphore_fd.get_semaphore_fd_khr(
self.device.internal_object(),
&info,
output.as_mut_ptr(),
))?;
output.assume_init()
};
let file = File::from_raw_fd(fd);
Ok(file)
}
}
}
impl Drop for Semaphore {
#[inline]
fn drop(&mut self) {
unsafe {
if self.must_put_in_pool {
let raw_sem = self.handle;
self.device.semaphore_pool().lock().unwrap().push(raw_sem);
} else {
let fns = self.device.fns();
fns.v1_0
.destroy_semaphore(self.device.internal_object(), self.handle, ptr::null());
}
}
}
}
unsafe impl VulkanObject for Semaphore {
type Object = ash::vk::Semaphore;
#[inline]
fn internal_object(&self) -> ash::vk::Semaphore {
self.handle
}
}
unsafe impl DeviceOwned for Semaphore {
#[inline]
fn device(&self) -> &Arc<Device> {
&self.device
}
}
impl PartialEq for Semaphore {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.handle == other.handle && self.device() == other.device()
}
}
impl Eq for Semaphore {}
impl Hash for Semaphore {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
self.device().hash(state);
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SemaphoreCreationError {
OomError(OomError),
MissingExtension(&'static str),
}
impl fmt::Display for SemaphoreCreationError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available"),
Self::MissingExtension(s) => {
write!(fmt, "Missing the following extension: {}", s)
}
}
}
}
impl From<Error> for SemaphoreCreationError {
#[inline]
fn from(err: Error) -> Self {
match err {
e @ Error::OutOfHostMemory | e @ Error::OutOfDeviceMemory => Self::OomError(e.into()),
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl std::error::Error for SemaphoreCreationError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl From<OomError> for SemaphoreCreationError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
#[derive(Clone, Debug)]
pub struct SemaphoreCreateInfo {
pub export_handle_types: ExternalSemaphoreHandleTypes,
pub _ne: crate::NonExhaustive,
}
impl Default for SemaphoreCreateInfo {
#[inline]
fn default() -> Self {
Self {
export_handle_types: ExternalSemaphoreHandleTypes::none(),
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum ExternalSemaphoreHandleType {
OpaqueFd = ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_FD.as_raw(),
OpaqueWin32 = ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32.as_raw(),
OpaqueWin32Kmt = ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32_KMT.as_raw(),
D3D12Fence = ash::vk::ExternalSemaphoreHandleTypeFlags::D3D12_FENCE.as_raw(),
SyncFd = ash::vk::ExternalSemaphoreHandleTypeFlags::SYNC_FD.as_raw(),
}
impl From<ExternalSemaphoreHandleType> for ash::vk::ExternalSemaphoreHandleTypeFlags {
fn from(val: ExternalSemaphoreHandleType) -> Self {
Self::from_raw(val as u32)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct ExternalSemaphoreHandleTypes {
pub opaque_fd: bool,
pub opaque_win32: bool,
pub opaque_win32_kmt: bool,
pub d3d12_fence: bool,
pub sync_fd: bool,
}
impl ExternalSemaphoreHandleTypes {
#[inline]
pub fn none() -> ExternalSemaphoreHandleTypes {
ExternalSemaphoreHandleTypes {
opaque_fd: false,
opaque_win32: false,
opaque_win32_kmt: false,
d3d12_fence: false,
sync_fd: false,
}
}
#[inline]
pub fn posix() -> ExternalSemaphoreHandleTypes {
ExternalSemaphoreHandleTypes {
opaque_fd: true,
..ExternalSemaphoreHandleTypes::none()
}
}
}
impl From<ExternalSemaphoreHandleTypes> for ash::vk::ExternalSemaphoreHandleTypeFlags {
#[inline]
fn from(val: ExternalSemaphoreHandleTypes) -> Self {
let mut result = ash::vk::ExternalSemaphoreHandleTypeFlags::empty();
if val.opaque_fd {
result |= ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_FD;
}
if val.opaque_win32 {
result |= ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32;
}
if val.opaque_win32_kmt {
result |= ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32_KMT;
}
if val.d3d12_fence {
result |= ash::vk::ExternalSemaphoreHandleTypeFlags::D3D12_FENCE;
}
if val.sync_fd {
result |= ash::vk::ExternalSemaphoreHandleTypeFlags::SYNC_FD;
}
result
}
}
impl From<ash::vk::ExternalSemaphoreHandleTypeFlags> for ExternalSemaphoreHandleTypes {
fn from(val: ash::vk::ExternalSemaphoreHandleTypeFlags) -> Self {
Self {
opaque_fd: !(val & ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_FD).is_empty(),
opaque_win32: !(val & ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32)
.is_empty(),
opaque_win32_kmt: !(val & ash::vk::ExternalSemaphoreHandleTypeFlags::OPAQUE_WIN32_KMT)
.is_empty(),
d3d12_fence: !(val & ash::vk::ExternalSemaphoreHandleTypeFlags::D3D12_FENCE).is_empty(),
sync_fd: !(val & ash::vk::ExternalSemaphoreHandleTypeFlags::SYNC_FD).is_empty(),
}
}
}
impl BitOr for ExternalSemaphoreHandleTypes {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
ExternalSemaphoreHandleTypes {
opaque_fd: self.opaque_fd || rhs.opaque_fd,
opaque_win32: self.opaque_win32 || rhs.opaque_win32,
opaque_win32_kmt: self.opaque_win32_kmt || rhs.opaque_win32_kmt,
d3d12_fence: self.d3d12_fence || rhs.d3d12_fence,
sync_fd: self.sync_fd || rhs.sync_fd,
}
}
}
#[derive(Clone, Debug)]
pub struct ExternalSemaphoreInfo {
pub handle_type: ExternalSemaphoreHandleType,
pub _ne: crate::NonExhaustive,
}
impl ExternalSemaphoreInfo {
#[inline]
pub fn handle_type(handle_type: ExternalSemaphoreHandleType) -> Self {
Self {
handle_type,
_ne: crate::NonExhaustive(()),
}
}
}
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct ExternalSemaphoreProperties {
pub exportable: bool,
pub importable: bool,
pub export_from_imported_handle_types: ExternalSemaphoreHandleTypes,
pub compatible_handle_types: ExternalSemaphoreHandleTypes,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SemaphoreExportError {
OomError(OomError),
HandleTypeNotSupported {
handle_type: ExternalSemaphoreHandleType,
},
}
impl fmt::Display for SemaphoreExportError {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match *self {
Self::OomError(_) => write!(fmt, "not enough memory available"),
Self::HandleTypeNotSupported { handle_type } => write!(
fmt,
"the requested export handle type ({:?}) was not provided in `export_handle_types` when creating the semaphore",
handle_type,
),
}
}
}
impl From<Error> for SemaphoreExportError {
#[inline]
fn from(err: Error) -> Self {
match err {
e @ Error::OutOfHostMemory | e @ Error::OutOfDeviceMemory => Self::OomError(e.into()),
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl std::error::Error for SemaphoreExportError {
#[inline]
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl From<OomError> for SemaphoreExportError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
#[cfg(test)]
mod tests {
use crate::device::physical::PhysicalDevice;
use crate::device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo};
use crate::instance::{Instance, InstanceCreateInfo, InstanceExtensions};
use crate::sync::{ExternalSemaphoreHandleTypes, Semaphore, SemaphoreCreateInfo};
use crate::VulkanObject;
#[test]
fn semaphore_create() {
let (device, _) = gfx_dev_and_queue!();
let _ = Semaphore::new(device.clone(), Default::default());
}
#[test]
fn semaphore_pool() {
let (device, _) = gfx_dev_and_queue!();
assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0);
let sem1_internal_obj = {
let sem = Semaphore::from_pool(device.clone()).unwrap();
assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0);
sem.internal_object()
};
assert_eq!(device.semaphore_pool().lock().unwrap().len(), 1);
let sem2 = Semaphore::from_pool(device.clone()).unwrap();
assert_eq!(device.semaphore_pool().lock().unwrap().len(), 0);
assert_eq!(sem2.internal_object(), sem1_internal_obj);
}
#[test]
fn semaphore_export() {
let instance = match Instance::new(InstanceCreateInfo {
enabled_extensions: InstanceExtensions {
khr_get_physical_device_properties2: true,
khr_external_semaphore_capabilities: true,
..InstanceExtensions::none()
},
..Default::default()
}) {
Ok(x) => x,
Err(_) => return,
};
let physical_device = PhysicalDevice::enumerate(&instance).next().unwrap();
let queue_family = physical_device.queue_families().next().unwrap();
let (device, _) = match Device::new(
physical_device,
DeviceCreateInfo {
enabled_extensions: DeviceExtensions {
khr_external_semaphore: true,
khr_external_semaphore_fd: true,
..DeviceExtensions::none()
},
queue_create_infos: vec![QueueCreateInfo::family(queue_family)],
..Default::default()
},
) {
Ok(x) => x,
Err(_) => return,
};
let sem = Semaphore::new(
device.clone(),
SemaphoreCreateInfo {
export_handle_types: ExternalSemaphoreHandleTypes::posix(),
..Default::default()
},
)
.unwrap();
let fd = unsafe { sem.export_opaque_fd().unwrap() };
}
}