Add khr_external_fence and remaining parts of khr_external_semaphore (#1972)

This commit is contained in:
Rua 2022-09-13 11:07:10 +02:00 committed by GitHub
parent 00c006aafa
commit e99ad4fce5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 645 additions and 243 deletions

View File

@ -43,8 +43,8 @@ mod linux {
sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo},
swapchain::{AcquireError, Swapchain, SwapchainCreateInfo, SwapchainCreationError},
sync::{
now, ExternalSemaphoreHandleTypes, FlushError, GpuFuture, PipelineStages, Semaphore,
SemaphoreCreateInfo,
now, ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, FlushError, GpuFuture,
PipelineStages, Semaphore, SemaphoreCreateInfo,
},
VulkanLibrary,
};
@ -146,8 +146,16 @@ mod linux {
.unwrap(),
);
let acquire_fd = unsafe { acquire_sem.export_opaque_fd().unwrap() };
let release_fd = unsafe { release_sem.export_opaque_fd().unwrap() };
let acquire_fd = unsafe {
acquire_sem
.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
.unwrap()
};
let release_fd = unsafe {
release_sem
.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
.unwrap()
};
let barrier_clone = barrier.clone();
let barrier_2_clone = barrier_2.clone();

View File

@ -23,7 +23,10 @@ use crate::{
ColorSpace, FullScreenExclusive, PresentMode, SupportedSurfaceTransforms, Surface,
SurfaceApi, SurfaceCapabilities, SurfaceInfo,
},
sync::{ExternalSemaphoreInfo, ExternalSemaphoreProperties},
sync::{
ExternalFenceInfo, ExternalFenceProperties, ExternalSemaphoreInfo,
ExternalSemaphoreProperties,
},
OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use bytemuck::cast_slice;
@ -499,6 +502,107 @@ impl PhysicalDevice {
}
}
/// Retrieves the external handle properties supported for fences with a given
/// configuration.
///
/// The instance API version must be at least 1.1, or the
/// [`khr_external_fence_capabilities`](crate::instance::InstanceExtensions::khr_external_fence_capabilities)
/// extension must be enabled on the instance.
#[inline]
pub fn external_fence_properties(
&self,
info: ExternalFenceInfo,
) -> Result<ExternalFenceProperties, ExternalFenceSemaphorePropertiesError> {
self.validate_external_fence_properties(&info)?;
unsafe { Ok(self.external_fence_properties_unchecked(info)) }
}
fn validate_external_fence_properties(
&self,
info: &ExternalFenceInfo,
) -> Result<(), ExternalFenceSemaphorePropertiesError> {
if !(self.instance.api_version() >= Version::V1_1
|| self
.instance
.enabled_extensions()
.khr_external_fence_capabilities)
{
return Err(ExternalFenceSemaphorePropertiesError::RequirementNotMet {
required_for: "`external_fence_properties`",
requires_one_of: RequiresOneOf {
api_version: Some(Version::V1_1),
instance_extensions: &["khr_external_fence_capabilities"],
..Default::default()
},
});
}
let &ExternalFenceInfo {
handle_type,
_ne: _,
} = info;
// VUID-VkPhysicalDeviceExternalFenceInfo-handleType-parameter
handle_type.validate_physical_device(self)?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn external_fence_properties_unchecked(
&self,
info: ExternalFenceInfo,
) -> ExternalFenceProperties {
/* Input */
let ExternalFenceInfo {
handle_type,
_ne: _,
} = info;
let external_fence_info = ash::vk::PhysicalDeviceExternalFenceInfo {
handle_type: handle_type.into(),
..Default::default()
};
/* Output */
let mut external_fence_properties = ash::vk::ExternalFenceProperties::default();
/* Call */
let fns = self.instance.fns();
if self.instance.api_version() >= Version::V1_1 {
(fns.v1_1.get_physical_device_external_fence_properties)(
self.handle,
&external_fence_info,
&mut external_fence_properties,
)
} else {
(fns.khr_external_fence_capabilities
.get_physical_device_external_fence_properties_khr)(
self.handle,
&external_fence_info,
&mut external_fence_properties,
);
}
ExternalFenceProperties {
exportable: external_fence_properties
.external_fence_features
.intersects(ash::vk::ExternalFenceFeatureFlags::EXPORTABLE),
importable: external_fence_properties
.external_fence_features
.intersects(ash::vk::ExternalFenceFeatureFlags::IMPORTABLE),
export_from_imported_handle_types: external_fence_properties
.export_from_imported_handle_types
.into(),
compatible_handle_types: external_fence_properties.compatible_handle_types.into(),
}
}
/// Retrieves the external handle properties supported for semaphores with a given
/// configuration.
///
@ -509,7 +613,7 @@ impl PhysicalDevice {
pub fn external_semaphore_properties(
&self,
info: ExternalSemaphoreInfo,
) -> Result<ExternalSemaphoreProperties, ExternalSemaphorePropertiesError> {
) -> Result<ExternalSemaphoreProperties, ExternalFenceSemaphorePropertiesError> {
self.validate_external_semaphore_properties(&info)?;
unsafe { Ok(self.external_semaphore_properties_unchecked(info)) }
@ -518,14 +622,14 @@ impl PhysicalDevice {
fn validate_external_semaphore_properties(
&self,
info: &ExternalSemaphoreInfo,
) -> Result<(), ExternalSemaphorePropertiesError> {
) -> Result<(), ExternalFenceSemaphorePropertiesError> {
if !(self.instance.api_version() >= Version::V1_1
|| self
.instance
.enabled_extensions()
.khr_external_semaphore_capabilities)
{
return Err(ExternalSemaphorePropertiesError::RequirementNotMet {
return Err(ExternalFenceSemaphorePropertiesError::RequirementNotMet {
required_for: "`external_semaphore_properties`",
requires_one_of: RequiresOneOf {
api_version: Some(Version::V1_1),
@ -1977,18 +2081,18 @@ impl From<RequirementNotMet> for ExternalBufferPropertiesError {
}
}
/// Error that can happen when retrieving properties of an external semaphore.
/// Error that can happen when retrieving properties of an external fence or semaphore.
#[derive(Clone, Debug)]
pub enum ExternalSemaphorePropertiesError {
pub enum ExternalFenceSemaphorePropertiesError {
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
}
impl Error for ExternalSemaphorePropertiesError {}
impl Error for ExternalFenceSemaphorePropertiesError {}
impl Display for ExternalSemaphorePropertiesError {
impl Display for ExternalFenceSemaphorePropertiesError {
#[inline]
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match self {
@ -2004,7 +2108,7 @@ impl Display for ExternalSemaphorePropertiesError {
}
}
impl From<RequirementNotMet> for ExternalSemaphorePropertiesError {
impl From<RequirementNotMet> for ExternalFenceSemaphorePropertiesError {
#[inline]
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {

View File

@ -28,8 +28,8 @@ use crate::{
macros::vulkan_enum,
swapchain::{SurfaceApi, SurfaceInfo, SurfaceSwapchainLock},
sync::{
AccessCheckError, AccessError, AccessFlags, Fence, FlushError, GpuFuture, PipelineStages,
Semaphore, SemaphoreCreationError, Sharing,
AccessCheckError, AccessError, AccessFlags, Fence, FenceError, FlushError, GpuFuture,
PipelineStages, Semaphore, SemaphoreError, Sharing,
},
DeviceSize, OomError, RequirementNotMet, RequiresOneOf, VulkanError, VulkanObject,
};
@ -1717,8 +1717,11 @@ pub enum AcquireError {
/// surface's new properties and recreate a new swapchain if you want to continue drawing.
OutOfDate,
/// Error during semaphore creation
SemaphoreError(SemaphoreCreationError),
/// Error during fence creation.
FenceError(FenceError),
/// Error during semaphore creation.
SemaphoreError(SemaphoreError),
}
impl Error for AcquireError {
@ -1746,14 +1749,21 @@ impl Display for AcquireError {
AcquireError::FullScreenExclusiveModeLost => {
"the swapchain no longer has full-screen exclusivity"
}
AcquireError::FenceError(_) => "error creating fence",
AcquireError::SemaphoreError(_) => "error creating semaphore",
}
)
}
}
impl From<SemaphoreCreationError> for AcquireError {
fn from(err: SemaphoreCreationError) -> Self {
impl From<FenceError> for AcquireError {
fn from(err: FenceError) -> Self {
AcquireError::FenceError(err)
}
}
impl From<SemaphoreError> for AcquireError {
fn from(err: SemaphoreError) -> Self {
AcquireError::SemaphoreError(err)
}
}

View File

@ -9,7 +9,8 @@
use crate::{
device::{Device, DeviceOwned},
OomError, VulkanError, VulkanObject,
macros::{vulkan_bitflags, vulkan_enum},
OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use smallvec::SmallVec;
use std::{
@ -35,6 +36,8 @@ pub struct Fence {
handle: ash::vk::Fence,
device: Arc<Device>,
_export_handle_types: ExternalFenceHandleTypes,
// If true, we know that the `Fence` is signaled. If false, we don't know.
// This variable exists so that we don't need to call `vkGetFenceStatus` or `vkWaitForFences`
// multiple times.
@ -47,8 +50,55 @@ pub struct Fence {
impl Fence {
/// Creates a new `Fence`.
pub fn new(device: Arc<Device>, create_info: FenceCreateInfo) -> Result<Fence, OomError> {
let FenceCreateInfo { signaled, _ne: _ } = create_info;
#[inline]
pub fn new(device: Arc<Device>, create_info: FenceCreateInfo) -> Result<Fence, FenceError> {
Self::validate_new(&device, &create_info)?;
unsafe { Ok(Self::new_unchecked(device, create_info)?) }
}
fn validate_new(device: &Device, create_info: &FenceCreateInfo) -> Result<(), FenceError> {
let FenceCreateInfo {
signaled: _,
export_handle_types,
_ne: _,
} = create_info;
if !export_handle_types.is_empty() {
if !(device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_external_fence)
{
return Err(FenceError::RequirementNotMet {
required_for: "`create_info.export_handle_types` is not empty",
requires_one_of: RequiresOneOf {
api_version: Some(Version::V1_1),
device_extensions: &["khr_external_fence"],
..Default::default()
},
});
}
// VUID-VkExportFenceCreateInfo-handleTypes-01446
export_handle_types.validate_device(device)?;
// VUID-VkExportFenceCreateInfo-handleTypes-01446
// TODO: `vkGetPhysicalDeviceExternalFenceProperties` can only be called with one
// handle type, so which one do we give it?
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
device: Arc<Device>,
create_info: FenceCreateInfo,
) -> Result<Fence, VulkanError> {
let FenceCreateInfo {
signaled,
export_handle_types,
_ne: _,
} = create_info;
let mut flags = ash::vk::FenceCreateFlags::empty();
@ -56,17 +106,30 @@ impl Fence {
flags |= ash::vk::FenceCreateFlags::SIGNALED;
}
let create_info = ash::vk::FenceCreateInfo {
let mut create_info_vk = ash::vk::FenceCreateInfo {
flags,
..Default::default()
};
let mut export_fence_create_info_vk = None;
let handle = unsafe {
if !export_handle_types.is_empty() {
let _ = export_fence_create_info_vk.insert(ash::vk::ExportFenceCreateInfo {
handle_types: export_handle_types.into(),
..Default::default()
});
}
if let Some(info) = export_fence_create_info_vk.as_mut() {
info.p_next = create_info_vk.p_next;
create_info_vk.p_next = info as *const _ as *const _;
}
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.create_fence)(
device.internal_object(),
&create_info,
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
@ -78,18 +141,43 @@ impl Fence {
Ok(Fence {
handle,
device,
_export_handle_types: export_handle_types,
is_signaled: AtomicBool::new(signaled),
must_put_in_pool: false,
})
}
/// Creates a new `Fence` from an ash-handle
/// # Safety
/// The `handle` has to be a valid vulkan object handle and
/// the `create_info` must match the info used to create said object
pub unsafe fn from_handle(
handle: ash::vk::Fence,
create_info: FenceCreateInfo,
device: Arc<Device>,
) -> Fence {
let FenceCreateInfo {
signaled,
export_handle_types,
_ne: _,
} = create_info;
Fence {
handle,
device,
_export_handle_types: export_handle_types,
is_signaled: AtomicBool::new(signaled),
must_put_in_pool: false,
}
}
/// Takes a fence from the vulkano-provided fence pool.
/// If the pool is empty, a new fence will be created.
/// Upon `drop`, the fence is put back into the pool.
///
/// For most applications, using the fence pool should be preferred,
/// in order to avoid creating new fences every frame.
pub fn from_pool(device: Arc<Device>) -> Result<Fence, OomError> {
pub fn from_pool(device: Arc<Device>) -> Result<Fence, FenceError> {
let handle = device.fence_pool().lock().pop();
let fence = match handle {
Some(handle) => {
@ -104,6 +192,7 @@ impl Fence {
Fence {
handle,
device,
_export_handle_types: ExternalFenceHandleTypes::empty(),
is_signaled: AtomicBool::new(false),
must_put_in_pool: true,
}
@ -119,25 +208,6 @@ impl Fence {
Ok(fence)
}
/// Creates a new `Fence` from an ash-handle
/// # Safety
/// The `handle` has to be a valid vulkan object handle and
/// the `create_info` must match the info used to create said object
pub unsafe fn from_handle(
handle: ash::vk::Fence,
create_info: FenceCreateInfo,
device: Arc<Device>,
) -> Fence {
let FenceCreateInfo { signaled, _ne: _ } = create_info;
Fence {
handle,
device,
is_signaled: AtomicBool::new(signaled),
must_put_in_pool: false,
}
}
/// Returns true if the fence is signaled.
#[inline]
pub fn is_signaled(&self) -> Result<bool, OomError> {
@ -164,7 +234,7 @@ impl Fence {
/// Returns `Ok` if the fence is now signaled. Returns `Err` if the timeout was reached instead.
///
/// If you pass a duration of 0, then the function will return without blocking.
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceWaitError> {
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), FenceError> {
unsafe {
if self.is_signaled.load(Ordering::Relaxed) {
return Ok(());
@ -193,7 +263,7 @@ impl Fence {
self.is_signaled.store(true, Ordering::Relaxed);
Ok(())
}
ash::vk::Result::TIMEOUT => Err(FenceWaitError::Timeout),
ash::vk::Result::TIMEOUT => Err(FenceError::Timeout),
err => Err(VulkanError::from(err).into()),
}
}
@ -204,7 +274,7 @@ impl Fence {
/// # Panic
///
/// Panics if not all fences belong to the same device.
pub fn multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceWaitError>
pub fn multi_wait<'a, I>(iter: I, timeout: Option<Duration>) -> Result<(), FenceError>
where
I: IntoIterator<Item = &'a Fence>,
{
@ -256,7 +326,7 @@ impl Fence {
match result {
ash::vk::Result::SUCCESS => Ok(()),
ash::vk::Result::TIMEOUT => Err(FenceWaitError::Timeout),
ash::vk::Result::TIMEOUT => Err(FenceError::Timeout),
err => Err(VulkanError::from(err).into()),
}
}
@ -377,6 +447,8 @@ pub struct FenceCreateInfo {
/// The default value is `false`.
pub signaled: bool,
pub export_handle_types: ExternalFenceHandleTypes,
pub _ne: crate::NonExhaustive,
}
@ -385,57 +457,189 @@ impl Default for FenceCreateInfo {
fn default() -> Self {
Self {
signaled: false,
export_handle_types: ExternalFenceHandleTypes::empty(),
_ne: crate::NonExhaustive(()),
}
}
}
/// Error that can be returned when waiting on a fence.
vulkan_enum! {
/// The handle type used for Vulkan external fence APIs.
#[non_exhaustive]
ExternalFenceHandleType = ExternalFenceHandleTypeFlags(u32);
// TODO: document
OpaqueFd = OPAQUE_FD,
// TODO: document
OpaqueWin32 = OPAQUE_WIN32,
// TODO: document
OpaqueWin32Kmt = OPAQUE_WIN32_KMT,
// TODO: document
SyncFd = SYNC_FD,
}
vulkan_bitflags! {
/// A mask of multiple external fence handle types.
#[non_exhaustive]
ExternalFenceHandleTypes = ExternalFenceHandleTypeFlags(u32);
// TODO: document
opaque_fd = OPAQUE_FD,
// TODO: document
opaque_win32 = OPAQUE_WIN32,
// TODO: document
opaque_win32_kmt = OPAQUE_WIN32_KMT,
// TODO: document
sync_fd = SYNC_FD,
}
impl From<ExternalFenceHandleType> for ExternalFenceHandleTypes {
#[inline]
fn from(val: ExternalFenceHandleType) -> Self {
let mut result = Self::empty();
match val {
ExternalFenceHandleType::OpaqueFd => result.opaque_fd = true,
ExternalFenceHandleType::OpaqueWin32 => result.opaque_win32 = true,
ExternalFenceHandleType::OpaqueWin32Kmt => result.opaque_win32_kmt = true,
ExternalFenceHandleType::SyncFd => result.sync_fd = true,
}
result
}
}
vulkan_bitflags! {
/// Additional parameters for a fence payload import.
#[non_exhaustive]
FenceImportFlags = FenceImportFlags(u32);
/// The fence payload will be imported only temporarily, regardless of the permanence of the
/// imported handle type.
temporary = TEMPORARY,
}
/// The fence configuration to query in
/// [`PhysicalDevice::external_fence_properties`](crate::device::physical::PhysicalDevice::external_fence_properties).
#[derive(Clone, Debug)]
pub struct ExternalFenceInfo {
/// The external handle type that will be used with the fence.
pub handle_type: ExternalFenceHandleType,
pub _ne: crate::NonExhaustive,
}
impl ExternalFenceInfo {
/// Returns an `ExternalFenceInfo` with the specified `handle_type`.
#[inline]
pub fn handle_type(handle_type: ExternalFenceHandleType) -> Self {
Self {
handle_type,
_ne: crate::NonExhaustive(()),
}
}
}
/// The properties for exporting or importing external handles, when a fence is created
/// with a specific configuration.
#[derive(Clone, Debug)]
#[non_exhaustive]
pub struct ExternalFenceProperties {
/// Whether a handle can be exported to an external source with the queried
/// external handle type.
pub exportable: bool,
/// Whether a handle can be imported from an external source with the queried
/// external handle type.
pub importable: bool,
/// Which external handle types can be re-exported after the queried external handle type has
/// been imported.
pub export_from_imported_handle_types: ExternalFenceHandleTypes,
/// Which external handle types can be enabled along with the queried external handle type
/// when creating the fence.
pub compatible_handle_types: ExternalFenceHandleTypes,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum FenceWaitError {
/// Not enough memory to complete the wait.
pub enum FenceError {
/// Not enough memory available.
OomError(OomError),
/// The device has been lost.
DeviceLost,
/// The specified timeout wasn't long enough.
Timeout,
/// The device has been lost.
DeviceLostError,
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
}
impl Error for FenceWaitError {
impl Error for FenceError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self {
FenceWaitError::OomError(ref err) => Some(err),
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl Display for FenceWaitError {
#[inline]
impl Display for FenceError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
write!(
f,
"{}",
match *self {
FenceWaitError::OomError(_) => "no memory available",
FenceWaitError::Timeout => "the timeout has been reached",
FenceWaitError::DeviceLostError => "the device was lost",
}
)
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::DeviceLost => write!(f, "the device was lost"),
Self::Timeout => write!(f, "the timeout has been reached"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
}
}
}
impl From<VulkanError> for FenceWaitError {
impl From<VulkanError> for FenceError {
#[inline]
fn from(err: VulkanError) -> FenceWaitError {
fn from(err: VulkanError) -> Self {
match err {
VulkanError::OutOfHostMemory => FenceWaitError::OomError(From::from(err)),
VulkanError::OutOfDeviceMemory => FenceWaitError::OomError(From::from(err)),
VulkanError::DeviceLost => FenceWaitError::DeviceLostError,
_ => panic!("Unexpected error value"),
e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => {
Self::OomError(e.into())
}
VulkanError::DeviceLost => Self::DeviceLost,
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for FenceError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl From<RequirementNotMet> for FenceError {
#[inline]
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}

View File

@ -13,7 +13,7 @@ pub use self::{
now::{now, NowFuture},
semaphore_signal::SemaphoreSignalFuture,
};
use super::{AccessFlags, FenceWaitError, PipelineStages};
use super::{AccessFlags, FenceError, PipelineStages};
use crate::{
buffer::sys::UnsafeBuffer,
command_buffer::{
@ -578,13 +578,14 @@ impl From<SubmitBindSparseError> for FlushError {
}
}
impl From<FenceWaitError> for FlushError {
impl From<FenceError> for FlushError {
#[inline]
fn from(err: FenceWaitError) -> FlushError {
fn from(err: FenceError) -> FlushError {
match err {
FenceWaitError::OomError(err) => FlushError::OomError(err),
FenceWaitError::Timeout => FlushError::Timeout,
FenceWaitError::DeviceLostError => FlushError::DeviceLost,
FenceError::OomError(err) => FlushError::OomError(err),
FenceError::Timeout => FlushError::Timeout,
FenceError::DeviceLost => FlushError::DeviceLost,
FenceError::RequirementNotMet { .. } => unreachable!(),
}
}
}

View File

@ -105,7 +105,10 @@
pub use self::{
event::{Event, EventCreateInfo},
fence::{Fence, FenceCreateInfo, FenceWaitError},
fence::{
ExternalFenceHandleType, ExternalFenceHandleTypes, ExternalFenceInfo,
ExternalFenceProperties, Fence, FenceCreateInfo, FenceError, FenceImportFlags,
},
future::{
now, AccessCheckError, AccessError, FenceSignalFuture, FlushError, GpuFuture, JoinFuture,
NowFuture, SemaphoreSignalFuture,
@ -116,7 +119,8 @@ pub use self::{
},
semaphore::{
ExternalSemaphoreHandleType, ExternalSemaphoreHandleTypes, ExternalSemaphoreInfo,
ExternalSemaphoreProperties, Semaphore, SemaphoreCreateInfo, SemaphoreCreationError,
ExternalSemaphoreProperties, Semaphore, SemaphoreCreateInfo, SemaphoreError,
SemaphoreImportFlags,
},
};
use crate::device::Queue;

View File

@ -30,18 +30,29 @@ use std::{
pub struct Semaphore {
handle: ash::vk::Semaphore,
device: Arc<Device>,
must_put_in_pool: bool,
export_handle_types: ExternalSemaphoreHandleTypes,
must_put_in_pool: bool,
}
impl Semaphore {
/// Creates a new `Semaphore`.
#[inline]
pub fn new(
device: Arc<Device>,
create_info: SemaphoreCreateInfo,
) -> Result<Semaphore, SemaphoreCreationError> {
let SemaphoreCreateInfo {
) -> Result<Semaphore, SemaphoreError> {
Self::validate_new(&device, &create_info)?;
unsafe { Ok(Self::new_unchecked(device, create_info)?) }
}
fn validate_new(
device: &Device,
create_info: &SemaphoreCreateInfo,
) -> Result<(), SemaphoreError> {
let &SemaphoreCreateInfo {
export_handle_types,
_ne: _,
} = create_info;
@ -50,7 +61,7 @@ impl Semaphore {
if !(device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_external_semaphore)
{
return Err(SemaphoreCreationError::RequirementNotMet {
return Err(SemaphoreError::RequirementNotMet {
required_for: "`create_info.export_handle_types` is not empty",
requires_one_of: RequiresOneOf {
api_version: Some(Version::V1_1),
@ -61,34 +72,50 @@ impl Semaphore {
}
// VUID-VkExportSemaphoreCreateInfo-handleTypes-parameter
export_handle_types.validate_device(&device)?;
export_handle_types.validate_device(device)?;
// VUID-VkExportSemaphoreCreateInfo-handleTypes-01124
// TODO: `vkGetPhysicalDeviceExternalSemaphoreProperties` can only be called with one
// handle type, so which one do we give it?
}
let mut create_info = ash::vk::SemaphoreCreateInfo::builder();
Ok(())
}
let mut export_semaphore_create_info = if !export_handle_types.is_empty() {
Some(ash::vk::ExportSemaphoreCreateInfo {
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn new_unchecked(
device: Arc<Device>,
create_info: SemaphoreCreateInfo,
) -> Result<Semaphore, VulkanError> {
let SemaphoreCreateInfo {
export_handle_types,
_ne: _,
} = create_info;
let mut create_info_vk = ash::vk::SemaphoreCreateInfo {
flags: ash::vk::SemaphoreCreateFlags::empty(),
..Default::default()
};
let mut export_semaphore_create_info_vk = None;
if !export_handle_types.is_empty() {
let _ = export_semaphore_create_info_vk.insert(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);
if let Some(info) = export_semaphore_create_info_vk.as_mut() {
info.p_next = create_info_vk.p_next;
create_info_vk.p_next = info as *const _ as *const _;
}
let handle = unsafe {
let handle = {
let fns = device.fns();
let mut output = MaybeUninit::uninit();
(fns.v1_0.create_semaphore)(
device.internal_object(),
&create_info.build(),
&create_info_vk,
ptr::null(),
output.as_mut_ptr(),
)
@ -100,39 +127,13 @@ impl Semaphore {
Ok(Semaphore {
device,
handle,
must_put_in_pool: false,
export_handle_types,
must_put_in_pool: false,
})
}
/// Takes a semaphore from the vulkano-provided semaphore pool.
/// If the pool is empty, a new semaphore will be allocated.
/// Upon `drop`, the semaphore is put back into the pool.
///
/// For most applications, using the pool should be preferred,
/// in order to avoid creating new semaphores every frame.
pub fn from_pool(device: Arc<Device>) -> Result<Semaphore, SemaphoreCreationError> {
let handle = device.semaphore_pool().lock().pop();
let semaphore = match handle {
Some(handle) => Semaphore {
device,
handle,
must_put_in_pool: true,
export_handle_types: ExternalSemaphoreHandleTypes::empty(),
},
None => {
// Pool is empty, alloc new semaphore
let mut semaphore = Semaphore::new(device, Default::default())?;
semaphore.must_put_in_pool = true;
semaphore
}
};
Ok(semaphore)
}
/// Creates a new `Semaphore` from an ash-handle
/// # Safety
/// The `handle` has to be a valid vulkan object handle and
@ -150,58 +151,138 @@ impl Semaphore {
Semaphore {
device,
handle,
must_put_in_pool: false,
export_handle_types,
must_put_in_pool: false,
}
}
/// Takes a semaphore from the vulkano-provided semaphore pool.
/// If the pool is empty, a new semaphore will be allocated.
/// Upon `drop`, the semaphore is put back into the pool.
///
/// For most applications, using the pool should be preferred,
/// in order to avoid creating new semaphores every frame.
pub fn from_pool(device: Arc<Device>) -> Result<Semaphore, SemaphoreError> {
let handle = device.semaphore_pool().lock().pop();
let semaphore = match handle {
Some(handle) => Semaphore {
device,
handle,
export_handle_types: ExternalSemaphoreHandleTypes::empty(),
must_put_in_pool: true,
},
None => {
// Pool is empty, alloc new semaphore
let mut semaphore = Semaphore::new(device, Default::default())?;
semaphore.must_put_in_pool = true;
semaphore
}
};
Ok(semaphore)
}
/// Exports the semaphore into a POSIX file descriptor. The caller owns the returned `File`.
///
/// # Safety
///
/// - The semaphore must not be used, or have been used, to acquire a swapchain image.
pub unsafe fn export_opaque_fd(&self) -> Result<File, SemaphoreExportError> {
let fns = self.device.fns();
#[inline]
pub unsafe fn export_fd(
&self,
handle_type: ExternalSemaphoreHandleType,
) -> Result<File, SemaphoreError> {
self.validate_export_fd(handle_type)?;
// VUID-VkSemaphoreGetFdInfoKHR-handleType-01132
if !self.export_handle_types.opaque_fd {
return Err(SemaphoreExportError::HandleTypeNotSupported {
handle_type: ExternalSemaphoreHandleType::OpaqueFd,
Ok(self.export_fd_unchecked(handle_type)?)
}
fn validate_export_fd(
&self,
handle_type: ExternalSemaphoreHandleType,
) -> Result<(), SemaphoreError> {
if !self.device.enabled_extensions().khr_external_semaphore_fd {
return Err(SemaphoreError::RequirementNotMet {
required_for: "`export_fd`",
requires_one_of: RequiresOneOf {
device_extensions: &["khr_external_semaphore_fd"],
..Default::default()
},
});
}
assert!(self.device.enabled_extensions().khr_external_semaphore);
assert!(self.device.enabled_extensions().khr_external_semaphore_fd);
// VUID-VkMemoryGetFdInfoKHR-handleType-parameter
handle_type.validate_device(&self.device)?;
// VUID-VkSemaphoreGetFdInfoKHR-handleType-01132
if !self.export_handle_types.intersects(&handle_type.into()) {
return Err(SemaphoreError::HandleTypeNotSupported { handle_type });
}
// VUID-VkSemaphoreGetFdInfoKHR-semaphore-01133
// Can't validate for swapchain.
#[cfg(not(unix))]
unreachable!("`khr_external_semaphore_fd` was somehow enabled on a non-Unix system");
// VUID-VkSemaphoreGetFdInfoKHR-handleType-01134
// TODO:
#[cfg(unix)]
{
use std::os::unix::io::FromRawFd;
// VUID-VkSemaphoreGetFdInfoKHR-handleType-01135
// TODO:
let fd = {
let info = ash::vk::SemaphoreGetFdInfoKHR {
semaphore: self.handle,
handle_type: ash::vk::ExternalSemaphoreHandleTypeFlagsKHR::OPAQUE_FD,
..Default::default()
};
let mut output = MaybeUninit::uninit();
(fns.khr_external_semaphore_fd.get_semaphore_fd_khr)(
self.device.internal_object(),
&info,
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
output.assume_init()
};
let file = File::from_raw_fd(fd);
Ok(file)
// VUID-VkSemaphoreGetFdInfoKHR-handleType-01136
if !matches!(
handle_type,
ExternalSemaphoreHandleType::OpaqueFd | ExternalSemaphoreHandleType::SyncFd
) {
return Err(SemaphoreError::HandleTypeNotSupported { handle_type });
}
// VUID-VkSemaphoreGetFdInfoKHR-handleType-03253
// TODO:
// VUID-VkSemaphoreGetFdInfoKHR-handleType-03254
// TODO:
Ok(())
}
#[cfg(not(unix))]
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn export_fd_unchecked(
&self,
_handle_type: ExternalSemaphoreHandleType,
) -> Result<File, VulkanError> {
unreachable!("`khr_external_semaphore_fd` was somehow enabled on a non-Unix system");
}
#[cfg(unix)]
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn export_fd_unchecked(
&self,
handle_type: ExternalSemaphoreHandleType,
) -> Result<File, VulkanError> {
use std::os::unix::io::FromRawFd;
let info = ash::vk::SemaphoreGetFdInfoKHR {
semaphore: self.handle,
handle_type: handle_type.into(),
..Default::default()
};
let mut output = MaybeUninit::uninit();
let fns = self.device.fns();
(fns.khr_external_semaphore_fd.get_semaphore_fd_khr)(
self.device.internal_object(),
&info,
output.as_mut_ptr(),
)
.result()
.map_err(VulkanError::from)?;
Ok(File::from_raw_fd(output.assume_init()))
}
}
@ -257,73 +338,6 @@ impl Hash for Semaphore {
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SemaphoreCreationError {
/// Not enough memory available.
OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
}
impl Error for SemaphoreCreationError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl Display for SemaphoreCreationError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
}
}
}
impl From<VulkanError> for SemaphoreCreationError {
#[inline]
fn from(err: VulkanError) -> Self {
match err {
e @ VulkanError::OutOfHostMemory | e @ VulkanError::OutOfDeviceMemory => {
Self::OomError(e.into())
}
_ => panic!("unexpected error: {:?}", err),
}
}
}
impl From<OomError> for SemaphoreCreationError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl From<RequirementNotMet> for SemaphoreCreationError {
#[inline]
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
/// Parameters to create a new `Semaphore`.
#[derive(Clone, Debug)]
pub struct SemaphoreCreateInfo {
@ -346,7 +360,7 @@ impl Default for SemaphoreCreateInfo {
}
vulkan_enum! {
/// Describes the handle type used for Vulkan external semaphore APIs.
/// The handle type used for Vulkan external semaphore APIs.
#[non_exhaustive]
ExternalSemaphoreHandleType = ExternalSemaphoreHandleTypeFlags(u32);
@ -374,7 +388,7 @@ vulkan_enum! {
}
vulkan_bitflags! {
/// A mask of multiple handle types.
/// A mask of multiple external semaphore handle types.
#[non_exhaustive]
ExternalSemaphoreHandleTypes = ExternalSemaphoreHandleTypeFlags(u32);
@ -401,6 +415,33 @@ vulkan_bitflags! {
*/
}
impl From<ExternalSemaphoreHandleType> for ExternalSemaphoreHandleTypes {
#[inline]
fn from(val: ExternalSemaphoreHandleType) -> Self {
let mut result = Self::empty();
match val {
ExternalSemaphoreHandleType::OpaqueFd => result.opaque_fd = true,
ExternalSemaphoreHandleType::OpaqueWin32 => result.opaque_win32 = true,
ExternalSemaphoreHandleType::OpaqueWin32Kmt => result.opaque_win32_kmt = true,
ExternalSemaphoreHandleType::D3D12Fence => result.d3d12_fence = true,
ExternalSemaphoreHandleType::SyncFd => result.sync_fd = true,
}
result
}
}
vulkan_bitflags! {
/// Additional parameters for a semaphore payload import.
#[non_exhaustive]
SemaphoreImportFlags = SemaphoreImportFlags(u32);
/// The semaphore payload will be imported only temporarily, regardless of the permanence of the
/// imported handle type.
temporary = TEMPORARY,
}
/// The semaphore configuration to query in
/// [`PhysicalDevice::external_semaphore_properties`](crate::device::physical::PhysicalDevice::external_semaphore_properties).
#[derive(Clone, Debug)]
@ -440,15 +481,21 @@ pub struct ExternalSemaphoreProperties {
pub export_from_imported_handle_types: ExternalSemaphoreHandleTypes,
/// Which external handle types can be enabled along with the queried external handle type
/// when creating the buffer or image.
/// when creating the semaphore.
pub compatible_handle_types: ExternalSemaphoreHandleTypes,
}
/// Error that can be returned from operations on a semaphore.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SemaphoreExportError {
pub enum SemaphoreError {
/// Not enough memory available.
OomError(OomError),
RequirementNotMet {
required_for: &'static str,
requires_one_of: RequiresOneOf,
},
/// The requested export handle type was not provided in `export_handle_types` when creating the
/// semaphore.
HandleTypeNotSupported {
@ -456,10 +503,30 @@ pub enum SemaphoreExportError {
},
}
impl Display for SemaphoreExportError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
impl Error for SemaphoreError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl Display for SemaphoreError {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match self {
Self::OomError(_) => write!(f, "not enough memory available"),
Self::RequirementNotMet {
required_for,
requires_one_of,
} => write!(
f,
"a requirement was not met for: {}; requires one of: {}",
required_for, requires_one_of,
),
Self::HandleTypeNotSupported { handle_type } => write!(
f,
"the requested export handle type ({:?}) was not provided in `export_handle_types` when creating the semaphore",
@ -469,7 +536,7 @@ impl Display for SemaphoreExportError {
}
}
impl From<VulkanError> for SemaphoreExportError {
impl From<VulkanError> for SemaphoreError {
#[inline]
fn from(err: VulkanError) -> Self {
match err {
@ -481,25 +548,26 @@ impl From<VulkanError> for SemaphoreExportError {
}
}
impl Error for SemaphoreExportError {
#[inline]
fn source(&self) -> Option<&(dyn Error + 'static)> {
match *self {
Self::OomError(ref err) => Some(err),
_ => None,
}
}
}
impl From<OomError> for SemaphoreExportError {
impl From<OomError> for SemaphoreError {
#[inline]
fn from(err: OomError) -> Self {
Self::OomError(err)
}
}
impl From<RequirementNotMet> for SemaphoreError {
#[inline]
fn from(err: RequirementNotMet) -> Self {
Self::RequirementNotMet {
required_for: err.required_for,
requires_one_of: err.requires_one_of,
}
}
}
#[cfg(test)]
mod tests {
use super::ExternalSemaphoreHandleType;
use crate::{
device::{Device, DeviceCreateInfo, DeviceExtensions, QueueCreateInfo},
instance::{Instance, InstanceCreateInfo, InstanceExtensions},
@ -587,6 +655,9 @@ mod tests {
},
)
.unwrap();
let _fd = unsafe { sem.export_opaque_fd().unwrap() };
let _fd = unsafe {
sem.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
.unwrap()
};
}
}