Partial validation of queue commands (#2373)

* Move resource tracking/locking back from Queue to futures

* Add partially validated `submit` command

* Add partially validated `present` command

* More safety docs

* Small doc change

* Add SemaphorePresentInfo

* Put safety docs in the semaphore/fence modules instead

* More fence and semaphore docs

* Re-add missing imports

* Remove state tracking from Fence

* Remove state tracking from Semaphore
This commit is contained in:
Rua 2023-10-29 01:40:28 +02:00 committed by GitHub
parent 151e5c49cb
commit def21843fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1531 additions and 1901 deletions

View File

@ -189,12 +189,16 @@ mod linux {
.unwrap(), .unwrap(),
); );
let acquire_fd = acquire_sem let acquire_fd = unsafe {
.export_fd(ExternalSemaphoreHandleType::OpaqueFd) acquire_sem
.unwrap(); .export_fd(ExternalSemaphoreHandleType::OpaqueFd)
let release_fd = release_sem .unwrap()
.export_fd(ExternalSemaphoreHandleType::OpaqueFd) };
.unwrap(); let release_fd = unsafe {
release_sem
.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
.unwrap()
};
let barrier_clone = barrier.clone(); let barrier_clone = barrier.clone();
let barrier_2_clone = barrier_2.clone(); let barrier_2_clone = barrier_2.clone();
@ -300,9 +304,9 @@ mod linux {
Event::RedrawEventsCleared => { Event::RedrawEventsCleared => {
queue queue
.with(|mut q| unsafe { .with(|mut q| unsafe {
q.submit_unchecked( q.submit(
[SubmitInfo { &[SubmitInfo {
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore( signal_semaphores: vec![SemaphoreSubmitInfo::new(
acquire_sem.clone(), acquire_sem.clone(),
)], )],
..Default::default() ..Default::default()
@ -317,9 +321,9 @@ mod linux {
queue queue
.with(|mut q| unsafe { .with(|mut q| unsafe {
q.submit_unchecked( q.submit(
[SubmitInfo { &[SubmitInfo {
wait_semaphores: vec![SemaphoreSubmitInfo::semaphore( wait_semaphores: vec![SemaphoreSubmitInfo::new(
release_sem.clone(), release_sem.clone(),
)], )],
..Default::default() ..Default::default()

View File

@ -131,6 +131,10 @@ unsafe impl<A> PrimaryCommandBufferAbstract for PrimaryAutoCommandBuffer<A>
where where
A: CommandBufferAllocator, A: CommandBufferAllocator,
{ {
fn queue_family_index(&self) -> u32 {
self.inner.queue_family_index()
}
fn usage(&self) -> CommandBufferUsage { fn usage(&self) -> CommandBufferUsage {
self.inner.usage() self.inner.usage()
} }

View File

@ -104,7 +104,7 @@
//! [`StandardCommandBufferAllocator`]: self::allocator::StandardCommandBufferAllocator //! [`StandardCommandBufferAllocator`]: self::allocator::StandardCommandBufferAllocator
//! [`CommandBufferAllocator`]: self::allocator::CommandBufferAllocator //! [`CommandBufferAllocator`]: self::allocator::CommandBufferAllocator
//! [inherit]: CommandBufferInheritanceInfo //! [inherit]: CommandBufferInheritanceInfo
//! [`build`]: CommandBufferBuilder::build //! [`build`]: AutoCommandBufferBuilder::build
//! [pipeline barriers]: CommandBufferBuilder::pipeline_barrier //! [pipeline barriers]: CommandBufferBuilder::pipeline_barrier
//! [`GpuFuture`]: crate::sync::GpuFuture //! [`GpuFuture`]: crate::sync::GpuFuture
@ -723,7 +723,7 @@ pub struct SubmitInfo {
/// The command buffers to execute. /// The command buffers to execute.
/// ///
/// The default value is empty. /// The default value is empty.
pub command_buffers: Vec<Arc<dyn PrimaryCommandBufferAbstract>>, pub command_buffers: Vec<CommandBufferSubmitInfo>,
/// The semaphores to signal after the execution of this batch of command buffer operations /// The semaphores to signal after the execution of this batch of command buffer operations
/// has completed. /// has completed.
@ -746,10 +746,95 @@ impl Default for SubmitInfo {
} }
} }
/// Parameters for a semaphore signal or wait operation in a command buffer submission. impl SubmitInfo {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref wait_semaphores,
ref command_buffers,
ref signal_semaphores,
_ne: _,
} = self;
for (index, semaphore_submit_info) in wait_semaphores.iter().enumerate() {
semaphore_submit_info
.validate(device)
.map_err(|err| err.add_context(format!("wait_semaphores[{}]", index)))?;
}
for (index, command_buffer_submit_info) in command_buffers.iter().enumerate() {
command_buffer_submit_info
.validate(device)
.map_err(|err| err.add_context(format!("command_buffers[{}]", index)))?;
}
for (index, semaphore_submit_info) in signal_semaphores.iter().enumerate() {
semaphore_submit_info
.validate(device)
.map_err(|err| err.add_context(format!("signal_semaphores[{}]", index)))?;
let &SemaphoreSubmitInfo {
semaphore: _,
stages,
_ne: _,
} = semaphore_submit_info;
if stages != PipelineStages::ALL_COMMANDS && !device.enabled_features().synchronization2
{
return Err(Box::new(ValidationError {
context: format!("signal_semaphores[{}].stages", index).into(),
problem: "is not `PipelineStages::ALL_COMMANDS`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"synchronization2",
)])]),
vuids: &["VUID-vkQueueSubmit2-synchronization2-03866"],
}));
}
}
Ok(())
}
}
/// Parameters for a command buffer in a queue submit operation.
#[derive(Clone, Debug)]
pub struct CommandBufferSubmitInfo {
/// The command buffer to execute.
///
/// There is no default value.
pub command_buffer: Arc<dyn PrimaryCommandBufferAbstract>,
pub _ne: crate::NonExhaustive,
}
impl CommandBufferSubmitInfo {
/// Returns a `CommandBufferSubmitInfo` with the specified `command_buffer`.
#[inline]
pub fn new(command_buffer: Arc<dyn PrimaryCommandBufferAbstract>) -> Self {
Self {
command_buffer,
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref command_buffer,
_ne: _,
} = self;
// VUID?
assert_eq!(device, command_buffer.device().as_ref());
Ok(())
}
}
/// Parameters for a semaphore signal or wait operation in a queue submit operation.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SemaphoreSubmitInfo { pub struct SemaphoreSubmitInfo {
/// The semaphore to signal or wait for. /// The semaphore to signal or wait for.
///
/// There is no default value.
pub semaphore: Arc<Semaphore>, pub semaphore: Arc<Semaphore>,
/// For a semaphore wait operation, specifies the pipeline stages in the second synchronization /// For a semaphore wait operation, specifies the pipeline stages in the second synchronization
@ -774,13 +859,191 @@ pub struct SemaphoreSubmitInfo {
impl SemaphoreSubmitInfo { impl SemaphoreSubmitInfo {
/// Returns a `SemaphoreSubmitInfo` with the specified `semaphore`. /// Returns a `SemaphoreSubmitInfo` with the specified `semaphore`.
#[inline] #[inline]
pub fn semaphore(semaphore: Arc<Semaphore>) -> Self { pub fn new(semaphore: Arc<Semaphore>) -> Self {
Self { Self {
semaphore, semaphore,
stages: PipelineStages::ALL_COMMANDS, stages: PipelineStages::ALL_COMMANDS,
_ne: crate::NonExhaustive(()), _ne: crate::NonExhaustive(()),
} }
} }
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref semaphore,
stages,
_ne: _,
} = self;
// VUID?
assert_eq!(device, semaphore.device().as_ref());
stages.validate_device(device).map_err(|err| {
err.add_context("stages")
.set_vuids(&["VUID-VkSemaphoreSubmitInfo-stageMask-parameter"])
})?;
if !device.enabled_features().synchronization2 && stages.contains_flags2() {
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains flags from `VkPipelineStageFlagBits2`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"synchronization2",
)])]),
..Default::default()
}));
}
if !device.enabled_features().geometry_shader
&& stages.intersects(PipelineStages::GEOMETRY_SHADER)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::GEOMETRY_SHADER`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"geometry_shader",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-03929"],
}));
}
if !device.enabled_features().tessellation_shader
&& stages.intersects(
PipelineStages::TESSELLATION_CONTROL_SHADER
| PipelineStages::TESSELLATION_EVALUATION_SHADER,
)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \
`PipelineStages::TESSELLATION_EVALUATION_SHADER`"
.into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"tessellation_shader",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-03930"],
}));
}
if !device.enabled_features().conditional_rendering
&& stages.intersects(PipelineStages::CONDITIONAL_RENDERING)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::CONDITIONAL_RENDERING`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"conditional_rendering",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-03931"],
}));
}
if !device.enabled_features().fragment_density_map
&& stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"fragment_density_map",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-03932"],
}));
}
if !device.enabled_features().transform_feedback
&& stages.intersects(PipelineStages::TRANSFORM_FEEDBACK)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::TRANSFORM_FEEDBACK`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"transform_feedback",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-03933"],
}));
}
if !device.enabled_features().mesh_shader && stages.intersects(PipelineStages::MESH_SHADER)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::MESH_SHADER`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"mesh_shader",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-03934"],
}));
}
if !device.enabled_features().task_shader && stages.intersects(PipelineStages::TASK_SHADER)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::TASK_SHADER`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"task_shader",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-03935"],
}));
}
if !(device.enabled_features().attachment_fragment_shading_rate
|| device.enabled_features().shading_rate_image)
&& stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`".into(),
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::Feature("attachment_fragment_shading_rate")]),
RequiresAllOf(&[Requires::Feature("shading_rate_image")]),
]),
vuids: &["VUID-VkMemoryBarrier2-shadingRateImage-07316"],
}));
}
if !device.enabled_features().subpass_shading
&& stages.intersects(PipelineStages::SUBPASS_SHADING)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::SUBPASS_SHADING`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"subpass_shading",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-04957"],
}));
}
if !device.enabled_features().invocation_mask
&& stages.intersects(PipelineStages::INVOCATION_MASK)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::INVOCATION_MASK`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"invocation_mask",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-04995"],
}));
}
if !(device.enabled_extensions().nv_ray_tracing
|| device.enabled_features().ray_tracing_pipeline)
&& stages.intersects(PipelineStages::RAY_TRACING_SHADER)
{
return Err(Box::new(ValidationError {
context: "stages".into(),
problem: "contains `PipelineStages::RAY_TRACING_SHADER`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"ray_tracing_pipeline",
)])]),
vuids: &["VUID-VkSemaphoreSubmitInfo-stageMask-07946"],
}));
}
Ok(())
}
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]

View File

@ -9,7 +9,8 @@
use super::{ use super::{
CommandBufferInheritanceInfo, CommandBufferResourcesUsage, CommandBufferState, CommandBufferInheritanceInfo, CommandBufferResourcesUsage, CommandBufferState,
CommandBufferUsage, SecondaryCommandBufferResourcesUsage, SemaphoreSubmitInfo, SubmitInfo, CommandBufferSubmitInfo, CommandBufferUsage, SecondaryCommandBufferResourcesUsage,
SemaphoreSubmitInfo, SubmitInfo,
}; };
use crate::{ use crate::{
buffer::Buffer, buffer::Buffer,
@ -17,7 +18,10 @@ use crate::{
image::{Image, ImageLayout}, image::{Image, ImageLayout},
swapchain::Swapchain, swapchain::Swapchain,
sync::{ sync::{
future::{now, AccessCheckError, AccessError, GpuFuture, NowFuture, SubmitAnyBuilder}, future::{
now, queue_submit, AccessCheckError, AccessError, GpuFuture, NowFuture,
SubmitAnyBuilder,
},
PipelineStages, PipelineStages,
}, },
DeviceSize, SafeDeref, Validated, ValidationError, VulkanError, VulkanObject, DeviceSize, SafeDeref, Validated, ValidationError, VulkanError, VulkanObject,
@ -38,6 +42,9 @@ use std::{
pub unsafe trait PrimaryCommandBufferAbstract: pub unsafe trait PrimaryCommandBufferAbstract:
VulkanObject<Handle = ash::vk::CommandBuffer> + DeviceOwned + Send + Sync VulkanObject<Handle = ash::vk::CommandBuffer> + DeviceOwned + Send + Sync
{ {
/// Returns the queue family index of this command buffer.
fn queue_family_index(&self) -> u32;
/// Returns the usage of this command buffer. /// Returns the usage of this command buffer.
fn usage(&self) -> CommandBufferUsage; fn usage(&self) -> CommandBufferUsage;
@ -143,6 +150,10 @@ where
T: VulkanObject<Handle = ash::vk::CommandBuffer> + SafeDeref + Send + Sync, T: VulkanObject<Handle = ash::vk::CommandBuffer> + SafeDeref + Send + Sync,
T::Target: PrimaryCommandBufferAbstract, T::Target: PrimaryCommandBufferAbstract,
{ {
fn queue_family_index(&self) -> u32 {
(**self).queue_family_index()
}
fn usage(&self) -> CommandBufferUsage { fn usage(&self) -> CommandBufferUsage {
(**self).usage() (**self).usage()
} }
@ -237,7 +248,9 @@ where
Ok(match self.previous.build_submission()? { Ok(match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => SubmitAnyBuilder::CommandBuffer( SubmitAnyBuilder::Empty => SubmitAnyBuilder::CommandBuffer(
SubmitInfo { SubmitInfo {
command_buffers: vec![self.command_buffer.clone()], command_buffers: vec![CommandBufferSubmitInfo::new(
self.command_buffer.clone(),
)],
..Default::default() ..Default::default()
}, },
None, None,
@ -251,11 +264,13 @@ where
SemaphoreSubmitInfo { SemaphoreSubmitInfo {
// TODO: correct stages ; hard // TODO: correct stages ; hard
stages: PipelineStages::ALL_COMMANDS, stages: PipelineStages::ALL_COMMANDS,
..SemaphoreSubmitInfo::semaphore(semaphore) ..SemaphoreSubmitInfo::new(semaphore)
} }
}) })
.collect(), .collect(),
command_buffers: vec![self.command_buffer.clone()], command_buffers: vec![CommandBufferSubmitInfo::new(
self.command_buffer.clone(),
)],
..Default::default() ..Default::default()
}, },
None, None,
@ -265,7 +280,7 @@ where
// FIXME: add pipeline barrier // FIXME: add pipeline barrier
submit_info submit_info
.command_buffers .command_buffers
.push(self.command_buffer.clone()); .push(CommandBufferSubmitInfo::new(self.command_buffer.clone()));
SubmitAnyBuilder::CommandBuffer(submit_info, fence) SubmitAnyBuilder::CommandBuffer(submit_info, fence)
} }
SubmitAnyBuilder::QueuePresent(_) | SubmitAnyBuilder::BindSparse(_, _) => { SubmitAnyBuilder::QueuePresent(_) | SubmitAnyBuilder::BindSparse(_, _) => {
@ -305,9 +320,7 @@ where
match self.build_submission_impl()? { match self.build_submission_impl()? {
SubmitAnyBuilder::Empty => {} SubmitAnyBuilder::Empty => {}
SubmitAnyBuilder::CommandBuffer(submit_info, fence) => { SubmitAnyBuilder::CommandBuffer(submit_info, fence) => {
self.queue.with(|mut q| { queue_submit(&self.queue, submit_info, fence, &self.previous).unwrap();
q.submit_with_future(submit_info, fence, &self.previous, &self.queue)
})?;
} }
_ => unreachable!(), _ => unreachable!(),
}; };
@ -319,7 +332,36 @@ where
} }
unsafe fn signal_finished(&self) { unsafe fn signal_finished(&self) {
self.finished.store(true, Ordering::SeqCst); if !self.finished.swap(true, Ordering::SeqCst) {
let resource_usage = self.command_buffer.resources_usage();
for usage in &resource_usage.buffers {
let mut state = usage.buffer.state();
for (range, range_usage) in usage.ranges.iter() {
if range_usage.mutable {
state.gpu_write_unlock(range.clone());
} else {
state.gpu_read_unlock(range.clone());
}
}
}
for usage in &resource_usage.images {
let mut state = usage.image.state();
for (range, range_usage) in usage.ranges.iter() {
if range_usage.mutable {
state.gpu_write_unlock(range.clone());
} else {
state.gpu_read_unlock(range.clone());
}
}
}
self.command_buffer.state().set_submit_finished();
}
self.previous.signal_finished(); self.previous.signal_finished();
} }
@ -445,7 +487,10 @@ where
self.flush().unwrap(); self.flush().unwrap();
// Block until the queue finished. // Block until the queue finished.
self.queue.with(|mut q| q.wait_idle()).unwrap(); self.queue.with(|mut q| q.wait_idle()).unwrap();
unsafe { self.previous.signal_finished() };
unsafe {
self.signal_finished();
}
} }
} }
} }

View File

@ -75,6 +75,7 @@
//! [`DescriptorSet`]. It is what you pass to the draw functions. //! [`DescriptorSet`]. It is what you pass to the draw functions.
//! //!
//! [`DescriptorPool`]: pool::DescriptorPool //! [`DescriptorPool`]: pool::DescriptorPool
//! [`UnsafeDescriptorSet`]: sys::UnsafeDescriptorSet
//! [`DescriptorSetAllocator`]: allocator::DescriptorSetAllocator //! [`DescriptorSetAllocator`]: allocator::DescriptorSetAllocator
//! [`StandardDescriptorSetAllocator`]: allocator::StandardDescriptorSetAllocator //! [`StandardDescriptorSetAllocator`]: allocator::StandardDescriptorSetAllocator

File diff suppressed because it is too large Load Diff

View File

@ -14,10 +14,11 @@ use crate::{
image::{Image, ImageLayout}, image::{Image, ImageLayout},
sync::{ sync::{
fence::Fence, fence::Fence,
future::{AccessCheckError, AccessError, GpuFuture, SubmitAnyBuilder}, future::{queue_present, AccessCheckError, AccessError, GpuFuture, SubmitAnyBuilder},
semaphore::Semaphore, semaphore::Semaphore,
}, },
DeviceSize, Validated, ValidationError, VulkanError, VulkanObject, DeviceSize, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError,
VulkanObject,
}; };
use smallvec::smallvec; use smallvec::smallvec;
use std::{ use std::{
@ -145,16 +146,6 @@ pub fn acquire_next_image(
})? })?
}; };
unsafe {
let mut state = semaphore.state();
state.swapchain_acquire();
}
unsafe {
let mut state = fence.state();
state.import_swapchain_acquire();
}
Ok(( Ok((
image_index, image_index,
is_suboptimal, is_suboptimal,
@ -216,16 +207,6 @@ pub unsafe fn acquire_next_image_raw(
err => return Err(VulkanError::from(err).into()), err => return Err(VulkanError::from(err).into()),
}; };
if let Some(semaphore) = semaphore {
let mut state = semaphore.state();
state.swapchain_acquire();
}
if let Some(fence) = fence {
let mut state = fence.state();
state.import_swapchain_acquire();
}
Ok(AcquiredImage { Ok(AcquiredImage {
image_index: out.assume_init(), image_index: out.assume_init(),
is_suboptimal, is_suboptimal,
@ -431,12 +412,12 @@ pub struct PresentInfo {
/// The semaphores to wait for before beginning the execution of the present operations. /// The semaphores to wait for before beginning the execution of the present operations.
/// ///
/// The default value is empty. /// The default value is empty.
pub wait_semaphores: Vec<Arc<Semaphore>>, pub wait_semaphores: Vec<SemaphorePresentInfo>,
/// The present operations to perform. /// The present operations to perform.
/// ///
/// The default value is empty. /// The default value is empty.
pub swapchain_infos: Vec<SwapchainPresentInfo>, pub swapchains: Vec<SwapchainPresentInfo>,
pub _ne: crate::NonExhaustive, pub _ne: crate::NonExhaustive,
} }
@ -446,12 +427,79 @@ impl Default for PresentInfo {
fn default() -> Self { fn default() -> Self {
Self { Self {
wait_semaphores: Vec::new(), wait_semaphores: Vec::new(),
swapchain_infos: Vec::new(), swapchains: Vec::new(),
_ne: crate::NonExhaustive(()), _ne: crate::NonExhaustive(()),
} }
} }
} }
impl PresentInfo {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref wait_semaphores,
swapchains: ref swapchain_infos,
_ne: _,
} = self;
for (index, semaphore_present_info) in wait_semaphores.iter().enumerate() {
semaphore_present_info
.validate(device)
.map_err(|err| err.add_context(format!("wait_semaphores[{}]", index)))?;
}
assert!(!swapchain_infos.is_empty());
let has_present_mode = swapchain_infos
.first()
.map_or(false, |first| first.present_mode.is_some());
for (index, swapchain_info) in swapchain_infos.iter().enumerate() {
swapchain_info
.validate(device)
.map_err(|err| err.add_context(format!("swapchain_infos[{}]", index)))?;
let &SwapchainPresentInfo {
swapchain: _,
image_index: _,
present_id: _,
present_mode,
present_regions: _,
_ne: _,
} = swapchain_info;
if has_present_mode {
if present_mode.is_none() {
return Err(Box::new(ValidationError {
problem: format!(
"`swapchain_infos[0].present_mode` is `Some`, but \
`swapchain_infos[{}].present_mode` is not also `Some`",
index
)
.into(),
vuids: &["VUID-VkPresentInfoKHR-pSwapchains-09199"],
..Default::default()
}));
}
} else {
if present_mode.is_some() {
return Err(Box::new(ValidationError {
problem: format!(
"`swapchain_infos[0].present_mode` is `None`, but \
`swapchain_infos[{}].present_mode` is not also `None`",
index
)
.into(),
vuids: &["VUID-VkPresentInfoKHR-pSwapchains-09199"],
..Default::default()
}));
}
}
}
Ok(())
}
}
/// Parameters for a single present operation on a swapchain. /// Parameters for a single present operation on a swapchain.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct SwapchainPresentInfo { pub struct SwapchainPresentInfo {
@ -530,6 +578,113 @@ impl SwapchainPresentInfo {
_ne: crate::NonExhaustive(()), _ne: crate::NonExhaustive(()),
} }
} }
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref swapchain,
image_index,
present_id,
present_mode,
ref present_regions,
_ne: _,
} = self;
// VUID-VkPresentInfoKHR-commonparent
assert_eq!(device, swapchain.device().as_ref());
if image_index >= swapchain.image_count() {
return Err(Box::new(ValidationError {
problem: "`image_index` is not less than `swapchain.image_count()`".into(),
vuids: &["VUID-VkPresentInfoKHR-pImageIndices-01430"],
..Default::default()
}));
}
if present_id.is_some() && !device.enabled_features().present_id {
return Err(Box::new(ValidationError {
context: "present_id".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature(
"present_id",
)])]),
vuids: &["VUID-VkPresentInfoKHR-pNext-06235"],
}));
}
if let Some(present_mode) = present_mode {
if !swapchain.present_modes().contains(&present_mode) {
return Err(Box::new(ValidationError {
problem: "`swapchain.present_modes()` does not contain `present_mode`".into(),
vuids: &["VUID-VkSwapchainPresentModeInfoEXT-pPresentModes-07761"],
..Default::default()
}));
}
}
if !present_regions.is_empty() && !device.enabled_extensions().khr_incremental_present {
return Err(Box::new(ValidationError {
context: "present_regions".into(),
problem: "is not empty".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"khr_incremental_present",
)])]),
..Default::default()
}));
}
for (index, rectangle_layer) in present_regions.iter().enumerate() {
let &RectangleLayer {
offset,
extent,
layer,
} = rectangle_layer;
if offset[0] + extent[0] > swapchain.image_extent()[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`present_regions[{0}].offset[0]` + `present_regions[{0}].extent[0]` is \
greater than `swapchain.image_extent()[0]`",
index
)
.into(),
vuids: &["VUID-VkRectLayerKHR-offset-04864"],
..Default::default()
}));
}
if offset[1] + extent[1] > swapchain.image_extent()[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`present_regions[{0}].offset[1]` + `present_regions[{0}].extent[1]` is \
greater than `swapchain.image_extent()[1]`",
index
)
.into(),
vuids: &["VUID-VkRectLayerKHR-offset-04864"],
..Default::default()
}));
}
if layer >= swapchain.image_array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`present_regions[{0}].layer` is greater than \
`swapchain.image_array_layers()`",
index
)
.into(),
vuids: &["VUID-VkRectLayerKHR-layer-01262"],
..Default::default()
}));
}
}
// unsafe
// VUID-VkPresentInfoKHR-pImageIndices-01430
// VUID-VkPresentIdKHR-presentIds-04999
Ok(())
}
} }
/// Represents a rectangular region on an image layer. /// Represents a rectangular region on an image layer.
@ -572,6 +727,40 @@ impl From<&RectangleLayer> for ash::vk::RectLayerKHR {
} }
} }
/// Parameters for a semaphore wait operation in a queue present operation.
#[derive(Clone, Debug)]
pub struct SemaphorePresentInfo {
/// The semaphore to wait for.
///
/// There is no default value.
pub semaphore: Arc<Semaphore>,
pub _ne: crate::NonExhaustive,
}
impl SemaphorePresentInfo {
/// Returns a `SemaphorePresentInfo` with the specified `semaphore`.
#[inline]
pub fn new(semaphore: Arc<Semaphore>) -> Self {
Self {
semaphore,
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref semaphore,
_ne: _,
} = self;
// VUID-VkPresentInfoKHR-commonparent
assert_eq!(device, semaphore.device().as_ref());
Ok(())
}
}
/// Represents a swapchain image being presented on the screen. /// Represents a swapchain image being presented on the screen.
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"] #[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
pub struct PresentFuture<P> pub struct PresentFuture<P>
@ -640,13 +829,16 @@ where
Ok(match self.previous.build_submission()? { Ok(match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo { SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()], swapchains: vec![self.swapchain_info.clone()],
..Default::default() ..Default::default()
}), }),
SubmitAnyBuilder::SemaphoresWait(semaphores) => { SubmitAnyBuilder::SemaphoresWait(semaphores) => {
SubmitAnyBuilder::QueuePresent(PresentInfo { SubmitAnyBuilder::QueuePresent(PresentInfo {
wait_semaphores: semaphores.into_iter().collect(), wait_semaphores: semaphores
swapchain_infos: vec![self.swapchain_info.clone()], .into_iter()
.map(SemaphorePresentInfo::new)
.collect(),
swapchains: vec![self.swapchain_info.clone()],
..Default::default() ..Default::default()
}) })
} }
@ -654,7 +846,7 @@ where
self.previous.flush()?; self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo { SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()], swapchains: vec![self.swapchain_info.clone()],
..Default::default() ..Default::default()
}) })
} }
@ -662,26 +854,24 @@ where
self.previous.flush()?; self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo { SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()], swapchains: vec![self.swapchain_info.clone()],
..Default::default() ..Default::default()
}) })
} }
SubmitAnyBuilder::QueuePresent(mut present_info) => { SubmitAnyBuilder::QueuePresent(mut present_info) => {
if present_info.swapchain_infos.first().map_or(false, |prev| { if present_info.swapchains.first().map_or(false, |prev| {
prev.present_mode.is_some() != self.swapchain_info.present_mode.is_some() prev.present_mode.is_some() != self.swapchain_info.present_mode.is_some()
}) { }) {
// If the present mode Option variants don't match, create a new command. // If the present mode Option variants don't match, create a new command.
self.previous.flush()?; self.previous.flush()?;
SubmitAnyBuilder::QueuePresent(PresentInfo { SubmitAnyBuilder::QueuePresent(PresentInfo {
swapchain_infos: vec![self.swapchain_info.clone()], swapchains: vec![self.swapchain_info.clone()],
..Default::default() ..Default::default()
}) })
} else { } else {
// Otherwise, add our swapchain to the previous. // Otherwise, add our swapchain to the previous.
present_info present_info.swapchains.push(self.swapchain_info.clone());
.swapchain_infos
.push(self.swapchain_info.clone());
SubmitAnyBuilder::QueuePresent(present_info) SubmitAnyBuilder::QueuePresent(present_info)
} }
@ -701,21 +891,17 @@ where
SubmitAnyBuilder::QueuePresent(present_info) => { SubmitAnyBuilder::QueuePresent(present_info) => {
let PresentInfo { let PresentInfo {
wait_semaphores: _, wait_semaphores: _,
swapchain_infos, swapchains,
_ne: _, _ne: _,
} = &present_info; } = &present_info;
let has_present_mode = swapchain_infos for swapchain_info in swapchains {
.first()
.map_or(false, |first| first.present_mode.is_some());
for swapchain_info in swapchain_infos {
let &SwapchainPresentInfo { let &SwapchainPresentInfo {
ref swapchain, ref swapchain,
image_index: _, image_index: _,
present_id, present_id,
present_regions: _, present_regions: _,
present_mode, present_mode: _,
_ne: _, _ne: _,
} = swapchain_info; } = swapchain_info;
@ -731,25 +917,6 @@ where
}) })
.into()); .into());
} }
if let Some(present_mode) = present_mode {
assert!(has_present_mode);
if !swapchain.present_modes().contains(&present_mode) {
return Err(Box::new(ValidationError {
problem: "the requested present mode is not one of the modes \
in `swapchain.present_modes()`"
.into(),
vuids: &[
"VUID-VkSwapchainPresentModeInfoEXT-pPresentModes-07761",
],
..Default::default()
})
.into());
}
} else {
assert!(!has_present_mode);
}
} }
match self.previous.check_swapchain_image_acquired( match self.previous.check_swapchain_image_acquired(
@ -769,9 +936,7 @@ where
} }
} }
Ok(self Ok(queue_present(&self.queue, present_info)?
.queue
.with(|mut q| q.present_unchecked(present_info))?
.map(|r| r.map(|_| ())) .map(|r| r.map(|_| ()))
.fold(Ok(()), Result::and)?) .fold(Ok(()), Result::and)?)
} }

View File

@ -9,15 +9,38 @@
//! A fence provides synchronization between the device and the host, or between an external source //! A fence provides synchronization between the device and the host, or between an external source
//! and the host. //! and the host.
//!
//! A fence has two states: **signaled** and **unsignaled**.
//!
//! The device can only perform one operation on a fence:
//! - A **fence signal operation** will put the fence into the signaled state.
//!
//! The host can poll a fence's status, wait for it to become signaled, or reset the fence back
//! to the unsignaled state.
//!
//! # Queue-to-host synchronization
//!
//! The primary use of a fence is to know when a queue operation has completed executing.
//! When adding a command to a queue, a fence can be provided with the command, to be signaled
//! when the operation finishes. You can check for a fence's current status by calling
//! `is_signaled`, `wait` or `await` on it. If the fence is found to be signaled, that means that
//! the queue has completed the operation that is associated with the fence, and all operations
//! that happened-before it have been completed as well.
//!
//! # Safety
//!
//! - There must never be more than one fence signal operation queued at any given time.
//! - The fence must be unsignaled at the time the function (for example [`submit`]) is called.
//!
//! [`submit`]: crate::device::QueueGuard::submit
use crate::{ use crate::{
device::{physical::PhysicalDevice, Device, DeviceOwned, Queue}, device::{physical::PhysicalDevice, Device, DeviceOwned},
instance::InstanceOwnedDebugWrapper, instance::InstanceOwnedDebugWrapper,
macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum}, macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum},
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
VulkanObject, VulkanObject,
}; };
use parking_lot::{Mutex, MutexGuard};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fs::File; use std::fs::File;
use std::{ use std::{
@ -26,32 +49,12 @@ use std::{
num::NonZeroU64, num::NonZeroU64,
pin::Pin, pin::Pin,
ptr, ptr,
sync::{Arc, Weak}, sync::Arc,
task::{Context, Poll}, task::{Context, Poll},
time::Duration, time::Duration,
}; };
/// A two-state synchronization primitive that is signalled by the device and waited on by the host. /// A two-state synchronization primitive that is signalled by the device and waited on by the host.
///
/// # Queue-to-host synchronization
///
/// The primary use of a fence is to know when a queue operation has completed executing.
/// When adding a command to a queue, a fence can be provided with the command, to be signaled
/// when the operation finishes. You can check for a fence's current status by calling
/// `is_signaled`, `wait` or `await` on it. If the fence is found to be signaled, that means that
/// the queue has completed the operation that is associated with the fence, and all operations that
/// were submitted before it have been completed as well.
///
/// When a queue command accesses a resource, it must be kept alive until the queue command has
/// finished executing, and you may not be allowed to perform certain other operations (or even any)
/// while the resource is in use. By calling `is_signaled`, `wait` or `await`, the queue will be
/// notified when the fence is signaled, so that all resources of the associated queue operation and
/// preceding operations can be released.
///
/// Because of this, it is highly recommended to call `is_signaled`, `wait` or `await` on your fences.
/// Otherwise, the queue will hold onto resources indefinitely (using up memory)
/// and resource locks will not be released, which may cause errors when submitting future
/// queue operations.
#[derive(Debug)] #[derive(Debug)]
pub struct Fence { pub struct Fence {
handle: ash::vk::Fence, handle: ash::vk::Fence,
@ -62,7 +65,6 @@ pub struct Fence {
export_handle_types: ExternalFenceHandleTypes, export_handle_types: ExternalFenceHandleTypes,
must_put_in_pool: bool, must_put_in_pool: bool,
state: Mutex<FenceState>,
} }
impl Fence { impl Fence {
@ -141,10 +143,6 @@ impl Fence {
export_handle_types, export_handle_types,
must_put_in_pool: false, must_put_in_pool: false,
state: Mutex::new(FenceState {
is_signaled: flags.intersects(FenceCreateFlags::SIGNALED),
..Default::default()
}),
}) })
} }
@ -176,7 +174,6 @@ impl Fence {
export_handle_types: ExternalFenceHandleTypes::empty(), export_handle_types: ExternalFenceHandleTypes::empty(),
must_put_in_pool: true, must_put_in_pool: true,
state: Mutex::new(Default::default()),
} }
} }
None => { None => {
@ -218,10 +215,6 @@ impl Fence {
export_handle_types, export_handle_types,
must_put_in_pool: false, must_put_in_pool: false,
state: Mutex::new(FenceState {
is_signaled: flags.intersects(FenceCreateFlags::SIGNALED),
..Default::default()
}),
} }
} }
@ -240,84 +233,44 @@ impl Fence {
/// Returns true if the fence is signaled. /// Returns true if the fence is signaled.
#[inline] #[inline]
pub fn is_signaled(&self) -> Result<bool, VulkanError> { pub fn is_signaled(&self) -> Result<bool, VulkanError> {
let queue_to_signal = { let result = unsafe {
let mut state = self.state(); let fns = self.device.fns();
(fns.v1_0.get_fence_status)(self.device.handle(), self.handle)
// If the fence is already signaled, or it's unsignaled but there's no queue that
// could signal it, return the currently known value.
if let Some(is_signaled) = state.is_signaled() {
return Ok(is_signaled);
}
// We must ask Vulkan for the state.
let result = unsafe {
let fns = self.device.fns();
(fns.v1_0.get_fence_status)(self.device.handle(), self.handle)
};
match result {
ash::vk::Result::SUCCESS => unsafe { state.set_signaled() },
ash::vk::Result::NOT_READY => return Ok(false),
err => return Err(VulkanError::from(err)),
}
}; };
// If we have a queue that we need to signal our status to, match result {
// do so now after the state lock is dropped, to avoid deadlocks. ash::vk::Result::SUCCESS => Ok(true),
if let Some(queue) = queue_to_signal { ash::vk::Result::NOT_READY => Ok(false),
unsafe { err => Err(VulkanError::from(err)),
queue.with(|mut q| q.fence_signaled(self));
}
} }
Ok(true)
} }
/// Waits until the fence is signaled, or at least until the timeout duration has elapsed. /// Waits until the fence is signaled, or at least until the timeout duration has elapsed.
/// ///
/// If you pass a duration of 0, then the function will return without blocking. /// If you pass a duration of 0, then the function will return without blocking.
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), VulkanError> { pub fn wait(&self, timeout: Option<Duration>) -> Result<(), VulkanError> {
let queue_to_signal = { let timeout_ns = timeout.map_or(u64::MAX, |timeout| {
let mut state = self.state.lock(); timeout
.as_secs()
.saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
});
// If the fence is already signaled, we don't need to wait. let result = unsafe {
if state.is_signaled().unwrap_or(false) { let fns = self.device.fns();
return Ok(()); (fns.v1_0.wait_for_fences)(
} self.device.handle(),
1,
let timeout_ns = timeout.map_or(u64::MAX, |timeout| { &self.handle,
timeout ash::vk::TRUE,
.as_secs() timeout_ns,
.saturating_mul(1_000_000_000) )
.saturating_add(timeout.subsec_nanos() as u64)
});
let result = unsafe {
let fns = self.device.fns();
(fns.v1_0.wait_for_fences)(
self.device.handle(),
1,
&self.handle,
ash::vk::TRUE,
timeout_ns,
)
};
match result {
ash::vk::Result::SUCCESS => unsafe { state.set_signaled() },
err => return Err(VulkanError::from(err)),
}
}; };
// If we have a queue that we need to signal our status to, match result {
// do so now after the state lock is dropped, to avoid deadlocks. ash::vk::Result::SUCCESS => Ok(()),
if let Some(queue) = queue_to_signal { err => Err(VulkanError::from(err)),
unsafe {
queue.with(|mut q| q.fence_signaled(self));
}
} }
Ok(())
} }
/// Waits for multiple fences at once. /// Waits for multiple fences at once.
@ -358,153 +311,96 @@ impl Fence {
fences: impl IntoIterator<Item = &'a Fence>, fences: impl IntoIterator<Item = &'a Fence>,
timeout: Option<Duration>, timeout: Option<Duration>,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let queues_to_signal: SmallVec<[_; 8]> = { let iter = fences.into_iter();
let iter = fences.into_iter(); let mut fences_vk: SmallVec<[_; 8]> = SmallVec::new();
let mut fences_vk: SmallVec<[_; 8]> = SmallVec::new(); let mut fences: SmallVec<[_; 8]> = SmallVec::new();
let mut fences: SmallVec<[_; 8]> = SmallVec::new();
let mut states: SmallVec<[_; 8]> = SmallVec::new();
for fence in iter { for fence in iter {
let state = fence.state.lock(); fences_vk.push(fence.handle);
fences.push(fence);
// Skip the fences that are already signaled.
if !state.is_signaled().unwrap_or(false) {
fences_vk.push(fence.handle);
fences.push(fence);
states.push(state);
}
}
// VUID-vkWaitForFences-fenceCount-arraylength
// If there are no fences, or all the fences are signaled, we don't need to wait.
if fences_vk.is_empty() {
return Ok(());
}
let device = &fences[0].device;
let timeout_ns = timeout.map_or(u64::MAX, |timeout| {
timeout
.as_secs()
.saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
});
let result = {
let fns = device.fns();
(fns.v1_0.wait_for_fences)(
device.handle(),
fences_vk.len() as u32,
fences_vk.as_ptr(),
ash::vk::TRUE, // TODO: let the user choose false here?
timeout_ns,
)
};
match result {
ash::vk::Result::SUCCESS => fences
.into_iter()
.zip(&mut states)
.filter_map(|(fence, state)| state.set_signaled().map(|state| (state, fence)))
.collect(),
err => return Err(VulkanError::from(err)),
}
};
// If we have queues that we need to signal our status to,
// do so now after the state locks are dropped, to avoid deadlocks.
for (queue, fence) in queues_to_signal {
queue.with(|mut q| q.fence_signaled(fence));
} }
Ok(()) // VUID-vkWaitForFences-fenceCount-arraylength
// If there are no fences, we don't need to wait.
if fences_vk.is_empty() {
return Ok(());
}
let device = &fences[0].device;
let timeout_ns = timeout.map_or(u64::MAX, |timeout| {
timeout
.as_secs()
.saturating_mul(1_000_000_000)
.saturating_add(timeout.subsec_nanos() as u64)
});
let result = {
let fns = device.fns();
(fns.v1_0.wait_for_fences)(
device.handle(),
fences_vk.len() as u32,
fences_vk.as_ptr(),
ash::vk::TRUE, // TODO: let the user choose false here?
timeout_ns,
)
};
match result {
ash::vk::Result::SUCCESS => Ok(()),
err => Err(VulkanError::from(err)),
}
} }
/// Resets the fence. /// Resets the fence.
/// ///
/// The fence must not be in use by a queue operation. /// # Safety
///
/// - The fence must not be in use by the device.
#[inline] #[inline]
pub fn reset(&self) -> Result<(), Validated<VulkanError>> { pub unsafe fn reset(&self) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_reset()?;
self.validate_reset(&state)?;
unsafe { Ok(self.reset_unchecked_locked(&mut state)?) } unsafe { Ok(self.reset_unchecked()?) }
} }
fn validate_reset(&self, state: &FenceState) -> Result<(), Box<ValidationError>> { fn validate_reset(&self) -> Result<(), Box<ValidationError>> {
if state.is_in_queue() {
return Err(Box::new(ValidationError {
problem: "the fence is in use".into(),
vuids: &["VUID-vkResetFences-pFences-01123"],
..Default::default()
}));
}
Ok(()) Ok(())
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn reset_unchecked(&self) -> Result<(), VulkanError> { pub unsafe fn reset_unchecked(&self) -> Result<(), VulkanError> {
let mut state = self.state.lock();
self.reset_unchecked_locked(&mut state)
}
unsafe fn reset_unchecked_locked(&self, state: &mut FenceState) -> Result<(), VulkanError> {
let fns = self.device.fns(); let fns = self.device.fns();
(fns.v1_0.reset_fences)(self.device.handle(), 1, &self.handle) (fns.v1_0.reset_fences)(self.device.handle(), 1, &self.handle)
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.reset();
Ok(()) Ok(())
} }
/// Resets multiple fences at once. /// Resets multiple fences at once.
/// ///
/// The fences must not be in use by a queue operation. /// # Safety
/// ///
/// # Panics /// - The elements of `fences` must not be in use by the device.
/// pub unsafe fn multi_reset<'a>(
/// - Panics if not all fences belong to the same device.
pub fn multi_reset<'a>(
fences: impl IntoIterator<Item = &'a Fence>, fences: impl IntoIterator<Item = &'a Fence>,
) -> Result<(), Validated<VulkanError>> { ) -> Result<(), Validated<VulkanError>> {
let (fences, mut states): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = fences let fences: SmallVec<[_; 8]> = fences.into_iter().collect();
.into_iter() Self::validate_multi_reset(&fences)?;
.map(|fence| {
let state = fence.state.lock();
(fence, state)
})
.unzip();
Self::validate_multi_reset(&fences, &states)?;
unsafe { Ok(Self::multi_reset_unchecked_locked(&fences, &mut states)?) } unsafe { Ok(Self::multi_reset_unchecked(fences)?) }
} }
fn validate_multi_reset( fn validate_multi_reset(fences: &[&Fence]) -> Result<(), Box<ValidationError>> {
fences: &[&Fence],
states: &[MutexGuard<'_, FenceState>],
) -> Result<(), Box<ValidationError>> {
if fences.is_empty() { if fences.is_empty() {
return Ok(()); return Ok(());
} }
let device = &fences[0].device; let device = &fences[0].device;
for (fence_index, (fence, state)) in fences.iter().zip(states).enumerate() { for fence in fences {
// VUID-vkResetFences-pFences-parent // VUID-vkResetFences-pFences-parent
assert_eq!(device, &fence.device); assert_eq!(device, &fence.device);
if state.is_in_queue() {
return Err(Box::new(ValidationError {
context: format!("fences[{}]", fence_index).into(),
problem: "the fence is in use".into(),
vuids: &["VUID-vkResetFences-pFences-01123"],
..Default::default()
}));
}
} }
Ok(()) Ok(())
@ -514,21 +410,8 @@ impl Fence {
pub unsafe fn multi_reset_unchecked<'a>( pub unsafe fn multi_reset_unchecked<'a>(
fences: impl IntoIterator<Item = &'a Fence>, fences: impl IntoIterator<Item = &'a Fence>,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let (fences, mut states): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = fences let fences: SmallVec<[_; 8]> = fences.into_iter().collect();
.into_iter()
.map(|fence| {
let state = fence.state.lock();
(fence, state)
})
.unzip();
Self::multi_reset_unchecked_locked(&fences, &mut states)
}
unsafe fn multi_reset_unchecked_locked(
fences: &[&Fence],
states: &mut [MutexGuard<'_, FenceState>],
) -> Result<(), VulkanError> {
if fences.is_empty() { if fences.is_empty() {
return Ok(()); return Ok(());
} }
@ -541,10 +424,6 @@ impl Fence {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
for state in states {
state.reset();
}
Ok(()) Ok(())
} }
@ -552,21 +431,27 @@ impl Fence {
/// ///
/// The [`khr_external_fence_fd`](crate::device::DeviceExtensions::khr_external_fence_fd) /// The [`khr_external_fence_fd`](crate::device::DeviceExtensions::khr_external_fence_fd)
/// extension must be enabled on the device. /// extension must be enabled on the device.
///
/// # Safety
///
/// - If `handle_type` has copy transference, then the fence must be signaled, or a signal
/// operation on the fence must be pending.
/// - The fence must not currently have an imported payload from a swapchain acquire operation.
/// - If the fence has an imported payload, its handle type must allow re-exporting as
/// `handle_type`, as returned by [`PhysicalDevice::external_fence_properties`].
#[inline] #[inline]
pub fn export_fd( pub unsafe fn export_fd(
&self, &self,
handle_type: ExternalFenceHandleType, handle_type: ExternalFenceHandleType,
) -> Result<File, Validated<VulkanError>> { ) -> Result<File, Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_export_fd(handle_type)?;
self.validate_export_fd(handle_type, &state)?;
unsafe { Ok(self.export_fd_unchecked_locked(handle_type, &mut state)?) } unsafe { Ok(self.export_fd_unchecked(handle_type)?) }
} }
fn validate_export_fd( fn validate_export_fd(
&self, &self,
handle_type: ExternalFenceHandleType, handle_type: ExternalFenceHandleType,
state: &FenceState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().khr_external_fence_fd { if !self.device.enabled_extensions().khr_external_fence_fd {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -604,73 +489,13 @@ impl Fence {
})); }));
} }
if handle_type.has_copy_transference()
&& !(state.is_signaled().unwrap_or(false) || state.is_in_queue())
{
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
the fence is not signaled, and \
a signal operation on the fence is not pending"
.into(),
vuids: &["VUID-VkFenceGetFdInfoKHR-handleType-01454"],
..Default::default()
}));
}
if let Some(imported_handle_type) = state.current_import {
match imported_handle_type {
ImportType::SwapchainAcquire => {
return Err(Box::new(ValidationError {
problem: "the fence currently has an imported payload from a \
swapchain acquire operation"
.into(),
vuids: &["VUID-VkFenceGetFdInfoKHR-fence-01455"],
..Default::default()
}));
}
ImportType::ExternalFence(imported_handle_type) => {
let external_fence_properties = unsafe {
self.device
.physical_device()
.external_fence_properties_unchecked(ExternalFenceInfo::handle_type(
handle_type,
))
};
if !external_fence_properties
.export_from_imported_handle_types
.intersects(imported_handle_type.into())
{
return Err(Box::new(ValidationError {
problem: "the fence currently has an imported payload, whose type \
does not allow re-exporting as `handle_type`, as \
returned by `PhysicalDevice::external_fence_properties`"
.into(),
vuids: &["VUID-VkFenceGetFdInfoKHR-fence-01455"],
..Default::default()
}));
}
}
}
}
Ok(()) Ok(())
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn export_fd_unchecked( pub unsafe fn export_fd_unchecked(
&self, &self,
handle_type: ExternalFenceHandleType, handle_type: ExternalFenceHandleType,
) -> Result<File, VulkanError> {
let mut state = self.state.lock();
self.export_fd_unchecked_locked(handle_type, &mut state)
}
unsafe fn export_fd_unchecked_locked(
&self,
handle_type: ExternalFenceHandleType,
state: &mut FenceState,
) -> Result<File, VulkanError> { ) -> Result<File, VulkanError> {
let info_vk = ash::vk::FenceGetFdInfoKHR { let info_vk = ash::vk::FenceGetFdInfoKHR {
fence: self.handle, fence: self.handle,
@ -688,8 +513,6 @@ impl Fence {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.export(handle_type);
#[cfg(unix)] #[cfg(unix)]
{ {
use std::os::unix::io::FromRawFd; use std::os::unix::io::FromRawFd;
@ -707,21 +530,29 @@ impl Fence {
/// ///
/// The [`khr_external_fence_win32`](crate::device::DeviceExtensions::khr_external_fence_win32) /// The [`khr_external_fence_win32`](crate::device::DeviceExtensions::khr_external_fence_win32)
/// extension must be enabled on the device. /// extension must be enabled on the device.
///
/// # Safety
///
/// - If `handle_type` has copy transference, then the fence must be signaled, or a signal
/// operation on the fence must be pending.
/// - The fence must not currently have an imported payload from a swapchain acquire operation.
/// - If the fence has an imported payload, its handle type must allow re-exporting as
/// `handle_type`, as returned by [`PhysicalDevice::external_fence_properties`].
/// - If `handle_type` is `ExternalFenceHandleType::OpaqueWin32`, then a handle of this type
/// must not have been already exported from this fence.
#[inline] #[inline]
pub fn export_win32_handle( pub fn export_win32_handle(
&self, &self,
handle_type: ExternalFenceHandleType, handle_type: ExternalFenceHandleType,
) -> Result<*mut std::ffi::c_void, Validated<VulkanError>> { ) -> Result<*mut std::ffi::c_void, Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_export_win32_handle(handle_type)?;
self.validate_export_win32_handle(handle_type, &state)?;
unsafe { Ok(self.export_win32_handle_unchecked_locked(handle_type, &mut state)?) } unsafe { Ok(self.export_win32_handle_unchecked(handle_type)?) }
} }
fn validate_export_win32_handle( fn validate_export_win32_handle(
&self, &self,
handle_type: ExternalFenceHandleType, handle_type: ExternalFenceHandleType,
state: &FenceState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().khr_external_fence_win32 { if !self.device.enabled_extensions().khr_external_fence_win32 {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -759,85 +590,13 @@ impl Fence {
})); }));
} }
if matches!(handle_type, ExternalFenceHandleType::OpaqueWin32)
&& state.is_exported(handle_type)
{
return Err(Box::new(ValidationError {
problem: "`handle_type` is `ExternalFenceHandleType::OpaqueWin32`, but \
a handle of this type has already been exported from this fence"
.into(),
vuids: &["VUID-VkFenceGetWin32HandleInfoKHR-handleType-01449"],
..Default::default()
}));
}
if handle_type.has_copy_transference()
&& !(state.is_signaled().unwrap_or(false) || state.is_in_queue())
{
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
the fence is not signaled, and \
a signal operation on the fence is not pending"
.into(),
vuids: &["VUID-VkFenceGetWin32HandleInfoKHR-handleType-01451"],
..Default::default()
}));
}
if let Some(imported_handle_type) = state.current_import {
match imported_handle_type {
ImportType::SwapchainAcquire => {
return Err(Box::new(ValidationError {
problem: "the fence currently has an imported payload from a \
swapchain acquire operation"
.into(),
vuids: &["VUID-VkFenceGetWin32HandleInfoKHR-fence-01450"],
..Default::default()
}));
}
ImportType::ExternalFence(imported_handle_type) => {
let external_fence_properties = unsafe {
self.device
.physical_device()
.external_fence_properties_unchecked(ExternalFenceInfo::handle_type(
handle_type,
))
};
if !external_fence_properties
.export_from_imported_handle_types
.intersects(imported_handle_type.into())
{
return Err(Box::new(ValidationError {
problem: "the fence currently has an imported payload, whose type \
does not allow re-exporting as `handle_type`, as \
returned by `PhysicalDevice::external_fence_properties`"
.into(),
vuids: &["VUID-VkFenceGetWin32HandleInfoKHR-fence-01450"],
..Default::default()
}));
}
}
}
}
Ok(()) Ok(())
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn export_win32_handle_unchecked( pub unsafe fn export_win32_handle_unchecked(
&self, &self,
handle_type: ExternalFenceHandleType, handle_type: ExternalFenceHandleType,
) -> Result<*mut std::ffi::c_void, VulkanError> {
let mut state = self.state.lock();
self.export_win32_handle_unchecked_locked(handle_type, &mut state)
}
unsafe fn export_win32_handle_unchecked_locked(
&self,
handle_type: ExternalFenceHandleType,
state: &mut FenceState,
) -> Result<*mut std::ffi::c_void, VulkanError> { ) -> Result<*mut std::ffi::c_void, VulkanError> {
let info_vk = ash::vk::FenceGetWin32HandleInfoKHR { let info_vk = ash::vk::FenceGetWin32HandleInfoKHR {
fence: self.handle, fence: self.handle,
@ -855,8 +614,6 @@ impl Fence {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.export(handle_type);
Ok(output.assume_init()) Ok(output.assume_init())
} }
@ -867,6 +624,7 @@ impl Fence {
/// ///
/// # Safety /// # Safety
/// ///
/// - The fence must not be in use by the device.
/// - If in `import_fence_fd_info`, `handle_type` is `ExternalHandleType::OpaqueFd`, /// - If in `import_fence_fd_info`, `handle_type` is `ExternalHandleType::OpaqueFd`,
/// then `file` must represent a fence that was exported from Vulkan or a compatible API, /// then `file` must represent a fence that was exported from Vulkan or a compatible API,
/// with a driver and device UUID equal to those of the device that owns `self`. /// with a driver and device UUID equal to those of the device that owns `self`.
@ -875,16 +633,14 @@ impl Fence {
&self, &self,
import_fence_fd_info: ImportFenceFdInfo, import_fence_fd_info: ImportFenceFdInfo,
) -> Result<(), Validated<VulkanError>> { ) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_import_fd(&import_fence_fd_info)?;
self.validate_import_fd(&import_fence_fd_info, &state)?;
Ok(self.import_fd_unchecked_locked(import_fence_fd_info, &mut state)?) Ok(self.import_fd_unchecked(import_fence_fd_info)?)
} }
fn validate_import_fd( fn validate_import_fd(
&self, &self,
import_fence_fd_info: &ImportFenceFdInfo, import_fence_fd_info: &ImportFenceFdInfo,
state: &FenceState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().khr_external_fence_fd { if !self.device.enabled_extensions().khr_external_fence_fd {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -895,14 +651,6 @@ impl Fence {
})); }));
} }
if state.is_in_queue() {
return Err(Box::new(ValidationError {
problem: "the fence is in use".into(),
vuids: &["VUID-vkImportFenceFdKHR-fence-01463"],
..Default::default()
}));
}
import_fence_fd_info import_fence_fd_info
.validate(&self.device) .validate(&self.device)
.map_err(|err| err.add_context("import_fence_fd_info"))?; .map_err(|err| err.add_context("import_fence_fd_info"))?;
@ -911,19 +659,9 @@ impl Fence {
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn import_fd_unchecked( pub unsafe fn import_fd_unchecked(
&self, &self,
import_fence_fd_info: ImportFenceFdInfo, import_fence_fd_info: ImportFenceFdInfo,
) -> Result<(), VulkanError> {
let mut state = self.state.lock();
self.import_fd_unchecked_locked(import_fence_fd_info, &mut state)
}
unsafe fn import_fd_unchecked_locked(
&self,
import_fence_fd_info: ImportFenceFdInfo,
state: &mut FenceState,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let ImportFenceFdInfo { let ImportFenceFdInfo {
flags, flags,
@ -957,8 +695,6 @@ impl Fence {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.import(handle_type, flags.intersects(FenceImportFlags::TEMPORARY));
Ok(()) Ok(())
} }
@ -969,6 +705,7 @@ impl Fence {
/// ///
/// # Safety /// # Safety
/// ///
/// - The fence must not be in use by the device.
/// - In `import_fence_win32_handle_info`, `handle` must represent a fence that was exported /// - In `import_fence_win32_handle_info`, `handle` must represent a fence that was exported
/// from Vulkan or a compatible API, with a driver and device UUID equal to those of the /// from Vulkan or a compatible API, with a driver and device UUID equal to those of the
/// device that owns `self`. /// device that owns `self`.
@ -977,16 +714,14 @@ impl Fence {
&self, &self,
import_fence_win32_handle_info: ImportFenceWin32HandleInfo, import_fence_win32_handle_info: ImportFenceWin32HandleInfo,
) -> Result<(), Validated<VulkanError>> { ) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_import_win32_handle(&import_fence_win32_handle_info)?;
self.validate_import_win32_handle(&import_fence_win32_handle_info, &state)?;
Ok(self.import_win32_handle_unchecked_locked(import_fence_win32_handle_info, &mut state)?) Ok(self.import_win32_handle_unchecked(import_fence_win32_handle_info)?)
} }
fn validate_import_win32_handle( fn validate_import_win32_handle(
&self, &self,
import_fence_win32_handle_info: &ImportFenceWin32HandleInfo, import_fence_win32_handle_info: &ImportFenceWin32HandleInfo,
state: &FenceState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().khr_external_fence_win32 { if !self.device.enabled_extensions().khr_external_fence_win32 {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -997,14 +732,6 @@ impl Fence {
})); }));
} }
if state.is_in_queue() {
return Err(Box::new(ValidationError {
problem: "the fence is in use".into(),
vuids: &["VUID-vkImportFenceWin32HandleKHR-fence-04448"],
..Default::default()
}));
}
import_fence_win32_handle_info import_fence_win32_handle_info
.validate(&self.device) .validate(&self.device)
.map_err(|err| err.add_context("import_fence_win32_handle_info"))?; .map_err(|err| err.add_context("import_fence_win32_handle_info"))?;
@ -1013,19 +740,9 @@ impl Fence {
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn import_win32_handle_unchecked( pub unsafe fn import_win32_handle_unchecked(
&self, &self,
import_fence_win32_handle_info: ImportFenceWin32HandleInfo, import_fence_win32_handle_info: ImportFenceWin32HandleInfo,
) -> Result<(), VulkanError> {
let mut state = self.state.lock();
self.import_win32_handle_unchecked_locked(import_fence_win32_handle_info, &mut state)
}
unsafe fn import_win32_handle_unchecked_locked(
&self,
import_fence_win32_handle_info: ImportFenceWin32HandleInfo,
state: &mut FenceState,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let ImportFenceWin32HandleInfo { let ImportFenceWin32HandleInfo {
flags, flags,
@ -1051,15 +768,9 @@ impl Fence {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.import(handle_type, flags.intersects(FenceImportFlags::TEMPORARY));
Ok(()) Ok(())
} }
pub(crate) fn state(&self) -> MutexGuard<'_, FenceState> {
self.state.lock()
}
// Shared by Fence and FenceSignalFuture // Shared by Fence and FenceSignalFuture
pub(crate) fn poll_impl(&self, cx: &mut Context<'_>) -> Poll<Result<(), VulkanError>> { pub(crate) fn poll_impl(&self, cx: &mut Context<'_>) -> Poll<Result<(), VulkanError>> {
// Vulkan only allows polling of the fence status, so we have to use a spin future. // Vulkan only allows polling of the fence status, so we have to use a spin future.
@ -1527,124 +1238,6 @@ pub struct ExternalFenceProperties {
pub compatible_handle_types: ExternalFenceHandleTypes, pub compatible_handle_types: ExternalFenceHandleTypes,
} }
#[derive(Debug, Default)]
pub(crate) struct FenceState {
is_signaled: bool,
pending_signal: Option<Weak<Queue>>,
reference_exported: bool,
exported_handle_types: ExternalFenceHandleTypes,
current_import: Option<ImportType>,
permanent_import: Option<ExternalFenceHandleType>,
}
impl FenceState {
/// If the fence is not in a queue and has no external references, returns the current status.
#[inline]
fn is_signaled(&self) -> Option<bool> {
// If either of these is true, we can't be certain of the status.
if self.is_in_queue() || self.has_external_reference() {
None
} else {
Some(self.is_signaled)
}
}
#[inline]
fn is_in_queue(&self) -> bool {
self.pending_signal.is_some()
}
/// Returns whether there are any potential external references to the fence payload.
/// That is, the fence has been exported by reference transference, or imported.
#[inline]
fn has_external_reference(&self) -> bool {
self.reference_exported || self.current_import.is_some()
}
#[allow(dead_code)]
#[inline]
fn is_exported(&self, handle_type: ExternalFenceHandleType) -> bool {
self.exported_handle_types.intersects(handle_type.into())
}
#[inline]
pub(crate) unsafe fn add_queue_signal(&mut self, queue: &Arc<Queue>) {
self.pending_signal = Some(Arc::downgrade(queue));
}
/// Called when a fence first discovers that it is signaled.
/// Returns the queue that should be informed about it.
#[inline]
unsafe fn set_signaled(&mut self) -> Option<Arc<Queue>> {
self.is_signaled = true;
// Fences with external references can't be used to determine queue completion.
if self.has_external_reference() {
self.pending_signal = None;
None
} else {
self.pending_signal.take().and_then(|queue| queue.upgrade())
}
}
/// Called when a queue is unlocking resources.
#[inline]
pub(crate) unsafe fn set_signal_finished(&mut self) {
self.is_signaled = true;
self.pending_signal = None;
}
#[inline]
unsafe fn reset(&mut self) {
debug_assert!(!self.is_in_queue());
self.current_import = self.permanent_import.map(Into::into);
self.is_signaled = false;
}
#[allow(dead_code)]
#[inline]
unsafe fn export(&mut self, handle_type: ExternalFenceHandleType) {
self.exported_handle_types |= handle_type.into();
if handle_type.has_copy_transference() {
self.reset();
} else {
self.reference_exported = true;
}
}
#[allow(dead_code)]
#[inline]
unsafe fn import(&mut self, handle_type: ExternalFenceHandleType, temporary: bool) {
debug_assert!(!self.is_in_queue());
self.current_import = Some(handle_type.into());
if !temporary {
self.permanent_import = Some(handle_type);
}
}
#[inline]
pub(crate) unsafe fn import_swapchain_acquire(&mut self) {
debug_assert!(!self.is_in_queue());
self.current_import = Some(ImportType::SwapchainAcquire);
}
}
#[derive(Clone, Copy, Debug)]
enum ImportType {
SwapchainAcquire,
ExternalFence(ExternalFenceHandleType),
}
impl From<ExternalFenceHandleType> for ImportType {
#[inline]
fn from(handle_type: ExternalFenceHandleType) -> Self {
Self::ExternalFence(handle_type)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
@ -1703,7 +1296,11 @@ mod tests {
}, },
) )
.unwrap(); .unwrap();
fence.reset().unwrap();
unsafe {
fence.reset().unwrap();
}
assert!(!fence.is_signaled().unwrap()); assert!(!fence.is_signaled().unwrap());
} }
@ -1760,7 +1357,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let _ = Fence::multi_reset([&fence1, &fence2]); let _ = unsafe { Fence::multi_reset([&fence1, &fence2]) };
}); });
} }

View File

@ -16,7 +16,7 @@ use crate::{
swapchain::Swapchain, swapchain::Swapchain,
sync::{ sync::{
fence::Fence, fence::Fence,
future::{AccessError, SubmitAnyBuilder}, future::{queue_bind_sparse, queue_present, queue_submit, AccessError, SubmitAnyBuilder},
PipelineStages, PipelineStages,
}, },
DeviceSize, Validated, ValidationError, VulkanError, DeviceSize, Validated, ValidationError, VulkanError,
@ -243,35 +243,36 @@ where
SubmitAnyBuilder::Empty => { SubmitAnyBuilder::Empty => {
debug_assert!(!partially_flushed); debug_assert!(!partially_flushed);
queue queue_submit(
.with(|mut q| { &queue,
q.submit_unchecked([Default::default()], Some(new_fence.clone())) Default::default(),
}) Some(new_fence.clone()),
.map_err(|err| OutcomeErr::Full(err.into())) &previous,
)
.map_err(OutcomeErr::Full)
} }
SubmitAnyBuilder::SemaphoresWait(semaphores) => { SubmitAnyBuilder::SemaphoresWait(semaphores) => {
debug_assert!(!partially_flushed); debug_assert!(!partially_flushed);
queue queue_submit(
.with(|mut q| { &queue,
q.submit_unchecked( SubmitInfo {
[SubmitInfo { wait_semaphores: semaphores
wait_semaphores: semaphores .into_iter()
.into_iter() .map(|semaphore| {
.map(|semaphore| { SemaphoreSubmitInfo {
SemaphoreSubmitInfo { // TODO: correct stages ; hard
// TODO: correct stages ; hard stages: PipelineStages::ALL_COMMANDS,
stages: PipelineStages::ALL_COMMANDS, ..SemaphoreSubmitInfo::new(semaphore)
..SemaphoreSubmitInfo::semaphore(semaphore) }
} })
}) .collect(),
.collect(), ..Default::default()
..Default::default() },
}], None,
None, &previous,
) )
}) .map_err(OutcomeErr::Full)
.map_err(|err| OutcomeErr::Full(err.into()))
} }
SubmitAnyBuilder::CommandBuffer(submit_info, fence) => { SubmitAnyBuilder::CommandBuffer(submit_info, fence) => {
debug_assert!(!partially_flushed); debug_assert!(!partially_flushed);
@ -282,15 +283,7 @@ where
// assertion. // assertion.
assert!(fence.is_none()); assert!(fence.is_none());
queue queue_submit(&queue, submit_info, Some(new_fence.clone()), &previous)
.with(|mut q| {
q.submit_with_future(
submit_info,
Some(new_fence.clone()),
&previous,
&queue,
)
})
.map_err(OutcomeErr::Full) .map_err(OutcomeErr::Full)
} }
SubmitAnyBuilder::BindSparse(bind_infos, fence) => { SubmitAnyBuilder::BindSparse(bind_infos, fence) => {
@ -302,19 +295,20 @@ where
.queue_flags .queue_flags
.intersects(QueueFlags::SPARSE_BINDING)); .intersects(QueueFlags::SPARSE_BINDING));
queue queue_bind_sparse(&queue, bind_infos, Some(new_fence.clone()))
.with(|mut q| q.bind_sparse_unchecked(bind_infos, Some(new_fence.clone()))) .map_err(OutcomeErr::Full)
.map_err(|err| OutcomeErr::Full(err.into()))
} }
SubmitAnyBuilder::QueuePresent(present_info) => { SubmitAnyBuilder::QueuePresent(present_info) => {
if partially_flushed { if partially_flushed {
queue queue_submit(
.with(|mut q| { &queue,
q.submit_unchecked([Default::default()], Some(new_fence.clone())) Default::default(),
}) Some(new_fence.clone()),
.map_err(|err| OutcomeErr::Partial(err.into())) &previous,
)
.map_err(OutcomeErr::Partial)
} else { } else {
for swapchain_info in &present_info.swapchain_infos { for swapchain_info in &present_info.swapchains {
if swapchain_info.present_id.map_or(false, |present_id| { if swapchain_info.present_id.map_or(false, |present_id| {
!swapchain_info.swapchain.try_claim_present_id(present_id) !swapchain_info.swapchain.try_claim_present_id(present_id)
}) { }) {
@ -346,20 +340,18 @@ where
} }
} }
let intermediary_result = queue let intermediary_result = queue_present(&queue, present_info)?
.with(|mut q| q.present_unchecked(present_info))?
.map(|r| r.map(|_| ())) .map(|r| r.map(|_| ()))
.fold(Ok(()), Result::and); .fold(Ok(()), Result::and);
match intermediary_result { match intermediary_result {
Ok(()) => queue Ok(()) => queue_submit(
.with(|mut q| { &queue,
q.submit_unchecked( Default::default(),
[Default::default()], Some(new_fence.clone()),
Some(new_fence.clone()), &previous,
) )
}) .map_err(OutcomeErr::Partial),
.map_err(|err| OutcomeErr::Partial(err.into())),
Err(err) => Err(OutcomeErr::Full(err.into())), Err(err) => Err(OutcomeErr::Full(err.into())),
} }
} }

View File

@ -98,22 +98,26 @@ pub use self::{
}; };
use super::{fence::Fence, semaphore::Semaphore}; use super::{fence::Fence, semaphore::Semaphore};
use crate::{ use crate::{
buffer::Buffer, buffer::{Buffer, BufferState},
command_buffer::{ command_buffer::{
CommandBufferExecError, CommandBufferExecFuture, PrimaryCommandBufferAbstract, SubmitInfo, CommandBufferExecError, CommandBufferExecFuture, CommandBufferResourcesUsage,
CommandBufferState, CommandBufferSubmitInfo, CommandBufferUsage,
PrimaryCommandBufferAbstract, SubmitInfo,
}, },
device::{DeviceOwned, Queue}, device::{DeviceOwned, Queue},
image::{Image, ImageLayout}, image::{Image, ImageLayout, ImageState},
memory::BindSparseInfo, memory::BindSparseInfo,
swapchain::{self, PresentFuture, PresentInfo, Swapchain, SwapchainPresentInfo}, swapchain::{self, PresentFuture, PresentInfo, Swapchain, SwapchainPresentInfo},
DeviceSize, Validated, VulkanError, DeviceSize, Validated, ValidationError, VulkanError, VulkanObject,
}; };
use smallvec::SmallVec; use ahash::HashMap;
use parking_lot::MutexGuard;
use smallvec::{smallvec, SmallVec};
use std::{ use std::{
error::Error, error::Error,
fmt::{Display, Error as FmtError, Formatter}, fmt::{Display, Error as FmtError, Formatter},
ops::Range, ops::Range,
sync::Arc, sync::{atomic::Ordering, Arc},
}; };
mod fence_signal; mod fence_signal;
@ -554,3 +558,321 @@ impl From<AccessError> for AccessCheckError {
AccessCheckError::Denied(err) AccessCheckError::Denied(err)
} }
} }
pub(crate) unsafe fn queue_bind_sparse(
queue: &Arc<Queue>,
bind_infos: impl IntoIterator<Item = BindSparseInfo>,
fence: Option<Arc<Fence>>,
) -> Result<(), Validated<VulkanError>> {
let bind_infos: SmallVec<[_; 4]> = bind_infos.into_iter().collect();
queue.with(|mut queue_guard| queue_guard.bind_sparse_unchecked(&bind_infos, fence.as_ref()))?;
Ok(())
}
pub(crate) unsafe fn queue_present(
queue: &Arc<Queue>,
present_info: PresentInfo,
) -> Result<impl ExactSizeIterator<Item = Result<bool, VulkanError>>, Validated<VulkanError>> {
let results: SmallVec<[_; 1]> = queue
.with(|mut queue_guard| queue_guard.present(&present_info))?
.collect();
let PresentInfo {
wait_semaphores: _,
swapchains,
_ne: _,
} = &present_info;
// If a presentation results in a loss of full-screen exclusive mode,
// signal that to the relevant swapchain.
for (&result, swapchain_info) in results.iter().zip(swapchains) {
if result == Err(VulkanError::FullScreenExclusiveModeLost) {
swapchain_info
.swapchain
.full_screen_exclusive_held()
.store(false, Ordering::SeqCst);
}
}
Ok(results.into_iter())
}
pub(crate) unsafe fn queue_submit(
queue: &Arc<Queue>,
submit_info: SubmitInfo,
fence: Option<Arc<Fence>>,
future: &dyn GpuFuture,
) -> Result<(), Validated<VulkanError>> {
let submit_infos: SmallVec<[_; 4]> = smallvec![submit_info];
let mut states = States::from_submit_infos(&submit_infos);
for submit_info in &submit_infos {
for command_buffer_submit_info in &submit_info.command_buffers {
let &CommandBufferSubmitInfo {
ref command_buffer,
_ne: _,
} = command_buffer_submit_info;
let state = states
.command_buffers
.get(&command_buffer.handle())
.unwrap();
match command_buffer.usage() {
CommandBufferUsage::OneTimeSubmit => {
if state.has_been_submitted() {
return Err(Box::new(ValidationError {
problem: "a command buffer, or one of the secondary \
command buffers it executes, was created with the \
`CommandBufferUsage::OneTimeSubmit` usage, but \
it has already been submitted in the past"
.into(),
vuids: &["VUID-vkQueueSubmit2-commandBuffer-03874"],
..Default::default()
})
.into());
}
}
CommandBufferUsage::MultipleSubmit => {
if state.is_submit_pending() {
return Err(Box::new(ValidationError {
problem: "a command buffer, or one of the secondary \
command buffers it executes, was not created with the \
`CommandBufferUsage::SimultaneousUse` usage, but \
it is already in use by the device"
.into(),
vuids: &["VUID-vkQueueSubmit2-commandBuffer-03875"],
..Default::default()
})
.into());
}
}
CommandBufferUsage::SimultaneousUse => (),
}
let CommandBufferResourcesUsage {
buffers,
images,
buffer_indices: _,
image_indices: _,
} = command_buffer.resources_usage();
for usage in buffers {
let state = states.buffers.get_mut(&usage.buffer.handle()).unwrap();
for (range, range_usage) in usage.ranges.iter() {
match future.check_buffer_access(
&usage.buffer,
range.clone(),
range_usage.mutable,
queue,
) {
Err(AccessCheckError::Denied(error)) => {
return Err(Box::new(ValidationError {
problem: format!(
"access to a resource has been denied \
(resource use: {:?}, error: {})",
range_usage.first_use, error
)
.into(),
..Default::default()
})
.into());
}
Err(AccessCheckError::Unknown) => {
let result = if range_usage.mutable {
state.check_gpu_write(range.clone())
} else {
state.check_gpu_read(range.clone())
};
if let Err(error) = result {
return Err(Box::new(ValidationError {
problem: format!(
"access to a resource has been denied \
(resource use: {:?}, error: {})",
range_usage.first_use, error
)
.into(),
..Default::default()
})
.into());
}
}
_ => (),
}
}
}
for usage in images {
let state = states.images.get_mut(&usage.image.handle()).unwrap();
for (range, range_usage) in usage.ranges.iter() {
match future.check_image_access(
&usage.image,
range.clone(),
range_usage.mutable,
range_usage.expected_layout,
queue,
) {
Err(AccessCheckError::Denied(error)) => {
return Err(Box::new(ValidationError {
problem: format!(
"access to a resource has been denied \
(resource use: {:?}, error: {})",
range_usage.first_use, error
)
.into(),
..Default::default()
})
.into());
}
Err(AccessCheckError::Unknown) => {
let result = if range_usage.mutable {
state.check_gpu_write(range.clone(), range_usage.expected_layout)
} else {
state.check_gpu_read(range.clone(), range_usage.expected_layout)
};
if let Err(error) = result {
return Err(Box::new(ValidationError {
problem: format!(
"access to a resource has been denied \
(resource use: {:?}, error: {})",
range_usage.first_use, error
)
.into(),
..Default::default()
})
.into());
}
}
_ => (),
};
}
}
}
}
queue.with(|mut queue_guard| queue_guard.submit(&submit_infos, fence.as_ref()))?;
for submit_info in &submit_infos {
let SubmitInfo {
wait_semaphores: _,
command_buffers,
signal_semaphores: _,
_ne: _,
} = submit_info;
for command_buffer_submit_info in command_buffers {
let CommandBufferSubmitInfo {
command_buffer,
_ne: _,
} = command_buffer_submit_info;
let state = states
.command_buffers
.get_mut(&command_buffer.handle())
.unwrap();
state.add_queue_submit();
let CommandBufferResourcesUsage {
buffers,
images,
buffer_indices: _,
image_indices: _,
} = command_buffer.resources_usage();
for usage in buffers {
let state = states.buffers.get_mut(&usage.buffer.handle()).unwrap();
for (range, range_usage) in usage.ranges.iter() {
if range_usage.mutable {
state.gpu_write_lock(range.clone());
} else {
state.gpu_read_lock(range.clone());
}
}
}
for usage in images {
let state = states.images.get_mut(&usage.image.handle()).unwrap();
for (range, range_usage) in usage.ranges.iter() {
if range_usage.mutable {
state.gpu_write_lock(range.clone(), range_usage.final_layout);
} else {
state.gpu_read_lock(range.clone());
}
}
}
}
}
Ok(())
}
// This struct exists to ensure that every object gets locked exactly once.
// Otherwise we get deadlocks.
#[derive(Debug)]
struct States<'a> {
buffers: HashMap<ash::vk::Buffer, MutexGuard<'a, BufferState>>,
command_buffers: HashMap<ash::vk::CommandBuffer, MutexGuard<'a, CommandBufferState>>,
images: HashMap<ash::vk::Image, MutexGuard<'a, ImageState>>,
}
impl<'a> States<'a> {
fn from_submit_infos(submit_infos: &'a [SubmitInfo]) -> Self {
let mut buffers = HashMap::default();
let mut command_buffers = HashMap::default();
let mut images = HashMap::default();
for submit_info in submit_infos {
let SubmitInfo {
wait_semaphores: _,
command_buffers: info_command_buffers,
signal_semaphores: _,
_ne: _,
} = submit_info;
for command_buffer_submit_info in info_command_buffers {
let &CommandBufferSubmitInfo {
ref command_buffer,
_ne: _,
} = command_buffer_submit_info;
command_buffers
.entry(command_buffer.handle())
.or_insert_with(|| command_buffer.state());
let CommandBufferResourcesUsage {
buffers: buffers_usage,
images: images_usage,
buffer_indices: _,
image_indices: _,
} = command_buffer.resources_usage();
for usage in buffers_usage {
let buffer = &usage.buffer;
buffers
.entry(buffer.handle())
.or_insert_with(|| buffer.state());
}
for usage in images_usage {
let image = &usage.image;
images
.entry(image.handle())
.or_insert_with(|| image.state());
}
}
}
Self {
buffers,
command_buffers,
images,
}
}
}

View File

@ -7,14 +7,18 @@
// notice may not be copied, modified, or distributed except // notice may not be copied, modified, or distributed except
// according to those terms. // according to those terms.
use super::{AccessCheckError, GpuFuture, SubmitAnyBuilder}; use super::{queue_present, AccessCheckError, GpuFuture, SubmitAnyBuilder};
use crate::{ use crate::{
buffer::Buffer, buffer::Buffer,
command_buffer::{SemaphoreSubmitInfo, SubmitInfo}, command_buffer::{SemaphoreSubmitInfo, SubmitInfo},
device::{Device, DeviceOwned, Queue}, device::{Device, DeviceOwned, Queue},
image::{Image, ImageLayout}, image::{Image, ImageLayout},
swapchain::Swapchain, swapchain::Swapchain,
sync::{future::AccessError, semaphore::Semaphore, PipelineStages}, sync::{
future::{queue_submit, AccessError},
semaphore::Semaphore,
PipelineStages,
},
DeviceSize, Validated, ValidationError, VulkanError, DeviceSize, Validated, ValidationError, VulkanError,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
@ -89,51 +93,49 @@ where
match self.previous.build_submission()? { match self.previous.build_submission()? {
SubmitAnyBuilder::Empty => { SubmitAnyBuilder::Empty => {
queue.with(|mut q| { queue_submit(
q.submit_unchecked( &queue,
[SubmitInfo { SubmitInfo {
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore( signal_semaphores: vec![SemaphoreSubmitInfo::new(
self.semaphore.clone(), self.semaphore.clone(),
)], )],
..Default::default() ..Default::default()
}], },
None, None,
) &self.previous,
})?; )?;
} }
SubmitAnyBuilder::SemaphoresWait(semaphores) => { SubmitAnyBuilder::SemaphoresWait(semaphores) => {
queue.with(|mut q| { queue_submit(
q.submit_unchecked( &queue,
[SubmitInfo { SubmitInfo {
wait_semaphores: semaphores wait_semaphores: semaphores
.into_iter() .into_iter()
.map(|semaphore| { .map(|semaphore| {
SemaphoreSubmitInfo { SemaphoreSubmitInfo {
// TODO: correct stages ; hard // TODO: correct stages ; hard
stages: PipelineStages::ALL_COMMANDS, stages: PipelineStages::ALL_COMMANDS,
..SemaphoreSubmitInfo::semaphore(semaphore) ..SemaphoreSubmitInfo::new(semaphore)
} }
}) })
.collect(), .collect(),
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore( signal_semaphores: vec![SemaphoreSubmitInfo::new(
self.semaphore.clone(), self.semaphore.clone(),
)], )],
..Default::default() ..Default::default()
}], },
None, None,
) &self.previous,
})?; )?;
} }
SubmitAnyBuilder::CommandBuffer(mut submit_info, fence) => { SubmitAnyBuilder::CommandBuffer(mut submit_info, fence) => {
debug_assert!(submit_info.signal_semaphores.is_empty()); debug_assert!(submit_info.signal_semaphores.is_empty());
submit_info submit_info
.signal_semaphores .signal_semaphores
.push(SemaphoreSubmitInfo::semaphore(self.semaphore.clone())); .push(SemaphoreSubmitInfo::new(self.semaphore.clone()));
queue.with(|mut q| { queue_submit(&queue, submit_info, fence, &self.previous)?;
q.submit_with_future(submit_info, fence, &self.previous, &queue)
})?;
} }
SubmitAnyBuilder::BindSparse(_, _) => { SubmitAnyBuilder::BindSparse(_, _) => {
unimplemented!() // TODO: how to do that? unimplemented!() // TODO: how to do that?
@ -142,7 +144,7 @@ where
builder.submit(&queue)?;*/ builder.submit(&queue)?;*/
} }
SubmitAnyBuilder::QueuePresent(present_info) => { SubmitAnyBuilder::QueuePresent(present_info) => {
for swapchain_info in &present_info.swapchain_infos { for swapchain_info in &present_info.swapchains {
if swapchain_info.present_id.map_or(false, |present_id| { if swapchain_info.present_id.map_or(false, |present_id| {
!swapchain_info.swapchain.try_claim_present_id(present_id) !swapchain_info.swapchain.try_claim_present_id(present_id)
}) { }) {
@ -174,22 +176,22 @@ where
} }
} }
queue.with(|mut q| { queue_present(&queue, present_info)?
q.present_unchecked(present_info)? .map(|r| r.map(|_| ()))
.map(|r| r.map(|_| ())) .fold(Ok(()), Result::and)?;
.fold(Ok(()), Result::and)?;
// FIXME: problematic because if we return an error and flush() is called again, then we'll submit the present twice // FIXME: problematic because if we return an error and flush() is called again, then we'll submit the present twice
q.submit_unchecked( queue_submit(
[SubmitInfo { &queue,
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore( SubmitInfo {
self.semaphore.clone(), signal_semaphores: vec![SemaphoreSubmitInfo::new(
)], self.semaphore.clone(),
..Default::default() )],
}], ..Default::default()
None, },
)?; None,
Ok::<_, Validated<VulkanError>>(()) &self.previous,
})?; )?;
} }
}; };
@ -267,7 +269,10 @@ where
self.flush().unwrap(); self.flush().unwrap();
// Block until the queue finished. // Block until the queue finished.
self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap(); self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap();
unsafe { self.previous.signal_finished() };
unsafe {
self.signal_finished();
}
} }
} }
} }

View File

@ -9,22 +9,42 @@
//! A semaphore provides synchronization between multiple queues, with non-command buffer //! A semaphore provides synchronization between multiple queues, with non-command buffer
//! commands on the same queue, or between the device and an external source. //! commands on the same queue, or between the device and an external source.
//!
//! A semaphore has two states: **signaled** and **unsignaled**.
//! Only the device can perform operations on a semaphore,
//! the host cannot perform any operations on it.
//!
//! Two operations can be performed on a semaphore:
//! - A **semaphore signal operation** will put the semaphore into the signaled state.
//! - A **semaphore wait operation** will block execution of the operation is associated with,
//! as long as the semaphore is in the unsignaled state. Once the semaphore is in the signaled
//! state, the semaphore is put back in the unsignaled state and execution continues.
//!
//! Semaphore signals and waits must always occur in pairs: one signal operation is paired with one
//! wait operation. If a semaphore is signaled without waiting for it, it stays in the signaled
//! state until it is waited for, or destroyed.
//!
//! # Safety
//!
//! - When a semaphore signal operation is executed on the device,
//! the semaphore must be in the unsignaled state.
//! In other words, the same semaphore cannot be signalled by multiple commands;
//! there must always be a wait operation in between them.
//! - There must never be more than one semaphore wait operation executing on the same semaphore
//! at the same time.
//! - When a semaphore wait operation is queued as part of a command,
//! the semaphore must already be in the signaled state, or
//! the signal operation that it waits for must have been queued previously
//! (as part of a previous command, or an earlier batch within the same command).
use crate::{ use crate::{
device::{physical::PhysicalDevice, Device, DeviceOwned, Queue}, device::{physical::PhysicalDevice, Device, DeviceOwned},
instance::InstanceOwnedDebugWrapper, instance::InstanceOwnedDebugWrapper,
macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum}, macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum},
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
VulkanObject, VulkanObject,
}; };
use parking_lot::{Mutex, MutexGuard}; use std::{fs::File, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
use std::{
fs::File,
mem::MaybeUninit,
num::NonZeroU64,
ptr,
sync::{Arc, Weak},
};
/// Used to provide synchronization between command buffers during their execution. /// Used to provide synchronization between command buffers during their execution.
/// ///
@ -39,7 +59,6 @@ pub struct Semaphore {
export_handle_types: ExternalSemaphoreHandleTypes, export_handle_types: ExternalSemaphoreHandleTypes,
must_put_in_pool: bool, must_put_in_pool: bool,
state: Mutex<SemaphoreState>,
} }
impl Semaphore { impl Semaphore {
@ -128,7 +147,6 @@ impl Semaphore {
export_handle_types: ExternalSemaphoreHandleTypes::empty(), export_handle_types: ExternalSemaphoreHandleTypes::empty(),
must_put_in_pool: true, must_put_in_pool: true,
state: Mutex::new(Default::default()),
}, },
None => { None => {
// Pool is empty, alloc new semaphore // Pool is empty, alloc new semaphore
@ -167,7 +185,6 @@ impl Semaphore {
export_handle_types, export_handle_types,
must_put_in_pool: false, must_put_in_pool: false,
state: Mutex::new(Default::default()),
} }
} }
@ -178,21 +195,28 @@ impl Semaphore {
} }
/// Exports the semaphore into a POSIX file descriptor. The caller owns the returned `File`. /// Exports the semaphore into a POSIX file descriptor. The caller owns the returned `File`.
///
/// # Safety
///
/// - If `handle_type` has copy transference, then the semaphore must be signaled, or a signal
/// operation on the semaphore must be pending, and no wait operations must be pending.
/// - The semaphore must not currently have an imported payload from a swapchain acquire
/// operation.
/// - If the semaphore has an imported payload, its handle type must allow re-exporting as
/// `handle_type`, as returned by [`PhysicalDevice::external_semaphore_properties`].
#[inline] #[inline]
pub fn export_fd( pub unsafe fn export_fd(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
) -> Result<File, Validated<VulkanError>> { ) -> Result<File, Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_export_fd(handle_type)?;
self.validate_export_fd(handle_type, &state)?;
unsafe { Ok(self.export_fd_unchecked_locked(handle_type, &mut state)?) } unsafe { Ok(self.export_fd_unchecked(handle_type)?) }
} }
fn validate_export_fd( fn validate_export_fd(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
state: &SemaphoreState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().khr_external_semaphore_fd { if !self.device.enabled_extensions().khr_external_semaphore_fd {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -230,86 +254,13 @@ impl Semaphore {
})); }));
} }
if let Some(imported_handle_type) = state.current_import {
match imported_handle_type {
ImportType::SwapchainAcquire => {
return Err(Box::new(ValidationError {
problem: "the semaphore currently has an imported payload from a \
swapchain acquire operation"
.into(),
vuids: &["VUID-VkSemaphoreGetFdInfoKHR-semaphore-01133"],
..Default::default()
}));
}
ImportType::ExternalSemaphore(imported_handle_type) => {
let external_semaphore_properties = unsafe {
self.device
.physical_device()
.external_semaphore_properties_unchecked(
ExternalSemaphoreInfo::handle_type(handle_type),
)
};
if !external_semaphore_properties
.export_from_imported_handle_types
.intersects(imported_handle_type.into())
{
return Err(Box::new(ValidationError {
problem: "the semaphore currently has an imported payload, whose type \
does not allow re-exporting as `handle_type`, as \
returned by `PhysicalDevice::external_semaphore_properties`"
.into(),
vuids: &["VUID-VkSemaphoreGetFdInfoKHR-semaphore-01133"],
..Default::default()
}));
}
}
}
}
if handle_type.has_copy_transference() {
if state.is_wait_pending() {
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
a wait operation on the semaphore is pending"
.into(),
vuids: &["VUID-VkSemaphoreGetFdInfoKHR-handleType-01134"],
..Default::default()
}));
}
if !(state.is_signaled().unwrap_or(false) || state.is_signal_pending()) {
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
the semaphore is not signaled, and \
a signal operation on the semaphore is not pending"
.into(),
vuids: &[
"VUID-VkSemaphoreGetFdInfoKHR-handleType-01135",
"VUID-VkSemaphoreGetFdInfoKHR-handleType-03254",
],
..Default::default()
}));
}
}
Ok(()) Ok(())
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn export_fd_unchecked( pub unsafe fn export_fd_unchecked(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
) -> Result<File, VulkanError> {
let mut state = self.state.lock();
self.export_fd_unchecked_locked(handle_type, &mut state)
}
unsafe fn export_fd_unchecked_locked(
&self,
handle_type: ExternalSemaphoreHandleType,
state: &mut SemaphoreState,
) -> Result<File, VulkanError> { ) -> Result<File, VulkanError> {
let info_vk = ash::vk::SemaphoreGetFdInfoKHR { let info_vk = ash::vk::SemaphoreGetFdInfoKHR {
semaphore: self.handle, semaphore: self.handle,
@ -327,8 +278,6 @@ impl Semaphore {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.export(handle_type);
#[cfg(unix)] #[cfg(unix)]
{ {
use std::os::unix::io::FromRawFd; use std::os::unix::io::FromRawFd;
@ -346,21 +295,31 @@ impl Semaphore {
/// ///
/// The [`khr_external_semaphore_win32`](crate::device::DeviceExtensions::khr_external_semaphore_win32) /// The [`khr_external_semaphore_win32`](crate::device::DeviceExtensions::khr_external_semaphore_win32)
/// extension must be enabled on the device. /// extension must be enabled on the device.
///
/// # Safety
///
/// - If `handle_type` has copy transference, then the semaphore must be signaled, or a signal
/// operation on the semaphore must be pending, and no wait operations must be pending.
/// - The semaphore must not currently have an imported payload from a swapchain acquire
/// operation.
/// - If the semaphore has an imported payload, its handle type must allow re-exporting as
/// `handle_type`, as returned by [`PhysicalDevice::external_semaphore_properties`].
/// - If `handle_type` is `ExternalSemaphoreHandleType::OpaqueWin32` or
/// `ExternalSemaphoreHandleType::D3D12Fence`, then a handle of this type must not have been
/// already exported from this semaphore.
#[inline] #[inline]
pub fn export_win32_handle( pub fn export_win32_handle(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
) -> Result<*mut std::ffi::c_void, Validated<VulkanError>> { ) -> Result<*mut std::ffi::c_void, Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_export_win32_handle(handle_type)?;
self.validate_export_win32_handle(handle_type, &state)?;
unsafe { Ok(self.export_win32_handle_unchecked_locked(handle_type, &mut state)?) } unsafe { Ok(self.export_win32_handle_unchecked(handle_type)?) }
} }
fn validate_export_win32_handle( fn validate_export_win32_handle(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
state: &SemaphoreState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self if !self
.device .device
@ -405,98 +364,13 @@ impl Semaphore {
})); }));
} }
if matches!(
handle_type,
ExternalSemaphoreHandleType::OpaqueWin32 | ExternalSemaphoreHandleType::D3D12Fence
) && state.is_exported(handle_type)
{
return Err(Box::new(ValidationError {
problem: "`handle_type` is `ExternalSemaphoreHandleType::OpaqueWin32` or \
`ExternalSemaphoreHandleType::D3D12Fence`, but \
a handle of this type has already been exported from this semaphore"
.into(),
vuids: &["VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01127"],
..Default::default()
}));
}
if let Some(imported_handle_type) = state.current_import {
match imported_handle_type {
ImportType::SwapchainAcquire => {
return Err(Box::new(ValidationError {
problem: "the semaphore currently has an imported payload from a \
swapchain acquire operation"
.into(),
vuids: &["VUID-VkSemaphoreGetWin32HandleInfoKHR-semaphore-01128"],
..Default::default()
}));
}
ImportType::ExternalSemaphore(imported_handle_type) => {
let external_semaphore_properties = unsafe {
self.device
.physical_device()
.external_semaphore_properties_unchecked(
ExternalSemaphoreInfo::handle_type(handle_type),
)
};
if !external_semaphore_properties
.export_from_imported_handle_types
.intersects(imported_handle_type.into())
{
return Err(Box::new(ValidationError {
problem: "the semaphore currently has an imported payload, whose type \
does not allow re-exporting as `handle_type`, as \
returned by `PhysicalDevice::external_semaphore_properties`"
.into(),
vuids: &["VUID-VkSemaphoreGetWin32HandleInfoKHR-semaphore-01128"],
..Default::default()
}));
}
}
}
}
if handle_type.has_copy_transference() {
if state.is_wait_pending() {
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
a wait operation on the semaphore is pending"
.into(),
vuids: &["VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01129"],
..Default::default()
}));
}
if !(state.is_signaled().unwrap_or(false) || state.is_signal_pending()) {
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
the semaphore is not signaled, and \
a signal operation on the semaphore is not pending"
.into(),
vuids: &["VUID-VkSemaphoreGetWin32HandleInfoKHR-handleType-01130"],
..Default::default()
}));
}
}
Ok(()) Ok(())
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn export_win32_handle_unchecked( pub unsafe fn export_win32_handle_unchecked(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
) -> Result<*mut std::ffi::c_void, VulkanError> {
let mut state = self.state.lock();
self.export_win32_handle_unchecked_locked(handle_type, &mut state)
}
unsafe fn export_win32_handle_unchecked_locked(
&self,
handle_type: ExternalSemaphoreHandleType,
state: &mut SemaphoreState,
) -> Result<*mut std::ffi::c_void, VulkanError> { ) -> Result<*mut std::ffi::c_void, VulkanError> {
let info_vk = ash::vk::SemaphoreGetWin32HandleInfoKHR { let info_vk = ash::vk::SemaphoreGetWin32HandleInfoKHR {
semaphore: self.handle, semaphore: self.handle,
@ -513,27 +387,32 @@ impl Semaphore {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.export(handle_type);
Ok(output.assume_init()) Ok(output.assume_init())
} }
/// Exports the semaphore into a Zircon event handle. /// Exports the semaphore into a Zircon event handle.
///
/// # Safety
///
/// - If `handle_type` has copy transference, then the semaphore must be signaled, or a signal
/// operation on the semaphore must be pending, and no wait operations must be pending.
/// - The semaphore must not currently have an imported payload from a swapchain acquire
/// operation.
/// - If the semaphore has an imported payload, its handle type must allow re-exporting as
/// `handle_type`, as returned by [`PhysicalDevice::external_semaphore_properties`].
#[inline] #[inline]
pub fn export_zircon_handle( pub unsafe fn export_zircon_handle(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
) -> Result<ash::vk::zx_handle_t, Validated<VulkanError>> { ) -> Result<ash::vk::zx_handle_t, Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_export_zircon_handle(handle_type)?;
self.validate_export_zircon_handle(handle_type, &state)?;
unsafe { Ok(self.export_zircon_handle_unchecked_locked(handle_type, &mut state)?) } unsafe { Ok(self.export_zircon_handle_unchecked(handle_type)?) }
} }
fn validate_export_zircon_handle( fn validate_export_zircon_handle(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
state: &SemaphoreState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().fuchsia_external_semaphore { if !self.device.enabled_extensions().fuchsia_external_semaphore {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -566,83 +445,13 @@ impl Semaphore {
})); }));
} }
if let Some(imported_handle_type) = state.current_import {
match imported_handle_type {
ImportType::SwapchainAcquire => {
return Err(Box::new(ValidationError {
problem: "the semaphore currently has an imported payload from a \
swapchain acquire operation"
.into(),
vuids: &["VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-semaphore-04759"],
..Default::default()
}));
}
ImportType::ExternalSemaphore(imported_handle_type) => {
let external_semaphore_properties = unsafe {
self.device
.physical_device()
.external_semaphore_properties_unchecked(
ExternalSemaphoreInfo::handle_type(handle_type),
)
};
if !external_semaphore_properties
.export_from_imported_handle_types
.intersects(imported_handle_type.into())
{
return Err(Box::new(ValidationError {
problem: "the semaphore currently has an imported payload, whose type \
does not allow re-exporting as `handle_type`, as \
returned by `PhysicalDevice::external_semaphore_properties`"
.into(),
vuids: &["VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-semaphore-04759"],
..Default::default()
}));
}
}
}
}
if handle_type.has_copy_transference() {
if state.is_wait_pending() {
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
a wait operation on the semaphore is pending"
.into(),
vuids: &["VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-04760"],
..Default::default()
}));
}
if !(state.is_signaled().unwrap_or(false) || state.is_signal_pending()) {
return Err(Box::new(ValidationError {
problem: "`handle_type` has copy transference, but \
the semaphore is not signaled, and \
a signal operation on the semaphore is not pending"
.into(),
vuids: &["VUID-VkSemaphoreGetZirconHandleInfoFUCHSIA-handleType-04761"],
..Default::default()
}));
}
}
Ok(()) Ok(())
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn export_zircon_handle_unchecked( pub unsafe fn export_zircon_handle_unchecked(
&self, &self,
handle_type: ExternalSemaphoreHandleType, handle_type: ExternalSemaphoreHandleType,
) -> Result<ash::vk::zx_handle_t, VulkanError> {
let mut state = self.state.lock();
self.export_zircon_handle_unchecked_locked(handle_type, &mut state)
}
unsafe fn export_zircon_handle_unchecked_locked(
&self,
handle_type: ExternalSemaphoreHandleType,
state: &mut SemaphoreState,
) -> Result<ash::vk::zx_handle_t, VulkanError> { ) -> Result<ash::vk::zx_handle_t, VulkanError> {
let info_vk = ash::vk::SemaphoreGetZirconHandleInfoFUCHSIA { let info_vk = ash::vk::SemaphoreGetZirconHandleInfoFUCHSIA {
semaphore: self.handle, semaphore: self.handle,
@ -661,8 +470,6 @@ impl Semaphore {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.export(handle_type);
Ok(output.assume_init()) Ok(output.assume_init())
} }
@ -673,6 +480,7 @@ impl Semaphore {
/// ///
/// # Safety /// # Safety
/// ///
/// - The semaphore must not be in use by the device.
/// - If in `import_semaphore_fd_info`, `handle_type` is `ExternalHandleType::OpaqueFd`, /// - If in `import_semaphore_fd_info`, `handle_type` is `ExternalHandleType::OpaqueFd`,
/// then `file` must represent a binary semaphore that was exported from Vulkan or a /// then `file` must represent a binary semaphore that was exported from Vulkan or a
/// compatible API, with a driver and device UUID equal to those of the device that owns /// compatible API, with a driver and device UUID equal to those of the device that owns
@ -682,16 +490,14 @@ impl Semaphore {
&self, &self,
import_semaphore_fd_info: ImportSemaphoreFdInfo, import_semaphore_fd_info: ImportSemaphoreFdInfo,
) -> Result<(), Validated<VulkanError>> { ) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_import_fd(&import_semaphore_fd_info)?;
self.validate_import_fd(&import_semaphore_fd_info, &state)?;
Ok(self.import_fd_unchecked_locked(import_semaphore_fd_info, &mut state)?) Ok(self.import_fd_unchecked(import_semaphore_fd_info)?)
} }
fn validate_import_fd( fn validate_import_fd(
&self, &self,
import_semaphore_fd_info: &ImportSemaphoreFdInfo, import_semaphore_fd_info: &ImportSemaphoreFdInfo,
state: &SemaphoreState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().khr_external_semaphore_fd { if !self.device.enabled_extensions().khr_external_semaphore_fd {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -702,14 +508,6 @@ impl Semaphore {
})); }));
} }
if state.is_in_queue() {
return Err(Box::new(ValidationError {
problem: "the semaphore is in use".into(),
vuids: &["VUID-vkImportSemaphoreFdKHR-semaphore-01142"],
..Default::default()
}));
}
import_semaphore_fd_info import_semaphore_fd_info
.validate(&self.device) .validate(&self.device)
.map_err(|err| err.add_context("import_semaphore_fd_info"))?; .map_err(|err| err.add_context("import_semaphore_fd_info"))?;
@ -718,19 +516,9 @@ impl Semaphore {
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn import_fd_unchecked( pub unsafe fn import_fd_unchecked(
&self, &self,
import_semaphore_fd_info: ImportSemaphoreFdInfo, import_semaphore_fd_info: ImportSemaphoreFdInfo,
) -> Result<(), VulkanError> {
let mut state = self.state.lock();
self.import_fd_unchecked_locked(import_semaphore_fd_info, &mut state)
}
unsafe fn import_fd_unchecked_locked(
&self,
import_semaphore_fd_info: ImportSemaphoreFdInfo,
state: &mut SemaphoreState,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let ImportSemaphoreFdInfo { let ImportSemaphoreFdInfo {
flags, flags,
@ -764,11 +552,6 @@ impl Semaphore {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.import(
handle_type,
flags.intersects(SemaphoreImportFlags::TEMPORARY),
);
Ok(()) Ok(())
} }
@ -779,6 +562,7 @@ impl Semaphore {
/// ///
/// # Safety /// # Safety
/// ///
/// - The semaphore must not be in use by the device.
/// - In `import_semaphore_win32_handle_info`, `handle` must represent a binary semaphore that /// - In `import_semaphore_win32_handle_info`, `handle` must represent a binary semaphore that
/// was exported from Vulkan or a compatible API, with a driver and device UUID equal to /// was exported from Vulkan or a compatible API, with a driver and device UUID equal to
/// those of the device that owns `self`. /// those of the device that owns `self`.
@ -787,17 +571,14 @@ impl Semaphore {
&self, &self,
import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo, import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo,
) -> Result<(), Validated<VulkanError>> { ) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_import_win32_handle(&import_semaphore_win32_handle_info)?;
self.validate_import_win32_handle(&import_semaphore_win32_handle_info, &state)?;
Ok(self Ok(self.import_win32_handle_unchecked(import_semaphore_win32_handle_info)?)
.import_win32_handle_unchecked_locked(import_semaphore_win32_handle_info, &mut state)?)
} }
fn validate_import_win32_handle( fn validate_import_win32_handle(
&self, &self,
import_semaphore_win32_handle_info: &ImportSemaphoreWin32HandleInfo, import_semaphore_win32_handle_info: &ImportSemaphoreWin32HandleInfo,
state: &SemaphoreState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self if !self
.device .device
@ -812,14 +593,6 @@ impl Semaphore {
})); }));
} }
if state.is_in_queue() {
return Err(Box::new(ValidationError {
problem: "the semaphore is in use".into(),
// vuids?
..Default::default()
}));
}
import_semaphore_win32_handle_info import_semaphore_win32_handle_info
.validate(&self.device) .validate(&self.device)
.map_err(|err| err.add_context("import_semaphore_win32_handle_info"))?; .map_err(|err| err.add_context("import_semaphore_win32_handle_info"))?;
@ -828,19 +601,9 @@ impl Semaphore {
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn import_win32_handle_unchecked( pub unsafe fn import_win32_handle_unchecked(
&self, &self,
import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo, import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo,
) -> Result<(), VulkanError> {
let mut state = self.state.lock();
self.import_win32_handle_unchecked_locked(import_semaphore_win32_handle_info, &mut state)
}
unsafe fn import_win32_handle_unchecked_locked(
&self,
import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo,
state: &mut SemaphoreState,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let ImportSemaphoreWin32HandleInfo { let ImportSemaphoreWin32HandleInfo {
flags, flags,
@ -864,11 +627,6 @@ impl Semaphore {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.import(
handle_type,
flags.intersects(SemaphoreImportFlags::TEMPORARY),
);
Ok(()) Ok(())
} }
@ -879,6 +637,7 @@ impl Semaphore {
/// ///
/// # Safety /// # Safety
/// ///
/// - The semaphore must not be in use by the device.
/// - In `import_semaphore_zircon_handle_info`, `zircon_handle` must have `ZX_RIGHTS_BASIC` and /// - In `import_semaphore_zircon_handle_info`, `zircon_handle` must have `ZX_RIGHTS_BASIC` and
/// `ZX_RIGHTS_SIGNAL`. /// `ZX_RIGHTS_SIGNAL`.
#[inline] #[inline]
@ -886,19 +645,14 @@ impl Semaphore {
&self, &self,
import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo, import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo,
) -> Result<(), Validated<VulkanError>> { ) -> Result<(), Validated<VulkanError>> {
let mut state = self.state.lock(); self.validate_import_zircon_handle(&import_semaphore_zircon_handle_info)?;
self.validate_import_zircon_handle(&import_semaphore_zircon_handle_info, &state)?;
Ok(self.import_zircon_handle_unchecked_locked( Ok(self.import_zircon_handle_unchecked(import_semaphore_zircon_handle_info)?)
import_semaphore_zircon_handle_info,
&mut state,
)?)
} }
fn validate_import_zircon_handle( fn validate_import_zircon_handle(
&self, &self,
import_semaphore_zircon_handle_info: &ImportSemaphoreZirconHandleInfo, import_semaphore_zircon_handle_info: &ImportSemaphoreZirconHandleInfo,
state: &SemaphoreState,
) -> Result<(), Box<ValidationError>> { ) -> Result<(), Box<ValidationError>> {
if !self.device.enabled_extensions().fuchsia_external_semaphore { if !self.device.enabled_extensions().fuchsia_external_semaphore {
return Err(Box::new(ValidationError { return Err(Box::new(ValidationError {
@ -909,14 +663,6 @@ impl Semaphore {
})); }));
} }
if state.is_in_queue() {
return Err(Box::new(ValidationError {
problem: "the semaphore is in use".into(),
vuids: &["VUID-vkImportSemaphoreZirconHandleFUCHSIA-semaphore-04764"],
..Default::default()
}));
}
import_semaphore_zircon_handle_info import_semaphore_zircon_handle_info
.validate(&self.device) .validate(&self.device)
.map_err(|err| err.add_context("import_semaphore_zircon_handle_info"))?; .map_err(|err| err.add_context("import_semaphore_zircon_handle_info"))?;
@ -925,19 +671,9 @@ impl Semaphore {
} }
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
#[inline]
pub unsafe fn import_zircon_handle_unchecked( pub unsafe fn import_zircon_handle_unchecked(
&self, &self,
import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo, import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo,
) -> Result<(), VulkanError> {
let mut state = self.state.lock();
self.import_zircon_handle_unchecked_locked(import_semaphore_zircon_handle_info, &mut state)
}
unsafe fn import_zircon_handle_unchecked_locked(
&self,
import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo,
state: &mut SemaphoreState,
) -> Result<(), VulkanError> { ) -> Result<(), VulkanError> {
let ImportSemaphoreZirconHandleInfo { let ImportSemaphoreZirconHandleInfo {
flags, flags,
@ -960,17 +696,8 @@ impl Semaphore {
.result() .result()
.map_err(VulkanError::from)?; .map_err(VulkanError::from)?;
state.import(
handle_type,
flags.intersects(SemaphoreImportFlags::TEMPORARY),
);
Ok(()) Ok(())
} }
pub(crate) fn state(&self) -> MutexGuard<'_, SemaphoreState> {
self.state.lock()
}
} }
impl Drop for Semaphore { impl Drop for Semaphore {
@ -1498,136 +1225,6 @@ pub struct ExternalSemaphoreProperties {
pub compatible_handle_types: ExternalSemaphoreHandleTypes, pub compatible_handle_types: ExternalSemaphoreHandleTypes,
} }
#[derive(Debug, Default)]
pub(crate) struct SemaphoreState {
is_signaled: bool,
pending_signal: Option<SignalType>,
pending_wait: Option<Weak<Queue>>,
reference_exported: bool,
exported_handle_types: ExternalSemaphoreHandleTypes,
current_import: Option<ImportType>,
permanent_import: Option<ExternalSemaphoreHandleType>,
}
impl SemaphoreState {
/// If the semaphore does not have a pending operation and has no external references,
/// returns the current status.
#[inline]
fn is_signaled(&self) -> Option<bool> {
// If any of these is true, we can't be certain of the status.
if self.pending_signal.is_some()
|| self.pending_wait.is_some()
|| self.has_external_reference()
{
None
} else {
Some(self.is_signaled)
}
}
#[inline]
fn is_signal_pending(&self) -> bool {
self.pending_signal.is_some()
}
#[inline]
fn is_wait_pending(&self) -> bool {
self.pending_wait.is_some()
}
#[inline]
fn is_in_queue(&self) -> bool {
matches!(self.pending_signal, Some(SignalType::Queue(_))) || self.pending_wait.is_some()
}
/// Returns whether there are any potential external references to the semaphore payload.
/// That is, the semaphore has been exported by reference transference, or imported.
#[inline]
fn has_external_reference(&self) -> bool {
self.reference_exported || self.current_import.is_some()
}
#[allow(dead_code)]
#[inline]
fn is_exported(&self, handle_type: ExternalSemaphoreHandleType) -> bool {
self.exported_handle_types.intersects(handle_type.into())
}
#[inline]
pub(crate) unsafe fn add_queue_signal(&mut self, queue: &Arc<Queue>) {
self.pending_signal = Some(SignalType::Queue(Arc::downgrade(queue)));
}
#[inline]
pub(crate) unsafe fn add_queue_wait(&mut self, queue: &Arc<Queue>) {
self.pending_wait = Some(Arc::downgrade(queue));
}
/// Called when a queue is unlocking resources.
#[inline]
pub(crate) unsafe fn set_signal_finished(&mut self) {
self.pending_signal = None;
self.is_signaled = true;
}
/// Called when a queue is unlocking resources.
#[inline]
pub(crate) unsafe fn set_wait_finished(&mut self) {
self.pending_wait = None;
self.current_import = self.permanent_import.map(Into::into);
self.is_signaled = false;
}
#[allow(dead_code)]
#[inline]
unsafe fn export(&mut self, handle_type: ExternalSemaphoreHandleType) {
self.exported_handle_types |= handle_type.into();
if handle_type.has_copy_transference() {
self.current_import = self.permanent_import.map(Into::into);
self.is_signaled = false;
} else {
self.reference_exported = true;
}
}
#[allow(dead_code)]
#[inline]
unsafe fn import(&mut self, handle_type: ExternalSemaphoreHandleType, temporary: bool) {
self.current_import = Some(handle_type.into());
if !temporary {
self.permanent_import = Some(handle_type);
}
}
#[inline]
pub(crate) unsafe fn swapchain_acquire(&mut self) {
self.pending_signal = Some(SignalType::SwapchainAcquire);
self.current_import = Some(ImportType::SwapchainAcquire);
}
}
#[derive(Clone, Debug)]
enum SignalType {
Queue(Weak<Queue>),
SwapchainAcquire,
}
#[derive(Clone, Copy, Debug)]
enum ImportType {
SwapchainAcquire,
ExternalSemaphore(ExternalSemaphoreHandleType),
}
impl From<ExternalSemaphoreHandleType> for ImportType {
#[inline]
fn from(handle_type: ExternalSemaphoreHandleType) -> Self {
Self::ExternalSemaphore(handle_type)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
@ -1717,8 +1314,9 @@ mod tests {
}, },
) )
.unwrap(); .unwrap();
let _fd = sem let _fd = unsafe {
.export_fd(ExternalSemaphoreHandleType::OpaqueFd) sem.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
.unwrap(); .unwrap()
};
} }
} }