mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-22 06:45:23 +00:00
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:
parent
151e5c49cb
commit
def21843fc
@ -189,12 +189,16 @@ mod linux {
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let acquire_fd = acquire_sem
|
||||
let acquire_fd = unsafe {
|
||||
acquire_sem
|
||||
.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
|
||||
.unwrap();
|
||||
let release_fd = release_sem
|
||||
.unwrap()
|
||||
};
|
||||
let release_fd = unsafe {
|
||||
release_sem
|
||||
.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let barrier_clone = barrier.clone();
|
||||
let barrier_2_clone = barrier_2.clone();
|
||||
@ -300,9 +304,9 @@ mod linux {
|
||||
Event::RedrawEventsCleared => {
|
||||
queue
|
||||
.with(|mut q| unsafe {
|
||||
q.submit_unchecked(
|
||||
[SubmitInfo {
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore(
|
||||
q.submit(
|
||||
&[SubmitInfo {
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::new(
|
||||
acquire_sem.clone(),
|
||||
)],
|
||||
..Default::default()
|
||||
@ -317,9 +321,9 @@ mod linux {
|
||||
|
||||
queue
|
||||
.with(|mut q| unsafe {
|
||||
q.submit_unchecked(
|
||||
[SubmitInfo {
|
||||
wait_semaphores: vec![SemaphoreSubmitInfo::semaphore(
|
||||
q.submit(
|
||||
&[SubmitInfo {
|
||||
wait_semaphores: vec![SemaphoreSubmitInfo::new(
|
||||
release_sem.clone(),
|
||||
)],
|
||||
..Default::default()
|
||||
|
@ -131,6 +131,10 @@ unsafe impl<A> PrimaryCommandBufferAbstract for PrimaryAutoCommandBuffer<A>
|
||||
where
|
||||
A: CommandBufferAllocator,
|
||||
{
|
||||
fn queue_family_index(&self) -> u32 {
|
||||
self.inner.queue_family_index()
|
||||
}
|
||||
|
||||
fn usage(&self) -> CommandBufferUsage {
|
||||
self.inner.usage()
|
||||
}
|
||||
|
@ -104,7 +104,7 @@
|
||||
//! [`StandardCommandBufferAllocator`]: self::allocator::StandardCommandBufferAllocator
|
||||
//! [`CommandBufferAllocator`]: self::allocator::CommandBufferAllocator
|
||||
//! [inherit]: CommandBufferInheritanceInfo
|
||||
//! [`build`]: CommandBufferBuilder::build
|
||||
//! [`build`]: AutoCommandBufferBuilder::build
|
||||
//! [pipeline barriers]: CommandBufferBuilder::pipeline_barrier
|
||||
//! [`GpuFuture`]: crate::sync::GpuFuture
|
||||
|
||||
@ -723,7 +723,7 @@ pub struct SubmitInfo {
|
||||
/// The command buffers to execute.
|
||||
///
|
||||
/// 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
|
||||
/// 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)]
|
||||
pub struct SemaphoreSubmitInfo {
|
||||
/// The semaphore to signal or wait for.
|
||||
///
|
||||
/// There is no default value.
|
||||
pub semaphore: Arc<Semaphore>,
|
||||
|
||||
/// For a semaphore wait operation, specifies the pipeline stages in the second synchronization
|
||||
@ -774,13 +859,191 @@ pub struct SemaphoreSubmitInfo {
|
||||
impl SemaphoreSubmitInfo {
|
||||
/// Returns a `SemaphoreSubmitInfo` with the specified `semaphore`.
|
||||
#[inline]
|
||||
pub fn semaphore(semaphore: Arc<Semaphore>) -> Self {
|
||||
pub fn new(semaphore: Arc<Semaphore>) -> Self {
|
||||
Self {
|
||||
semaphore,
|
||||
stages: PipelineStages::ALL_COMMANDS,
|
||||
_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)]
|
||||
|
@ -9,7 +9,8 @@
|
||||
|
||||
use super::{
|
||||
CommandBufferInheritanceInfo, CommandBufferResourcesUsage, CommandBufferState,
|
||||
CommandBufferUsage, SecondaryCommandBufferResourcesUsage, SemaphoreSubmitInfo, SubmitInfo,
|
||||
CommandBufferSubmitInfo, CommandBufferUsage, SecondaryCommandBufferResourcesUsage,
|
||||
SemaphoreSubmitInfo, SubmitInfo,
|
||||
};
|
||||
use crate::{
|
||||
buffer::Buffer,
|
||||
@ -17,7 +18,10 @@ use crate::{
|
||||
image::{Image, ImageLayout},
|
||||
swapchain::Swapchain,
|
||||
sync::{
|
||||
future::{now, AccessCheckError, AccessError, GpuFuture, NowFuture, SubmitAnyBuilder},
|
||||
future::{
|
||||
now, queue_submit, AccessCheckError, AccessError, GpuFuture, NowFuture,
|
||||
SubmitAnyBuilder,
|
||||
},
|
||||
PipelineStages,
|
||||
},
|
||||
DeviceSize, SafeDeref, Validated, ValidationError, VulkanError, VulkanObject,
|
||||
@ -38,6 +42,9 @@ use std::{
|
||||
pub unsafe trait PrimaryCommandBufferAbstract:
|
||||
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.
|
||||
fn usage(&self) -> CommandBufferUsage;
|
||||
|
||||
@ -143,6 +150,10 @@ where
|
||||
T: VulkanObject<Handle = ash::vk::CommandBuffer> + SafeDeref + Send + Sync,
|
||||
T::Target: PrimaryCommandBufferAbstract,
|
||||
{
|
||||
fn queue_family_index(&self) -> u32 {
|
||||
(**self).queue_family_index()
|
||||
}
|
||||
|
||||
fn usage(&self) -> CommandBufferUsage {
|
||||
(**self).usage()
|
||||
}
|
||||
@ -237,7 +248,9 @@ where
|
||||
Ok(match self.previous.build_submission()? {
|
||||
SubmitAnyBuilder::Empty => SubmitAnyBuilder::CommandBuffer(
|
||||
SubmitInfo {
|
||||
command_buffers: vec![self.command_buffer.clone()],
|
||||
command_buffers: vec![CommandBufferSubmitInfo::new(
|
||||
self.command_buffer.clone(),
|
||||
)],
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
@ -251,11 +264,13 @@ where
|
||||
SemaphoreSubmitInfo {
|
||||
// TODO: correct stages ; hard
|
||||
stages: PipelineStages::ALL_COMMANDS,
|
||||
..SemaphoreSubmitInfo::semaphore(semaphore)
|
||||
..SemaphoreSubmitInfo::new(semaphore)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
command_buffers: vec![self.command_buffer.clone()],
|
||||
command_buffers: vec![CommandBufferSubmitInfo::new(
|
||||
self.command_buffer.clone(),
|
||||
)],
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
@ -265,7 +280,7 @@ where
|
||||
// FIXME: add pipeline barrier
|
||||
submit_info
|
||||
.command_buffers
|
||||
.push(self.command_buffer.clone());
|
||||
.push(CommandBufferSubmitInfo::new(self.command_buffer.clone()));
|
||||
SubmitAnyBuilder::CommandBuffer(submit_info, fence)
|
||||
}
|
||||
SubmitAnyBuilder::QueuePresent(_) | SubmitAnyBuilder::BindSparse(_, _) => {
|
||||
@ -305,9 +320,7 @@ where
|
||||
match self.build_submission_impl()? {
|
||||
SubmitAnyBuilder::Empty => {}
|
||||
SubmitAnyBuilder::CommandBuffer(submit_info, fence) => {
|
||||
self.queue.with(|mut q| {
|
||||
q.submit_with_future(submit_info, fence, &self.previous, &self.queue)
|
||||
})?;
|
||||
queue_submit(&self.queue, submit_info, fence, &self.previous).unwrap();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@ -319,7 +332,36 @@ where
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@ -445,7 +487,10 @@ where
|
||||
self.flush().unwrap();
|
||||
// Block until the queue finished.
|
||||
self.queue.with(|mut q| q.wait_idle()).unwrap();
|
||||
unsafe { self.previous.signal_finished() };
|
||||
|
||||
unsafe {
|
||||
self.signal_finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,7 @@
|
||||
//! [`DescriptorSet`]. It is what you pass to the draw functions.
|
||||
//!
|
||||
//! [`DescriptorPool`]: pool::DescriptorPool
|
||||
//! [`UnsafeDescriptorSet`]: sys::UnsafeDescriptorSet
|
||||
//! [`DescriptorSetAllocator`]: allocator::DescriptorSetAllocator
|
||||
//! [`StandardDescriptorSetAllocator`]: allocator::StandardDescriptorSetAllocator
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,10 +14,11 @@ use crate::{
|
||||
image::{Image, ImageLayout},
|
||||
sync::{
|
||||
fence::Fence,
|
||||
future::{AccessCheckError, AccessError, GpuFuture, SubmitAnyBuilder},
|
||||
future::{queue_present, AccessCheckError, AccessError, GpuFuture, SubmitAnyBuilder},
|
||||
semaphore::Semaphore,
|
||||
},
|
||||
DeviceSize, Validated, ValidationError, VulkanError, VulkanObject,
|
||||
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError,
|
||||
VulkanObject,
|
||||
};
|
||||
use smallvec::smallvec;
|
||||
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((
|
||||
image_index,
|
||||
is_suboptimal,
|
||||
@ -216,16 +207,6 @@ pub unsafe fn acquire_next_image_raw(
|
||||
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 {
|
||||
image_index: out.assume_init(),
|
||||
is_suboptimal,
|
||||
@ -431,12 +412,12 @@ pub struct PresentInfo {
|
||||
/// The semaphores to wait for before beginning the execution of the present operations.
|
||||
///
|
||||
/// The default value is empty.
|
||||
pub wait_semaphores: Vec<Arc<Semaphore>>,
|
||||
pub wait_semaphores: Vec<SemaphorePresentInfo>,
|
||||
|
||||
/// The present operations to perform.
|
||||
///
|
||||
/// The default value is empty.
|
||||
pub swapchain_infos: Vec<SwapchainPresentInfo>,
|
||||
pub swapchains: Vec<SwapchainPresentInfo>,
|
||||
|
||||
pub _ne: crate::NonExhaustive,
|
||||
}
|
||||
@ -446,12 +427,79 @@ impl Default for PresentInfo {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
wait_semaphores: Vec::new(),
|
||||
swapchain_infos: Vec::new(),
|
||||
swapchains: Vec::new(),
|
||||
_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.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SwapchainPresentInfo {
|
||||
@ -530,6 +578,113 @@ impl SwapchainPresentInfo {
|
||||
_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.
|
||||
@ -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.
|
||||
#[must_use = "Dropping this object will immediately block the thread until the GPU has finished processing the submission"]
|
||||
pub struct PresentFuture<P>
|
||||
@ -640,13 +829,16 @@ where
|
||||
|
||||
Ok(match self.previous.build_submission()? {
|
||||
SubmitAnyBuilder::Empty => SubmitAnyBuilder::QueuePresent(PresentInfo {
|
||||
swapchain_infos: vec![self.swapchain_info.clone()],
|
||||
swapchains: vec![self.swapchain_info.clone()],
|
||||
..Default::default()
|
||||
}),
|
||||
SubmitAnyBuilder::SemaphoresWait(semaphores) => {
|
||||
SubmitAnyBuilder::QueuePresent(PresentInfo {
|
||||
wait_semaphores: semaphores.into_iter().collect(),
|
||||
swapchain_infos: vec![self.swapchain_info.clone()],
|
||||
wait_semaphores: semaphores
|
||||
.into_iter()
|
||||
.map(SemaphorePresentInfo::new)
|
||||
.collect(),
|
||||
swapchains: vec![self.swapchain_info.clone()],
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
@ -654,7 +846,7 @@ where
|
||||
self.previous.flush()?;
|
||||
|
||||
SubmitAnyBuilder::QueuePresent(PresentInfo {
|
||||
swapchain_infos: vec![self.swapchain_info.clone()],
|
||||
swapchains: vec![self.swapchain_info.clone()],
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
@ -662,26 +854,24 @@ where
|
||||
self.previous.flush()?;
|
||||
|
||||
SubmitAnyBuilder::QueuePresent(PresentInfo {
|
||||
swapchain_infos: vec![self.swapchain_info.clone()],
|
||||
swapchains: vec![self.swapchain_info.clone()],
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
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()
|
||||
}) {
|
||||
// If the present mode Option variants don't match, create a new command.
|
||||
self.previous.flush()?;
|
||||
|
||||
SubmitAnyBuilder::QueuePresent(PresentInfo {
|
||||
swapchain_infos: vec![self.swapchain_info.clone()],
|
||||
swapchains: vec![self.swapchain_info.clone()],
|
||||
..Default::default()
|
||||
})
|
||||
} else {
|
||||
// Otherwise, add our swapchain to the previous.
|
||||
present_info
|
||||
.swapchain_infos
|
||||
.push(self.swapchain_info.clone());
|
||||
present_info.swapchains.push(self.swapchain_info.clone());
|
||||
|
||||
SubmitAnyBuilder::QueuePresent(present_info)
|
||||
}
|
||||
@ -701,21 +891,17 @@ where
|
||||
SubmitAnyBuilder::QueuePresent(present_info) => {
|
||||
let PresentInfo {
|
||||
wait_semaphores: _,
|
||||
swapchain_infos,
|
||||
swapchains,
|
||||
_ne: _,
|
||||
} = &present_info;
|
||||
|
||||
let has_present_mode = swapchain_infos
|
||||
.first()
|
||||
.map_or(false, |first| first.present_mode.is_some());
|
||||
|
||||
for swapchain_info in swapchain_infos {
|
||||
for swapchain_info in swapchains {
|
||||
let &SwapchainPresentInfo {
|
||||
ref swapchain,
|
||||
image_index: _,
|
||||
present_id,
|
||||
present_regions: _,
|
||||
present_mode,
|
||||
present_mode: _,
|
||||
_ne: _,
|
||||
} = swapchain_info;
|
||||
|
||||
@ -731,25 +917,6 @@ where
|
||||
})
|
||||
.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(
|
||||
@ -769,9 +936,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
Ok(self
|
||||
.queue
|
||||
.with(|mut q| q.present_unchecked(present_info))?
|
||||
Ok(queue_present(&self.queue, present_info)?
|
||||
.map(|r| r.map(|_| ()))
|
||||
.fold(Ok(()), Result::and)?)
|
||||
}
|
||||
|
@ -9,15 +9,38 @@
|
||||
|
||||
//! A fence provides synchronization between the device and the host, or between an external source
|
||||
//! 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::{
|
||||
device::{physical::PhysicalDevice, Device, DeviceOwned, Queue},
|
||||
device::{physical::PhysicalDevice, Device, DeviceOwned},
|
||||
instance::InstanceOwnedDebugWrapper,
|
||||
macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum},
|
||||
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
|
||||
VulkanObject,
|
||||
};
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use smallvec::SmallVec;
|
||||
use std::fs::File;
|
||||
use std::{
|
||||
@ -26,32 +49,12 @@ use std::{
|
||||
num::NonZeroU64,
|
||||
pin::Pin,
|
||||
ptr,
|
||||
sync::{Arc, Weak},
|
||||
sync::Arc,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// 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)]
|
||||
pub struct Fence {
|
||||
handle: ash::vk::Fence,
|
||||
@ -62,7 +65,6 @@ pub struct Fence {
|
||||
export_handle_types: ExternalFenceHandleTypes,
|
||||
|
||||
must_put_in_pool: bool,
|
||||
state: Mutex<FenceState>,
|
||||
}
|
||||
|
||||
impl Fence {
|
||||
@ -141,10 +143,6 @@ impl Fence {
|
||||
export_handle_types,
|
||||
|
||||
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(),
|
||||
|
||||
must_put_in_pool: true,
|
||||
state: Mutex::new(Default::default()),
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@ -218,10 +215,6 @@ impl Fence {
|
||||
export_handle_types,
|
||||
|
||||
must_put_in_pool: false,
|
||||
state: Mutex::new(FenceState {
|
||||
is_signaled: flags.intersects(FenceCreateFlags::SIGNALED),
|
||||
..Default::default()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -240,51 +233,22 @@ impl Fence {
|
||||
/// Returns true if the fence is signaled.
|
||||
#[inline]
|
||||
pub fn is_signaled(&self) -> Result<bool, VulkanError> {
|
||||
let queue_to_signal = {
|
||||
let mut state = self.state();
|
||||
|
||||
// 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)),
|
||||
ash::vk::Result::SUCCESS => Ok(true),
|
||||
ash::vk::Result::NOT_READY => Ok(false),
|
||||
err => Err(VulkanError::from(err)),
|
||||
}
|
||||
};
|
||||
|
||||
// If we have a queue that we need to signal our status to,
|
||||
// do so now after the state lock is dropped, to avoid deadlocks.
|
||||
if let Some(queue) = queue_to_signal {
|
||||
unsafe {
|
||||
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.
|
||||
///
|
||||
/// If you pass a duration of 0, then the function will return without blocking.
|
||||
pub fn wait(&self, timeout: Option<Duration>) -> Result<(), VulkanError> {
|
||||
let queue_to_signal = {
|
||||
let mut state = self.state.lock();
|
||||
|
||||
// If the fence is already signaled, we don't need to wait.
|
||||
if state.is_signaled().unwrap_or(false) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let timeout_ns = timeout.map_or(u64::MAX, |timeout| {
|
||||
timeout
|
||||
.as_secs()
|
||||
@ -304,20 +268,9 @@ impl Fence {
|
||||
};
|
||||
|
||||
match result {
|
||||
ash::vk::Result::SUCCESS => unsafe { state.set_signaled() },
|
||||
err => return Err(VulkanError::from(err)),
|
||||
ash::vk::Result::SUCCESS => Ok(()),
|
||||
err => Err(VulkanError::from(err)),
|
||||
}
|
||||
};
|
||||
|
||||
// If we have a queue that we need to signal our status to,
|
||||
// do so now after the state lock is dropped, to avoid deadlocks.
|
||||
if let Some(queue) = queue_to_signal {
|
||||
unsafe {
|
||||
queue.with(|mut q| q.fence_signaled(self));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Waits for multiple fences at once.
|
||||
@ -358,25 +311,17 @@ impl Fence {
|
||||
fences: impl IntoIterator<Item = &'a Fence>,
|
||||
timeout: Option<Duration>,
|
||||
) -> Result<(), VulkanError> {
|
||||
let queues_to_signal: SmallVec<[_; 8]> = {
|
||||
let iter = fences.into_iter();
|
||||
let mut fences_vk: SmallVec<[_; 8]> = SmallVec::new();
|
||||
let mut fences: SmallVec<[_; 8]> = SmallVec::new();
|
||||
let mut states: SmallVec<[_; 8]> = SmallVec::new();
|
||||
|
||||
for fence in iter {
|
||||
let state = fence.state.lock();
|
||||
|
||||
// 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 there are no fences, we don't need to wait.
|
||||
if fences_vk.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
@ -401,110 +346,61 @@ impl Fence {
|
||||
};
|
||||
|
||||
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)),
|
||||
ash::vk::Result::SUCCESS => Ok(()),
|
||||
err => 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(())
|
||||
}
|
||||
|
||||
/// 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]
|
||||
pub fn reset(&self) -> Result<(), Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_reset(&state)?;
|
||||
pub unsafe fn reset(&self) -> Result<(), Validated<VulkanError>> {
|
||||
self.validate_reset()?;
|
||||
|
||||
unsafe { Ok(self.reset_unchecked_locked(&mut state)?) }
|
||||
}
|
||||
|
||||
fn validate_reset(&self, state: &FenceState) -> 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()
|
||||
}));
|
||||
unsafe { Ok(self.reset_unchecked()?) }
|
||||
}
|
||||
|
||||
fn validate_reset(&self) -> Result<(), Box<ValidationError>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
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();
|
||||
(fns.v1_0.reset_fences)(self.device.handle(), 1, &self.handle)
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.reset();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Resets multiple fences at once.
|
||||
///
|
||||
/// The fences must not be in use by a queue operation.
|
||||
/// # Safety
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - Panics if not all fences belong to the same device.
|
||||
pub fn multi_reset<'a>(
|
||||
/// - The elements of `fences` must not be in use by the device.
|
||||
pub unsafe fn multi_reset<'a>(
|
||||
fences: impl IntoIterator<Item = &'a Fence>,
|
||||
) -> Result<(), Validated<VulkanError>> {
|
||||
let (fences, mut states): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = fences
|
||||
.into_iter()
|
||||
.map(|fence| {
|
||||
let state = fence.state.lock();
|
||||
(fence, state)
|
||||
})
|
||||
.unzip();
|
||||
Self::validate_multi_reset(&fences, &states)?;
|
||||
let fences: SmallVec<[_; 8]> = fences.into_iter().collect();
|
||||
Self::validate_multi_reset(&fences)?;
|
||||
|
||||
unsafe { Ok(Self::multi_reset_unchecked_locked(&fences, &mut states)?) }
|
||||
unsafe { Ok(Self::multi_reset_unchecked(fences)?) }
|
||||
}
|
||||
|
||||
fn validate_multi_reset(
|
||||
fences: &[&Fence],
|
||||
states: &[MutexGuard<'_, FenceState>],
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
fn validate_multi_reset(fences: &[&Fence]) -> Result<(), Box<ValidationError>> {
|
||||
if fences.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let device = &fences[0].device;
|
||||
|
||||
for (fence_index, (fence, state)) in fences.iter().zip(states).enumerate() {
|
||||
for fence in fences {
|
||||
// VUID-vkResetFences-pFences-parent
|
||||
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(())
|
||||
@ -514,21 +410,8 @@ impl Fence {
|
||||
pub unsafe fn multi_reset_unchecked<'a>(
|
||||
fences: impl IntoIterator<Item = &'a Fence>,
|
||||
) -> Result<(), VulkanError> {
|
||||
let (fences, mut states): (SmallVec<[_; 8]>, SmallVec<[_; 8]>) = fences
|
||||
.into_iter()
|
||||
.map(|fence| {
|
||||
let state = fence.state.lock();
|
||||
(fence, state)
|
||||
})
|
||||
.unzip();
|
||||
let fences: SmallVec<[_; 8]> = fences.into_iter().collect();
|
||||
|
||||
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() {
|
||||
return Ok(());
|
||||
}
|
||||
@ -541,10 +424,6 @@ impl Fence {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
for state in states {
|
||||
state.reset();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -552,21 +431,27 @@ impl Fence {
|
||||
///
|
||||
/// The [`khr_external_fence_fd`](crate::device::DeviceExtensions::khr_external_fence_fd)
|
||||
/// 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]
|
||||
pub fn export_fd(
|
||||
pub unsafe fn export_fd(
|
||||
&self,
|
||||
handle_type: ExternalFenceHandleType,
|
||||
) -> Result<File, Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_export_fd(handle_type, &state)?;
|
||||
self.validate_export_fd(handle_type)?;
|
||||
|
||||
unsafe { Ok(self.export_fd_unchecked_locked(handle_type, &mut state)?) }
|
||||
unsafe { Ok(self.export_fd_unchecked(handle_type)?) }
|
||||
}
|
||||
|
||||
fn validate_export_fd(
|
||||
&self,
|
||||
handle_type: ExternalFenceHandleType,
|
||||
state: &FenceState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().khr_external_fence_fd {
|
||||
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(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
pub unsafe fn export_fd_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let info_vk = ash::vk::FenceGetFdInfoKHR {
|
||||
fence: self.handle,
|
||||
@ -688,8 +513,6 @@ impl Fence {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.export(handle_type);
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::io::FromRawFd;
|
||||
@ -707,21 +530,29 @@ impl Fence {
|
||||
///
|
||||
/// The [`khr_external_fence_win32`](crate::device::DeviceExtensions::khr_external_fence_win32)
|
||||
/// 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]
|
||||
pub fn export_win32_handle(
|
||||
&self,
|
||||
handle_type: ExternalFenceHandleType,
|
||||
) -> Result<*mut std::ffi::c_void, Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_export_win32_handle(handle_type, &state)?;
|
||||
self.validate_export_win32_handle(handle_type)?;
|
||||
|
||||
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(
|
||||
&self,
|
||||
handle_type: ExternalFenceHandleType,
|
||||
state: &FenceState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().khr_external_fence_win32 {
|
||||
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(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
pub unsafe fn export_win32_handle_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let info_vk = ash::vk::FenceGetWin32HandleInfoKHR {
|
||||
fence: self.handle,
|
||||
@ -855,8 +614,6 @@ impl Fence {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.export(handle_type);
|
||||
|
||||
Ok(output.assume_init())
|
||||
}
|
||||
|
||||
@ -867,6 +624,7 @@ impl Fence {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The fence must not be in use by the device.
|
||||
/// - 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,
|
||||
/// with a driver and device UUID equal to those of the device that owns `self`.
|
||||
@ -875,16 +633,14 @@ impl Fence {
|
||||
&self,
|
||||
import_fence_fd_info: ImportFenceFdInfo,
|
||||
) -> Result<(), Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_import_fd(&import_fence_fd_info, &state)?;
|
||||
self.validate_import_fd(&import_fence_fd_info)?;
|
||||
|
||||
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(
|
||||
&self,
|
||||
import_fence_fd_info: &ImportFenceFdInfo,
|
||||
state: &FenceState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().khr_external_fence_fd {
|
||||
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
|
||||
.validate(&self.device)
|
||||
.map_err(|err| err.add_context("import_fence_fd_info"))?;
|
||||
@ -911,19 +659,9 @@ impl Fence {
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
pub unsafe fn import_fd_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let ImportFenceFdInfo {
|
||||
flags,
|
||||
@ -957,8 +695,6 @@ impl Fence {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.import(handle_type, flags.intersects(FenceImportFlags::TEMPORARY));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -969,6 +705,7 @@ impl Fence {
|
||||
///
|
||||
/// # 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
|
||||
/// from Vulkan or a compatible API, with a driver and device UUID equal to those of the
|
||||
/// device that owns `self`.
|
||||
@ -977,16 +714,14 @@ impl Fence {
|
||||
&self,
|
||||
import_fence_win32_handle_info: ImportFenceWin32HandleInfo,
|
||||
) -> Result<(), Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_import_win32_handle(&import_fence_win32_handle_info, &state)?;
|
||||
self.validate_import_win32_handle(&import_fence_win32_handle_info)?;
|
||||
|
||||
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(
|
||||
&self,
|
||||
import_fence_win32_handle_info: &ImportFenceWin32HandleInfo,
|
||||
state: &FenceState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().khr_external_fence_win32 {
|
||||
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
|
||||
.validate(&self.device)
|
||||
.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))]
|
||||
#[inline]
|
||||
pub unsafe fn import_win32_handle_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let ImportFenceWin32HandleInfo {
|
||||
flags,
|
||||
@ -1051,15 +768,9 @@ impl Fence {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.import(handle_type, flags.intersects(FenceImportFlags::TEMPORARY));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> MutexGuard<'_, FenceState> {
|
||||
self.state.lock()
|
||||
}
|
||||
|
||||
// Shared by Fence and FenceSignalFuture
|
||||
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.
|
||||
@ -1527,124 +1238,6 @@ pub struct ExternalFenceProperties {
|
||||
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)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
@ -1703,7 +1296,11 @@ mod tests {
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
fence.reset().unwrap();
|
||||
}
|
||||
|
||||
assert!(!fence.is_signaled().unwrap());
|
||||
}
|
||||
|
||||
@ -1760,7 +1357,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let _ = Fence::multi_reset([&fence1, &fence2]);
|
||||
let _ = unsafe { Fence::multi_reset([&fence1, &fence2]) };
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ use crate::{
|
||||
swapchain::Swapchain,
|
||||
sync::{
|
||||
fence::Fence,
|
||||
future::{AccessError, SubmitAnyBuilder},
|
||||
future::{queue_bind_sparse, queue_present, queue_submit, AccessError, SubmitAnyBuilder},
|
||||
PipelineStages,
|
||||
},
|
||||
DeviceSize, Validated, ValidationError, VulkanError,
|
||||
@ -243,35 +243,36 @@ where
|
||||
SubmitAnyBuilder::Empty => {
|
||||
debug_assert!(!partially_flushed);
|
||||
|
||||
queue
|
||||
.with(|mut q| {
|
||||
q.submit_unchecked([Default::default()], Some(new_fence.clone()))
|
||||
})
|
||||
.map_err(|err| OutcomeErr::Full(err.into()))
|
||||
queue_submit(
|
||||
&queue,
|
||||
Default::default(),
|
||||
Some(new_fence.clone()),
|
||||
&previous,
|
||||
)
|
||||
.map_err(OutcomeErr::Full)
|
||||
}
|
||||
SubmitAnyBuilder::SemaphoresWait(semaphores) => {
|
||||
debug_assert!(!partially_flushed);
|
||||
|
||||
queue
|
||||
.with(|mut q| {
|
||||
q.submit_unchecked(
|
||||
[SubmitInfo {
|
||||
queue_submit(
|
||||
&queue,
|
||||
SubmitInfo {
|
||||
wait_semaphores: semaphores
|
||||
.into_iter()
|
||||
.map(|semaphore| {
|
||||
SemaphoreSubmitInfo {
|
||||
// TODO: correct stages ; hard
|
||||
stages: PipelineStages::ALL_COMMANDS,
|
||||
..SemaphoreSubmitInfo::semaphore(semaphore)
|
||||
..SemaphoreSubmitInfo::new(semaphore)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
None,
|
||||
&previous,
|
||||
)
|
||||
})
|
||||
.map_err(|err| OutcomeErr::Full(err.into()))
|
||||
.map_err(OutcomeErr::Full)
|
||||
}
|
||||
SubmitAnyBuilder::CommandBuffer(submit_info, fence) => {
|
||||
debug_assert!(!partially_flushed);
|
||||
@ -282,15 +283,7 @@ where
|
||||
// assertion.
|
||||
assert!(fence.is_none());
|
||||
|
||||
queue
|
||||
.with(|mut q| {
|
||||
q.submit_with_future(
|
||||
submit_info,
|
||||
Some(new_fence.clone()),
|
||||
&previous,
|
||||
&queue,
|
||||
)
|
||||
})
|
||||
queue_submit(&queue, submit_info, Some(new_fence.clone()), &previous)
|
||||
.map_err(OutcomeErr::Full)
|
||||
}
|
||||
SubmitAnyBuilder::BindSparse(bind_infos, fence) => {
|
||||
@ -302,19 +295,20 @@ where
|
||||
.queue_flags
|
||||
.intersects(QueueFlags::SPARSE_BINDING));
|
||||
|
||||
queue
|
||||
.with(|mut q| q.bind_sparse_unchecked(bind_infos, Some(new_fence.clone())))
|
||||
.map_err(|err| OutcomeErr::Full(err.into()))
|
||||
queue_bind_sparse(&queue, bind_infos, Some(new_fence.clone()))
|
||||
.map_err(OutcomeErr::Full)
|
||||
}
|
||||
SubmitAnyBuilder::QueuePresent(present_info) => {
|
||||
if partially_flushed {
|
||||
queue
|
||||
.with(|mut q| {
|
||||
q.submit_unchecked([Default::default()], Some(new_fence.clone()))
|
||||
})
|
||||
.map_err(|err| OutcomeErr::Partial(err.into()))
|
||||
queue_submit(
|
||||
&queue,
|
||||
Default::default(),
|
||||
Some(new_fence.clone()),
|
||||
&previous,
|
||||
)
|
||||
.map_err(OutcomeErr::Partial)
|
||||
} 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| {
|
||||
!swapchain_info.swapchain.try_claim_present_id(present_id)
|
||||
}) {
|
||||
@ -346,20 +340,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
let intermediary_result = queue
|
||||
.with(|mut q| q.present_unchecked(present_info))?
|
||||
let intermediary_result = queue_present(&queue, present_info)?
|
||||
.map(|r| r.map(|_| ()))
|
||||
.fold(Ok(()), Result::and);
|
||||
|
||||
match intermediary_result {
|
||||
Ok(()) => queue
|
||||
.with(|mut q| {
|
||||
q.submit_unchecked(
|
||||
[Default::default()],
|
||||
Ok(()) => queue_submit(
|
||||
&queue,
|
||||
Default::default(),
|
||||
Some(new_fence.clone()),
|
||||
&previous,
|
||||
)
|
||||
})
|
||||
.map_err(|err| OutcomeErr::Partial(err.into())),
|
||||
.map_err(OutcomeErr::Partial),
|
||||
Err(err) => Err(OutcomeErr::Full(err.into())),
|
||||
}
|
||||
}
|
||||
|
@ -98,22 +98,26 @@ pub use self::{
|
||||
};
|
||||
use super::{fence::Fence, semaphore::Semaphore};
|
||||
use crate::{
|
||||
buffer::Buffer,
|
||||
buffer::{Buffer, BufferState},
|
||||
command_buffer::{
|
||||
CommandBufferExecError, CommandBufferExecFuture, PrimaryCommandBufferAbstract, SubmitInfo,
|
||||
CommandBufferExecError, CommandBufferExecFuture, CommandBufferResourcesUsage,
|
||||
CommandBufferState, CommandBufferSubmitInfo, CommandBufferUsage,
|
||||
PrimaryCommandBufferAbstract, SubmitInfo,
|
||||
},
|
||||
device::{DeviceOwned, Queue},
|
||||
image::{Image, ImageLayout},
|
||||
image::{Image, ImageLayout, ImageState},
|
||||
memory::BindSparseInfo,
|
||||
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::{
|
||||
error::Error,
|
||||
fmt::{Display, Error as FmtError, Formatter},
|
||||
ops::Range,
|
||||
sync::Arc,
|
||||
sync::{atomic::Ordering, Arc},
|
||||
};
|
||||
|
||||
mod fence_signal;
|
||||
@ -554,3 +558,321 @@ impl From<AccessError> for AccessCheckError {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,14 +7,18 @@
|
||||
// notice may not be copied, modified, or distributed except
|
||||
// according to those terms.
|
||||
|
||||
use super::{AccessCheckError, GpuFuture, SubmitAnyBuilder};
|
||||
use super::{queue_present, AccessCheckError, GpuFuture, SubmitAnyBuilder};
|
||||
use crate::{
|
||||
buffer::Buffer,
|
||||
command_buffer::{SemaphoreSubmitInfo, SubmitInfo},
|
||||
device::{Device, DeviceOwned, Queue},
|
||||
image::{Image, ImageLayout},
|
||||
swapchain::Swapchain,
|
||||
sync::{future::AccessError, semaphore::Semaphore, PipelineStages},
|
||||
sync::{
|
||||
future::{queue_submit, AccessError},
|
||||
semaphore::Semaphore,
|
||||
PipelineStages,
|
||||
},
|
||||
DeviceSize, Validated, ValidationError, VulkanError,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
@ -89,51 +93,49 @@ where
|
||||
|
||||
match self.previous.build_submission()? {
|
||||
SubmitAnyBuilder::Empty => {
|
||||
queue.with(|mut q| {
|
||||
q.submit_unchecked(
|
||||
[SubmitInfo {
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore(
|
||||
queue_submit(
|
||||
&queue,
|
||||
SubmitInfo {
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::new(
|
||||
self.semaphore.clone(),
|
||||
)],
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
&self.previous,
|
||||
)?;
|
||||
}
|
||||
SubmitAnyBuilder::SemaphoresWait(semaphores) => {
|
||||
queue.with(|mut q| {
|
||||
q.submit_unchecked(
|
||||
[SubmitInfo {
|
||||
queue_submit(
|
||||
&queue,
|
||||
SubmitInfo {
|
||||
wait_semaphores: semaphores
|
||||
.into_iter()
|
||||
.map(|semaphore| {
|
||||
SemaphoreSubmitInfo {
|
||||
// TODO: correct stages ; hard
|
||||
stages: PipelineStages::ALL_COMMANDS,
|
||||
..SemaphoreSubmitInfo::semaphore(semaphore)
|
||||
..SemaphoreSubmitInfo::new(semaphore)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore(
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::new(
|
||||
self.semaphore.clone(),
|
||||
)],
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
None,
|
||||
)
|
||||
})?;
|
||||
&self.previous,
|
||||
)?;
|
||||
}
|
||||
SubmitAnyBuilder::CommandBuffer(mut submit_info, fence) => {
|
||||
debug_assert!(submit_info.signal_semaphores.is_empty());
|
||||
|
||||
submit_info
|
||||
.signal_semaphores
|
||||
.push(SemaphoreSubmitInfo::semaphore(self.semaphore.clone()));
|
||||
.push(SemaphoreSubmitInfo::new(self.semaphore.clone()));
|
||||
|
||||
queue.with(|mut q| {
|
||||
q.submit_with_future(submit_info, fence, &self.previous, &queue)
|
||||
})?;
|
||||
queue_submit(&queue, submit_info, fence, &self.previous)?;
|
||||
}
|
||||
SubmitAnyBuilder::BindSparse(_, _) => {
|
||||
unimplemented!() // TODO: how to do that?
|
||||
@ -142,7 +144,7 @@ where
|
||||
builder.submit(&queue)?;*/
|
||||
}
|
||||
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| {
|
||||
!swapchain_info.swapchain.try_claim_present_id(present_id)
|
||||
}) {
|
||||
@ -174,22 +176,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
queue.with(|mut q| {
|
||||
q.present_unchecked(present_info)?
|
||||
queue_present(&queue, present_info)?
|
||||
.map(|r| r.map(|_| ()))
|
||||
.fold(Ok(()), Result::and)?;
|
||||
|
||||
// FIXME: problematic because if we return an error and flush() is called again, then we'll submit the present twice
|
||||
q.submit_unchecked(
|
||||
[SubmitInfo {
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::semaphore(
|
||||
queue_submit(
|
||||
&queue,
|
||||
SubmitInfo {
|
||||
signal_semaphores: vec![SemaphoreSubmitInfo::new(
|
||||
self.semaphore.clone(),
|
||||
)],
|
||||
..Default::default()
|
||||
}],
|
||||
},
|
||||
None,
|
||||
&self.previous,
|
||||
)?;
|
||||
Ok::<_, Validated<VulkanError>>(())
|
||||
})?;
|
||||
}
|
||||
};
|
||||
|
||||
@ -267,7 +269,10 @@ where
|
||||
self.flush().unwrap();
|
||||
// Block until the queue finished.
|
||||
self.queue().unwrap().with(|mut q| q.wait_idle()).unwrap();
|
||||
unsafe { self.previous.signal_finished() };
|
||||
|
||||
unsafe {
|
||||
self.signal_finished();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,22 +9,42 @@
|
||||
|
||||
//! A semaphore provides synchronization between multiple queues, with non-command buffer
|
||||
//! 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::{
|
||||
device::{physical::PhysicalDevice, Device, DeviceOwned, Queue},
|
||||
device::{physical::PhysicalDevice, Device, DeviceOwned},
|
||||
instance::InstanceOwnedDebugWrapper,
|
||||
macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum},
|
||||
Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
|
||||
VulkanObject,
|
||||
};
|
||||
use parking_lot::{Mutex, MutexGuard};
|
||||
use std::{
|
||||
fs::File,
|
||||
mem::MaybeUninit,
|
||||
num::NonZeroU64,
|
||||
ptr,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
use std::{fs::File, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
|
||||
|
||||
/// Used to provide synchronization between command buffers during their execution.
|
||||
///
|
||||
@ -39,7 +59,6 @@ pub struct Semaphore {
|
||||
export_handle_types: ExternalSemaphoreHandleTypes,
|
||||
|
||||
must_put_in_pool: bool,
|
||||
state: Mutex<SemaphoreState>,
|
||||
}
|
||||
|
||||
impl Semaphore {
|
||||
@ -128,7 +147,6 @@ impl Semaphore {
|
||||
export_handle_types: ExternalSemaphoreHandleTypes::empty(),
|
||||
|
||||
must_put_in_pool: true,
|
||||
state: Mutex::new(Default::default()),
|
||||
},
|
||||
None => {
|
||||
// Pool is empty, alloc new semaphore
|
||||
@ -167,7 +185,6 @@ impl Semaphore {
|
||||
export_handle_types,
|
||||
|
||||
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`.
|
||||
///
|
||||
/// # 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]
|
||||
pub fn export_fd(
|
||||
pub unsafe fn export_fd(
|
||||
&self,
|
||||
handle_type: ExternalSemaphoreHandleType,
|
||||
) -> Result<File, Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_export_fd(handle_type, &state)?;
|
||||
self.validate_export_fd(handle_type)?;
|
||||
|
||||
unsafe { Ok(self.export_fd_unchecked_locked(handle_type, &mut state)?) }
|
||||
unsafe { Ok(self.export_fd_unchecked(handle_type)?) }
|
||||
}
|
||||
|
||||
fn validate_export_fd(
|
||||
&self,
|
||||
handle_type: ExternalSemaphoreHandleType,
|
||||
state: &SemaphoreState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().khr_external_semaphore_fd {
|
||||
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(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
pub unsafe fn export_fd_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let info_vk = ash::vk::SemaphoreGetFdInfoKHR {
|
||||
semaphore: self.handle,
|
||||
@ -327,8 +278,6 @@ impl Semaphore {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.export(handle_type);
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::io::FromRawFd;
|
||||
@ -346,21 +295,31 @@ impl Semaphore {
|
||||
///
|
||||
/// The [`khr_external_semaphore_win32`](crate::device::DeviceExtensions::khr_external_semaphore_win32)
|
||||
/// 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]
|
||||
pub fn export_win32_handle(
|
||||
&self,
|
||||
handle_type: ExternalSemaphoreHandleType,
|
||||
) -> Result<*mut std::ffi::c_void, Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_export_win32_handle(handle_type, &state)?;
|
||||
self.validate_export_win32_handle(handle_type)?;
|
||||
|
||||
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(
|
||||
&self,
|
||||
handle_type: ExternalSemaphoreHandleType,
|
||||
state: &SemaphoreState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self
|
||||
.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(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
pub unsafe fn export_win32_handle_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let info_vk = ash::vk::SemaphoreGetWin32HandleInfoKHR {
|
||||
semaphore: self.handle,
|
||||
@ -513,27 +387,32 @@ impl Semaphore {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.export(handle_type);
|
||||
|
||||
Ok(output.assume_init())
|
||||
}
|
||||
|
||||
/// 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]
|
||||
pub fn export_zircon_handle(
|
||||
pub unsafe fn export_zircon_handle(
|
||||
&self,
|
||||
handle_type: ExternalSemaphoreHandleType,
|
||||
) -> Result<ash::vk::zx_handle_t, Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_export_zircon_handle(handle_type, &state)?;
|
||||
self.validate_export_zircon_handle(handle_type)?;
|
||||
|
||||
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(
|
||||
&self,
|
||||
handle_type: ExternalSemaphoreHandleType,
|
||||
state: &SemaphoreState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().fuchsia_external_semaphore {
|
||||
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(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
pub unsafe fn export_zircon_handle_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let info_vk = ash::vk::SemaphoreGetZirconHandleInfoFUCHSIA {
|
||||
semaphore: self.handle,
|
||||
@ -661,8 +470,6 @@ impl Semaphore {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.export(handle_type);
|
||||
|
||||
Ok(output.assume_init())
|
||||
}
|
||||
|
||||
@ -673,6 +480,7 @@ impl Semaphore {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The semaphore must not be in use by the device.
|
||||
/// - 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
|
||||
/// compatible API, with a driver and device UUID equal to those of the device that owns
|
||||
@ -682,16 +490,14 @@ impl Semaphore {
|
||||
&self,
|
||||
import_semaphore_fd_info: ImportSemaphoreFdInfo,
|
||||
) -> Result<(), Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_import_fd(&import_semaphore_fd_info, &state)?;
|
||||
self.validate_import_fd(&import_semaphore_fd_info)?;
|
||||
|
||||
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(
|
||||
&self,
|
||||
import_semaphore_fd_info: &ImportSemaphoreFdInfo,
|
||||
state: &SemaphoreState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().khr_external_semaphore_fd {
|
||||
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
|
||||
.validate(&self.device)
|
||||
.map_err(|err| err.add_context("import_semaphore_fd_info"))?;
|
||||
@ -718,19 +516,9 @@ impl Semaphore {
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
|
||||
#[inline]
|
||||
pub unsafe fn import_fd_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let ImportSemaphoreFdInfo {
|
||||
flags,
|
||||
@ -764,11 +552,6 @@ impl Semaphore {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.import(
|
||||
handle_type,
|
||||
flags.intersects(SemaphoreImportFlags::TEMPORARY),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -779,6 +562,7 @@ impl Semaphore {
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The semaphore must not be in use by the device.
|
||||
/// - 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
|
||||
/// those of the device that owns `self`.
|
||||
@ -787,17 +571,14 @@ impl Semaphore {
|
||||
&self,
|
||||
import_semaphore_win32_handle_info: ImportSemaphoreWin32HandleInfo,
|
||||
) -> Result<(), Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_import_win32_handle(&import_semaphore_win32_handle_info, &state)?;
|
||||
self.validate_import_win32_handle(&import_semaphore_win32_handle_info)?;
|
||||
|
||||
Ok(self
|
||||
.import_win32_handle_unchecked_locked(import_semaphore_win32_handle_info, &mut state)?)
|
||||
Ok(self.import_win32_handle_unchecked(import_semaphore_win32_handle_info)?)
|
||||
}
|
||||
|
||||
fn validate_import_win32_handle(
|
||||
&self,
|
||||
import_semaphore_win32_handle_info: &ImportSemaphoreWin32HandleInfo,
|
||||
state: &SemaphoreState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self
|
||||
.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
|
||||
.validate(&self.device)
|
||||
.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))]
|
||||
#[inline]
|
||||
pub unsafe fn import_win32_handle_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let ImportSemaphoreWin32HandleInfo {
|
||||
flags,
|
||||
@ -864,11 +627,6 @@ impl Semaphore {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.import(
|
||||
handle_type,
|
||||
flags.intersects(SemaphoreImportFlags::TEMPORARY),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -879,6 +637,7 @@ impl Semaphore {
|
||||
///
|
||||
/// # 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
|
||||
/// `ZX_RIGHTS_SIGNAL`.
|
||||
#[inline]
|
||||
@ -886,19 +645,14 @@ impl Semaphore {
|
||||
&self,
|
||||
import_semaphore_zircon_handle_info: ImportSemaphoreZirconHandleInfo,
|
||||
) -> Result<(), Validated<VulkanError>> {
|
||||
let mut state = self.state.lock();
|
||||
self.validate_import_zircon_handle(&import_semaphore_zircon_handle_info, &state)?;
|
||||
self.validate_import_zircon_handle(&import_semaphore_zircon_handle_info)?;
|
||||
|
||||
Ok(self.import_zircon_handle_unchecked_locked(
|
||||
import_semaphore_zircon_handle_info,
|
||||
&mut state,
|
||||
)?)
|
||||
Ok(self.import_zircon_handle_unchecked(import_semaphore_zircon_handle_info)?)
|
||||
}
|
||||
|
||||
fn validate_import_zircon_handle(
|
||||
&self,
|
||||
import_semaphore_zircon_handle_info: &ImportSemaphoreZirconHandleInfo,
|
||||
state: &SemaphoreState,
|
||||
) -> Result<(), Box<ValidationError>> {
|
||||
if !self.device.enabled_extensions().fuchsia_external_semaphore {
|
||||
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
|
||||
.validate(&self.device)
|
||||
.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))]
|
||||
#[inline]
|
||||
pub unsafe fn import_zircon_handle_unchecked(
|
||||
&self,
|
||||
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> {
|
||||
let ImportSemaphoreZirconHandleInfo {
|
||||
flags,
|
||||
@ -960,17 +696,8 @@ impl Semaphore {
|
||||
.result()
|
||||
.map_err(VulkanError::from)?;
|
||||
|
||||
state.import(
|
||||
handle_type,
|
||||
flags.intersects(SemaphoreImportFlags::TEMPORARY),
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn state(&self) -> MutexGuard<'_, SemaphoreState> {
|
||||
self.state.lock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Semaphore {
|
||||
@ -1498,136 +1225,6 @@ pub struct ExternalSemaphoreProperties {
|
||||
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)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
@ -1717,8 +1314,9 @@ mod tests {
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let _fd = sem
|
||||
.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
|
||||
.unwrap();
|
||||
let _fd = unsafe {
|
||||
sem.export_fd(ExternalSemaphoreHandleType::OpaqueFd)
|
||||
.unwrap()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user