diff options
Diffstat (limited to 'src/command_buffer/standard/builder/sync.rs')
-rw-r--r-- | src/command_buffer/standard/builder/sync.rs | 4459 |
1 files changed, 4459 insertions, 0 deletions
diff --git a/src/command_buffer/standard/builder/sync.rs b/src/command_buffer/standard/builder/sync.rs new file mode 100644 index 0000000..c608194 --- /dev/null +++ b/src/command_buffer/standard/builder/sync.rs @@ -0,0 +1,4459 @@ +// Copyright (c) 2022 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use super::{CommandBufferBuilder, RenderPassStateType}; +use crate::{ + command_buffer::allocator::CommandBufferAllocator, + device::{DeviceOwned, QueueFlags}, + image::{ImageAspects, ImageCreateFlags, ImageLayout, ImageUsage}, + sync::{ + event::Event, AccessFlags, BufferMemoryBarrier, DependencyFlags, DependencyInfo, + ImageMemoryBarrier, MemoryBarrier, PipelineStages, QueueFamilyOwnershipTransfer, Sharing, + }, + DeviceSize, RequirementNotMet, RequiresOneOf, Version, VulkanObject, +}; +use smallvec::SmallVec; +use std::{ + cmp::max, + error::Error, + fmt::{Display, Error as FmtError, Formatter}, + ptr, + sync::Arc, +}; + +impl<L, A> CommandBufferBuilder<L, A> +where + A: CommandBufferAllocator, +{ + /// Inserts a memory dependency into the queue. + /// + /// A pipeline barrier is the primary means of synchronizing work within a single queue. + /// Without a pipeline barrier, all commands in a queue could complete out of order, and there + /// would be no guarantee about the ordering of memory accesses. When a dependency is inserted, + /// a chosen subset of operations after the barrier (the destination synchronization scope) + /// depend on, and therefore must wait for the completion of, a chosen subset operations before + /// barrier (the source synchronization scope). + /// + /// When the queue has to wait, it may not be doing useful work, so barriers should be kept to + /// the minimum needed to ensure proper functioning. Overly broad barriers, specifying + /// a larger class of operations than needed (especially [`ALL_COMMANDS`]), can slow down the + /// queue and make it work less efficiently. + /// + /// In addition to ensuring correct operation, pipeline barriers are also used to transition + /// images (or parts of images) from one [image layout] to another. + /// + /// # Safety + /// + /// - All images that are accessed by the command must be in the expected image layout. + /// - For each element of `dependency_info.image_memory_barriers` that contains an image layout + /// transition, which is a write operation, the barrier must be defined appropriately to + /// ensure no memory access hazard occurs. + /// + /// [`ALL_COMMANDS`]: PipelineStages::ALL_COMMANDS + /// [image layout]: ImageLayout + #[inline] + pub unsafe fn pipeline_barrier( + &mut self, + dependency_info: DependencyInfo, + ) -> Result<&mut Self, SynchronizationError> { + self.validate_pipeline_barrier(&dependency_info)?; + + unsafe { Ok(self.pipeline_barrier_unchecked(dependency_info)) } + } + + fn validate_pipeline_barrier( + &self, + dependency_info: &DependencyInfo, + ) -> Result<(), SynchronizationError> { + let device = self.device(); + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdPipelineBarrier2-commandBuffer-cmdpool + if !queue_family_properties.queue_flags.intersects( + QueueFlags::TRANSFER + | QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(SynchronizationError::NotSupportedByQueueFamily); + } + + let &DependencyInfo { + dependency_flags, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne: _, + } = dependency_info; + + // VUID-VkDependencyInfo-dependencyFlags-parameter + dependency_flags.validate_device(device)?; + + let check_stages_access = |ty: char, + barrier_index: usize, + src_stages: PipelineStages, + src_access: AccessFlags, + dst_stages: PipelineStages, + dst_access: AccessFlags| + -> Result<(), SynchronizationError> { + for (stages, access) in [(src_stages, src_access), (dst_stages, dst_access)] { + // VUID-vkCmdPipelineBarrier2-synchronization2-03848 + if !device.enabled_features().synchronization2 { + if stages.is_2() { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_stages` or `dst_stages` contains flags from \ + `VkPipelineStageFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + if access.is_2() { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_access` or `dst_access` contains flags from \ + `VkAccessFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + } + + // VUID-VkMemoryBarrier2-srcStageMask-parameter + // VUID-VkMemoryBarrier2-dstStageMask-parameter + // VUID-VkBufferMemoryBarrier2-srcStageMask-parameter + // VUID-VkBufferMemoryBarrier2-dstStageMask-parameter + // VUID-VkImageMemoryBarrier2-srcStageMask-parameter + // VUID-VkImageMemoryBarrier2-dstStageMask-parameter + stages.validate_device(device)?; + + // VUID-VkMemoryBarrier2-srcAccessMask-parameter + // VUID-VkMemoryBarrier2-dstAccessMask-parameter + // VUID-VkBufferMemoryBarrier2-srcAccessMask-parameter + // VUID-VkBufferMemoryBarrier2-dstAccessMask-parameter + // VUID-VkImageMemoryBarrier2-srcAccessMask-parameter + // VUID-VkImageMemoryBarrier2-dstAccessMask-parameter + access.validate_device(device)?; + + // VUID-vkCmdPipelineBarrier2-srcStageMask-03849 + // VUID-vkCmdPipelineBarrier2-dstStageMask-03850 + if !PipelineStages::from(queue_family_properties.queue_flags).contains(stages) { + match ty { + 'm' => { + return Err(SynchronizationError::MemoryBarrierStageNotSupported { + barrier_index, + }) + } + 'b' => { + return Err( + SynchronizationError::BufferMemoryBarrierStageNotSupported { + barrier_index, + }, + ) + } + 'i' => { + return Err(SynchronizationError::ImageMemoryBarrierStageNotSupported { + barrier_index, + }) + } + _ => unreachable!(), + } + } + + // VUID-VkMemoryBarrier2-srcStageMask-03929 + // VUID-VkMemoryBarrier2-dstStageMask-03929 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03929 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03929 + // VUID-VkImageMemoryBarrier2-srcStageMask-03930 + // VUID-VkImageMemoryBarrier2-dstStageMask-03930 + if stages.intersects(PipelineStages::GEOMETRY_SHADER) + && !device.enabled_features().geometry_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::GEOMETRY_SHADER`", + requires_one_of: RequiresOneOf { + features: &["geometry_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03930 + // VUID-VkMemoryBarrier2-dstStageMask-03930 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03930 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03930 + // VUID-VkImageMemoryBarrier2-srcStageMask-03930 + // VUID-VkImageMemoryBarrier2-dstStageMask-03930 + if stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) && !device.enabled_features().tessellation_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`", + requires_one_of: RequiresOneOf { + features: &["tessellation_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03931 + // VUID-VkMemoryBarrier2-dstStageMask-03931 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03931 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03931 + // VUID-VImagekMemoryBarrier2-srcStageMask-03931 + // VUID-VkImageMemoryBarrier2-dstStageMask-03931 + if stages.intersects(PipelineStages::CONDITIONAL_RENDERING) + && !device.enabled_features().conditional_rendering + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::CONDITIONAL_RENDERING`", + requires_one_of: RequiresOneOf { + features: &["conditional_rendering"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03932 + // VUID-VkMemoryBarrier2-dstStageMask-03932 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03932 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03932 + // VUID-VkImageMemoryBarrier2-srcStageMask-03932 + // VUID-VkImageMemoryBarrier2-dstStageMask-03932 + if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) + && !device.enabled_features().fragment_density_map + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`", + requires_one_of: RequiresOneOf { + features: &["fragment_density_map"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03933 + // VUID-VkMemoryBarrier2-dstStageMask-03933 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03933 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03933 + // VUID-VkImageMemoryBarrier2-srcStageMask-03933 + // VUID-VkImageMemoryBarrier2-dstStageMask-03933 + if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) + && !device.enabled_features().transform_feedback + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TRANSFORM_FEEDBACK`", + requires_one_of: RequiresOneOf { + features: &["transform_feedback"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03934 + // VUID-VkMemoryBarrier2-dstStageMask-03934 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03934 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03934 + // VUID-VkImageMemoryBarrier2-srcStageMask-03934 + // VUID-VkImageMemoryBarrier2-dstStageMask-03934 + if stages.intersects(PipelineStages::MESH_SHADER) + && !device.enabled_features().mesh_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::MESH_SHADER`", + requires_one_of: RequiresOneOf { + features: &["mesh_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03935 + // VUID-VkMemoryBarrier2-dstStageMask-03935 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03935 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03935 + // VUID-VkImageMemoryBarrier2-srcStageMask-03935 + // VUID-VkImageMemoryBarrier2-dstStageMask-03935 + if stages.intersects(PipelineStages::TASK_SHADER) + && !device.enabled_features().task_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TASK_SHADER`", + requires_one_of: RequiresOneOf { + features: &["task_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-shadingRateImage-07316 + // VUID-VkMemoryBarrier2-shadingRateImage-07316 + // VUID-VkBufferMemoryBarrier2-shadingRateImage-07316 + // VUID-VkBufferMemoryBarrier2-shadingRateImage-07316 + // VUID-VkImageMemoryBarrier2-shadingRateImage-07316 + // VUID-VkImageMemoryBarrier2-shadingRateImage-07316 + if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) + && !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`", + requires_one_of: RequiresOneOf { + features: &["attachment_fragment_shading_rate", "shading_rate_image"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-04957 + // VUID-VkMemoryBarrier2-dstStageMask-04957 + // VUID-VkBufferMemoryBarrier2-srcStageMask-04957 + // VUID-VkBufferMemoryBarrier2-dstStageMask-04957 + // VUID-VkImageMemoryBarrier2-srcStageMask-04957 + // VUID-VkImageMemoryBarrier2-dstStageMask-04957 + if stages.intersects(PipelineStages::SUBPASS_SHADING) + && !device.enabled_features().subpass_shading + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::SUBPASS_SHADING`", + requires_one_of: RequiresOneOf { + features: &["subpass_shading"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-04995 + // VUID-VkMemoryBarrier2-dstStageMask-04995 + // VUID-VkBufferMemoryBarrier2-srcStageMask-04995 + // VUID-VkBufferMemoryBarrier2-dstStageMask-04995 + // VUID-VkImageMemoryBarrier2-srcStageMask-04995 + // VUID-VkImageMemoryBarrier2-dstStageMask-04995 + if stages.intersects(PipelineStages::INVOCATION_MASK) + && !device.enabled_features().invocation_mask + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::INVOCATION_MASK`", + requires_one_of: RequiresOneOf { + features: &["invocation_mask"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdPipelineBarrier-srcStageMask-03937 + // VUID-vkCmdPipelineBarrier-dstStageMask-03937 + if stages.is_empty() && !device.enabled_features().synchronization2 { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + is empty", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + // A bit of a ridiculous number of VUIDs... + + // VUID-VkMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkMemoryBarrier2-dstAccessMask-07458 + + // VUID-VkBufferMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkBufferMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkBufferMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkBufferMemoryBarrier2-dstAccessMask-07458 + + // VUID-VkImageMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkImageMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkImageMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkImageMemoryBarrier2-dstAccessMask-07458 + + if !AccessFlags::from(stages).contains(access) { + match ty { + 'm' => { + return Err( + SynchronizationError::MemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ) + } + 'b' => return Err( + SynchronizationError::BufferMemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ), + 'i' => return Err( + SynchronizationError::ImageMemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ), + _ => unreachable!(), + } + } + } + + // VUID-VkMemoryBarrier2-srcAccessMask-06256 + // VUID-VkBufferMemoryBarrier2-srcAccessMask-06256 + // VUID-VkImageMemoryBarrier2-srcAccessMask-06256 + if !device.enabled_features().ray_query + && src_access.intersects(AccessFlags::ACCELERATION_STRUCTURE_READ) + && src_stages.intersects( + PipelineStages::VERTEX_SHADER + | PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER + | PipelineStages::GEOMETRY_SHADER + | PipelineStages::FRAGMENT_SHADER + | PipelineStages::COMPUTE_SHADER + | PipelineStages::PRE_RASTERIZATION_SHADERS + | PipelineStages::TASK_SHADER + | PipelineStages::MESH_SHADER, + ) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_access` contains `ACCELERATION_STRUCTURE_READ`, and \ + `src_stages` contains a shader stage other than `RAY_TRACING_SHADER`", + requires_one_of: RequiresOneOf { + features: &["ray_query"], + ..Default::default() + }, + }); + } + + Ok(()) + }; + + let check_queue_family_ownership_transfer = |ty: char, + barrier_index: usize, + src_stages: PipelineStages, + dst_stages: PipelineStages, + queue_family_ownership_transfer: Option< + QueueFamilyOwnershipTransfer, + >, + sharing: &Sharing<_>| + -> Result<(), SynchronizationError> { + if let Some(transfer) = queue_family_ownership_transfer { + // VUID? + transfer.validate_device(device)?; + + // VUID-VkBufferMemoryBarrier2-srcQueueFamilyIndex-04087 + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04070 + // Ensured by the definition of `QueueFamilyOwnershipTransfer`. + + // VUID-VkBufferMemoryBarrier2-buffer-04088 + // VUID-VkImageMemoryBarrier2-image-04071 + // Ensured by the definition of `QueueFamilyOwnershipTransfer`. + + let queue_family_count = + device.physical_device().queue_family_properties().len() as u32; + + let provided_queue_family_index = match (sharing, transfer) { + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveBetweenLocal { + src_index, + dst_index, + }, + ) => Some(max(src_index, dst_index)), + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveToExternal { src_index } + | QueueFamilyOwnershipTransfer::ExclusiveToForeign { src_index }, + ) => Some(src_index), + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveFromExternal { dst_index } + | QueueFamilyOwnershipTransfer::ExclusiveFromForeign { dst_index }, + ) => Some(dst_index), + ( + Sharing::Concurrent(_), + QueueFamilyOwnershipTransfer::ConcurrentToExternal + | QueueFamilyOwnershipTransfer::ConcurrentFromExternal + | QueueFamilyOwnershipTransfer::ConcurrentToForeign + | QueueFamilyOwnershipTransfer::ConcurrentFromForeign, + ) => None, + _ => match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferSharingMismatch { + barrier_index, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferSharingMismatch { + barrier_index, + }), + _ => unreachable!(), + }, + }.filter(|&index| index >= queue_family_count); + + // VUID-VkBufferMemoryBarrier2-buffer-04089 + // VUID-VkImageMemoryBarrier2-image-04072 + + if let Some(provided_queue_family_index) = provided_queue_family_index { + match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + }), + _ => unreachable!(), + } + } + + // VUID-VkBufferMemoryBarrier2-srcStageMask-03851 + // VUID-VkImageMemoryBarrier2-srcStageMask-03854 + if src_stages.intersects(PipelineStages::HOST) + || dst_stages.intersects(PipelineStages::HOST) + { + match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferHostNotAllowed { + barrier_index, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferHostForbidden { + barrier_index, + }), + _ => unreachable!(), + } + } + } + + Ok(()) + }; + + for (barrier_index, barrier) in memory_barriers.iter().enumerate() { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + /* + Check stages and access + */ + + check_stages_access( + 'm', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + } + + for (barrier_index, barrier) in buffer_memory_barriers.iter().enumerate() { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + // VUID-VkBufferMemoryBarrier2-buffer-01931 + // Ensured by Buffer type construction. + + /* + Check stages and access + */ + + check_stages_access( + 'b', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + + /* + Check queue family transfer + */ + + check_queue_family_ownership_transfer( + 'b', + barrier_index, + src_stages, + dst_stages, + queue_family_ownership_transfer, + buffer.sharing(), + )?; + + /* + Check range + */ + + // VUID-VkBufferMemoryBarrier2-size-01188 + assert!(!range.is_empty()); + + // VUID-VkBufferMemoryBarrier2-offset-01187 + // VUID-VkBufferMemoryBarrier2-size-01189 + if range.end > buffer.size() { + return Err(SynchronizationError::BufferMemoryBarrierOutOfRange { + barrier_index, + range_end: range.end, + buffer_size: buffer.size(), + }); + } + } + + for (barrier_index, barrier) in image_memory_barriers.iter().enumerate() { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + // VUID-VkImageMemoryBarrier2-image-01932 + // Ensured by Image type construction. + + /* + Check stages and access + */ + + check_stages_access( + 'i', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + + /* + Check layouts + */ + + // VUID-VkImageMemoryBarrier2-oldLayout-parameter + old_layout.validate_device(device)?; + + // VUID-VkImageMemoryBarrier2-newLayout-parameter + new_layout.validate_device(device)?; + + // VUID-VkImageMemoryBarrier2-srcStageMask-03855 + if src_stages.intersects(PipelineStages::HOST) + && !matches!( + old_layout, + ImageLayout::Preinitialized | ImageLayout::Undefined | ImageLayout::General + ) + { + return Err( + SynchronizationError::ImageMemoryBarrierOldLayoutFromHostInvalid { + barrier_index, + old_layout, + }, + ); + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01197 + // Not checked yet, therefore unsafe. + + // VUID-VkImageMemoryBarrier2-newLayout-01198 + if matches!( + new_layout, + ImageLayout::Undefined | ImageLayout::Preinitialized + ) { + return Err(SynchronizationError::ImageMemoryBarrierNewLayoutInvalid { + barrier_index, + }); + } + + // VUID-VkImageMemoryBarrier2-attachmentFeedbackLoopLayout-07313 + /*if !device.enabled_features().attachment_feedback_loop_layout + && matches!(new_layout, ImageLayout::AttachmentFeedbackLoopOptimal) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element where \ + `new_layout` is `AttachmentFeedbackLoopOptimal`", + requires_one_of: RequiresOneOf { + features: &["attachment_feedback_loop_layout"], + ..Default::default() + }, + }); + }*/ + + for layout in [old_layout, new_layout] { + // VUID-VkImageMemoryBarrier2-synchronization2-06911 + /*if !device.enabled_features().synchronization2 + && matches!( + layout, + ImageLayout::AttachmentOptimal | ImageLayout::ReadOnlyOptimal + ) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element \ + where `old_layout` or `new_layout` is `AttachmentOptimal` or \ + `ReadOnlyOptimal`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + }*/ + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-07006 + /*if layout == ImageLayout::AttachmentFeedbackLoopOptimal { + if !image.usage().intersects( + ImageUsage::COLOR_ATTACHMENT | ImageUsage::DEPTH_STENCIL_ATTACHMENT, + ) { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::COLOR_ATTACHMENT + | ImageUsage::DEPTH_STENCIL_ATTACHMENT, + }, + ); + } + + if !image + .usage() + .intersects(ImageUsage::INPUT_ATTACHMENT | ImageUsage::SAMPLED) + { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::INPUT_ATTACHMENT + | ImageUsage::SAMPLED, + }, + ); + } + + if !image + .usage() + .intersects(ImageUsage::ATTACHMENT_FEEDBACK_LOOP) + { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::ATTACHMENT_FEEDBACK_LOOP, + }, + ); + } + }*/ + + let requires_one_of_usage = match layout { + // VUID-VkImageMemoryBarrier2-oldLayout-01208 + ImageLayout::ColorAttachmentOptimal => ImageUsage::COLOR_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-oldLayout-01209 + ImageLayout::DepthStencilAttachmentOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01210 + ImageLayout::DepthStencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01211 + ImageLayout::ShaderReadOnlyOptimal => { + ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01212 + ImageLayout::TransferSrcOptimal => ImageUsage::TRANSFER_SRC, + + // VUID-VkImageMemoryBarrier2-oldLayout-01213 + ImageLayout::TransferDstOptimal => ImageUsage::TRANSFER_DST, + + // VUID-VkImageMemoryBarrier2-oldLayout-01658 + ImageLayout::DepthReadOnlyStencilAttachmentOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01659 + ImageLayout::DepthAttachmentStencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + /* + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04065 + ImageLayout::DepthReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04066 + ImageLayout::DepthAttachmentOptimal => ImageUsage::DEPTH_STENCIL_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04067 + ImageLayout::StencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04068 + ImageLayout::StencilAttachmentOptimal => ImageUsage::DEPTH_STENCIL_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-03938 + ImageLayout::AttachmentOptimal => { + ImageUsage::COLOR_ATTACHMENT | ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-03939 + ImageLayout::ReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-02088 + ImageLayout::FragmentShadingRateAttachmentOptimal => { + ImageUsage::FRAGMENT_SHADING_RATE_ATTACHMENT + } + */ + _ => continue, + }; + + if !image.usage().intersects(requires_one_of_usage) { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage, + }, + ); + } + } + + /* + Check queue family tansfer + */ + + check_queue_family_ownership_transfer( + 'i', + barrier_index, + src_stages, + dst_stages, + queue_family_ownership_transfer, + image.sharing(), + )?; + + /* + Check subresource range + */ + + // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask + assert!(!subresource_range.aspects.is_empty()); + + // VUID-VkImageSubresourceRange-aspectMask-parameter + subresource_range.aspects.validate_device(device)?; + + let image_aspects = image.format().unwrap().aspects(); + + // VUID-VkImageMemoryBarrier2-image-01673 + // VUID-VkImageMemoryBarrier2-image-03319 + if image_aspects.contains(subresource_range.aspects) { + return Err(SynchronizationError::ImageMemoryBarrierAspectsNotAllowed { + barrier_index, + aspects: subresource_range.aspects - image_aspects, + }); + } + + if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { + // VUID-VkImageMemoryBarrier2-image-03320 + if !device.enabled_features().separate_depth_stencil_layouts + && image_aspects.contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + && !subresource_range + .aspects + .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element \ + where `image` has both a depth and a stencil aspect, and \ + `subresource_range.aspects` does not contain both aspects", + requires_one_of: RequiresOneOf { + features: &["separate_depth_stencil_layouts"], + ..Default::default() + }, + }); + } + } else { + // VUID-VkImageMemoryBarrier2-image-01671 + if !image.flags().intersects(ImageCreateFlags::DISJOINT) + && subresource_range.aspects != ImageAspects::COLOR + { + return Err(SynchronizationError::ImageMemoryBarrierAspectsNotAllowed { + barrier_index, + aspects: subresource_range.aspects - ImageAspects::COLOR, + }); + } + } + + // VUID-VkImageSubresourceRange-levelCount-01720 + assert!(!subresource_range.mip_levels.is_empty()); + + // VUID-VkImageMemoryBarrier2-subresourceRange-01486 + // VUID-VkImageMemoryBarrier2-subresourceRange-01724 + if subresource_range.mip_levels.end > image.mip_levels() { + return Err( + SynchronizationError::ImageMemoryBarrierMipLevelsOutOfRange { + barrier_index, + mip_levels_range_end: subresource_range.mip_levels.end, + image_mip_levels: image.mip_levels(), + }, + ); + } + + // VUID-VkImageSubresourceRange-layerCount-01721 + assert!(!subresource_range.array_layers.is_empty()); + + // VUID-VkImageMemoryBarrier2-subresourceRange-01488 + // VUID-VkImageMemoryBarrier2-subresourceRange-01725 + if subresource_range.array_layers.end > image.dimensions().array_layers() { + return Err( + SynchronizationError::ImageMemoryBarrierArrayLayersOutOfRange { + barrier_index, + array_layers_range_end: subresource_range.array_layers.end, + image_array_layers: image.dimensions().array_layers(), + }, + ); + } + } + + /* + Checks for current render pass + */ + + if let Some(render_pass_state) = self.builder_state.render_pass.as_ref() { + // VUID-vkCmdPipelineBarrier2-None-06191 + let begin_render_pass_state = match &render_pass_state.render_pass { + RenderPassStateType::BeginRenderPass(x) => x, + RenderPassStateType::BeginRendering(_) => { + return Err(SynchronizationError::ForbiddenWithBeginRendering) + } + }; + let subpass_index = begin_render_pass_state.subpass.index(); + let subpass_desc = begin_render_pass_state.subpass.subpass_desc(); + let dependencies = begin_render_pass_state.subpass.render_pass().dependencies(); + + // VUID-vkCmdPipelineBarrier2-pDependencies-02285 + // TODO: see https://github.com/KhronosGroup/Vulkan-Docs/issues/1982 + if !dependencies.iter().any(|dependency| { + dependency.src_subpass == Some(subpass_index) + && dependency.dst_subpass == Some(subpass_index) + }) { + return Err(SynchronizationError::MemoryBarrierNoMatchingSubpassSelfDependency); + } + + // VUID-vkCmdPipelineBarrier2-bufferMemoryBarrierCount-01178 + if !buffer_memory_barriers.is_empty() { + return Err(SynchronizationError::BufferMemoryBarrierForbiddenInsideRenderPass); + } + + for (barrier_index, barrier) in image_memory_barriers.iter().enumerate() { + // VUID-vkCmdPipelineBarrier2-image-04073 + // TODO: How are you supposed to verify this in secondary command buffers, + // when there is no inherited framebuffer? + // The image is not known until you execute it in a primary command buffer. + if let Some(framebuffer) = &begin_render_pass_state.framebuffer { + let attachment_index = (framebuffer.attachments().iter()) + .position(|attachment| attachment.image().inner().image == &barrier.image) + .ok_or(SynchronizationError::ImageMemoryBarrierNotInputAttachment { + barrier_index, + })? as u32; + + if !(subpass_desc.input_attachments.iter().flatten()) + .any(|atch_ref| atch_ref.attachment == attachment_index) + { + return Err(SynchronizationError::ImageMemoryBarrierNotInputAttachment { + barrier_index, + }); + } + + if !(subpass_desc.color_attachments.iter().flatten()) + .chain(subpass_desc.depth_stencil_attachment.as_ref()) + .any(|atch_ref| atch_ref.attachment == attachment_index) + { + return Err(SynchronizationError::ImageMemoryBarrierNotColorDepthStencilAttachment { + barrier_index, + }); + } + } + + // VUID-vkCmdPipelineBarrier2-oldLayout-01181 + if barrier.old_layout != barrier.new_layout { + return Err(SynchronizationError::ImageMemoryBarrierLayoutTransitionForbiddenInsideRenderPass { + barrier_index, + }); + } + + // VUID-vkCmdPipelineBarrier2-srcQueueFamilyIndex-01182 + if barrier.queue_family_ownership_transfer.is_some() { + return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferForbiddenInsideRenderPass { + barrier_index, + }); + } + } + } else { + // VUID-vkCmdPipelineBarrier2-dependencyFlags-01186 + if dependency_flags.intersects(DependencyFlags::VIEW_LOCAL) { + return Err(SynchronizationError::DependencyFlagsViewLocalNotAllowed); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn pipeline_barrier_unchecked( + &mut self, + dependency_info: DependencyInfo, + ) -> &mut Self { + if dependency_info.is_empty() { + return self; + } + + let &DependencyInfo { + dependency_flags, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne: _, + } = &dependency_info; + + if self.device().enabled_features().synchronization2 { + let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers + .iter() + .map(|barrier| { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + ash::vk::MemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + ..Default::default() + } + }) + .collect(); + + let buffer_memory_barriers_vk: SmallVec<[_; 8]> = buffer_memory_barriers + .iter() + .map(|barrier| { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::BufferMemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + src_queue_family_index, + dst_queue_family_index, + buffer: buffer.handle(), + offset: range.start, + size: range.end - range.start, + ..Default::default() + } + }) + .collect(); + + let image_memory_barriers_vk: SmallVec<[_; 8]> = image_memory_barriers + .iter() + .map(|barrier| { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::ImageMemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + old_layout: old_layout.into(), + new_layout: new_layout.into(), + src_queue_family_index, + dst_queue_family_index, + image: image.handle(), + subresource_range: subresource_range.clone().into(), + ..Default::default() + } + }) + .collect(); + + let dependency_info_vk = ash::vk::DependencyInfo { + dependency_flags: dependency_flags.into(), + memory_barrier_count: memory_barriers_vk.len() as u32, + p_memory_barriers: memory_barriers_vk.as_ptr(), + buffer_memory_barrier_count: buffer_memory_barriers_vk.len() as u32, + p_buffer_memory_barriers: buffer_memory_barriers_vk.as_ptr(), + image_memory_barrier_count: image_memory_barriers_vk.len() as u32, + p_image_memory_barriers: image_memory_barriers_vk.as_ptr(), + ..Default::default() + }; + + let fns = self.device().fns(); + + if self.device().api_version() >= Version::V1_3 { + (fns.v1_3.cmd_pipeline_barrier2)(self.handle(), &dependency_info_vk); + } else { + debug_assert!(self.device().enabled_extensions().khr_synchronization2); + (fns.khr_synchronization2.cmd_pipeline_barrier2_khr)( + self.handle(), + &dependency_info_vk, + ); + } + } else { + let mut src_stage_mask = ash::vk::PipelineStageFlags::empty(); + let mut dst_stage_mask = ash::vk::PipelineStageFlags::empty(); + + let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers + .iter() + .map(|barrier| { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + src_stage_mask |= src_stages.into(); + dst_stage_mask |= dst_stages.into(); + + ash::vk::MemoryBarrier { + src_access_mask: src_access.into(), + dst_access_mask: dst_access.into(), + ..Default::default() + } + }) + .collect(); + + let buffer_memory_barriers_vk: SmallVec<[_; 8]> = buffer_memory_barriers + .iter() + .map(|barrier| { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + src_stage_mask |= src_stages.into(); + dst_stage_mask |= dst_stages.into(); + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::BufferMemoryBarrier { + src_access_mask: src_access.into(), + dst_access_mask: dst_access.into(), + src_queue_family_index, + dst_queue_family_index, + buffer: buffer.handle(), + offset: range.start, + size: range.end - range.start, + ..Default::default() + } + }) + .collect(); + + let image_memory_barriers_vk: SmallVec<[_; 8]> = image_memory_barriers + .iter() + .map(|barrier| { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + src_stage_mask |= src_stages.into(); + dst_stage_mask |= dst_stages.into(); + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::ImageMemoryBarrier { + src_access_mask: src_access.into(), + dst_access_mask: dst_access.into(), + old_layout: old_layout.into(), + new_layout: new_layout.into(), + src_queue_family_index, + dst_queue_family_index, + image: image.handle(), + subresource_range: subresource_range.clone().into(), + ..Default::default() + } + }) + .collect(); + + if src_stage_mask.is_empty() { + // "VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT is [...] equivalent to + // VK_PIPELINE_STAGE_2_NONE in the first scope." + src_stage_mask |= ash::vk::PipelineStageFlags::TOP_OF_PIPE; + } + + if dst_stage_mask.is_empty() { + // "VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT is [...] equivalent to + // VK_PIPELINE_STAGE_2_NONE in the second scope." + dst_stage_mask |= ash::vk::PipelineStageFlags::BOTTOM_OF_PIPE; + } + + let fns = self.device().fns(); + (fns.v1_0.cmd_pipeline_barrier)( + self.handle(), + src_stage_mask, + dst_stage_mask, + dependency_flags.into(), + memory_barriers_vk.len() as u32, + memory_barriers_vk.as_ptr(), + buffer_memory_barriers_vk.len() as u32, + buffer_memory_barriers_vk.as_ptr(), + image_memory_barriers_vk.len() as u32, + image_memory_barriers_vk.as_ptr(), + ); + } + + let command_index = self.next_command_index; + let command_name = "pipeline_barrier"; + self.resources_usage_state.record_pipeline_barrier( + command_index, + command_name, + &dependency_info, + self.queue_family_properties().queue_flags, + ); + + self.resources + .reserve(buffer_memory_barriers.len() + image_memory_barriers.len()); + + for barrier in dependency_info.buffer_memory_barriers { + self.resources.push(Box::new(barrier.buffer)); + } + + for barrier in dependency_info.image_memory_barriers { + self.resources.push(Box::new(barrier.image)); + } + + self.next_command_index += 1; + self + } + + /// Signals an [`Event`] from the device. + /// + /// # Safety + /// + /// - All images that are accessed by the command must be in the expected image layout. + /// - For each element of `dependency_info.image_memory_barriers` that contains an image layout + /// transition, which is a write operation, the barrier must be defined appropriately to + /// ensure no memory access hazard occurs. + #[inline] + pub unsafe fn set_event( + &mut self, + event: Arc<Event>, + dependency_info: DependencyInfo, + ) -> Result<&mut Self, SynchronizationError> { + self.validate_set_event(&event, &dependency_info)?; + + unsafe { Ok(self.set_event_unchecked(event, dependency_info)) } + } + + fn validate_set_event( + &self, + event: &Event, + dependency_info: &DependencyInfo, + ) -> Result<(), SynchronizationError> { + // VUID-vkCmdSetEvent2-renderpass + if self.builder_state.render_pass.is_some() { + return Err(SynchronizationError::ForbiddenInsideRenderPass); + } + + // VUID-vkCmdSetEvent2-commandBuffer-03826 + // TODO: + + let device = self.device(); + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdSetEvent2-commandBuffer-cmdpool + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(SynchronizationError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdSetEvent2-commonparent + assert_eq!(device, event.device()); + + let &DependencyInfo { + dependency_flags, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne: _, + } = dependency_info; + + // VUID-VkDependencyInfo-dependencyFlags-parameter + dependency_flags.validate_device(device)?; + + // VUID-vkCmdSetEvent2-dependencyFlags-03825 + assert!(dependency_flags.is_empty()); + + let check_stages_access = |ty: char, + barrier_index: usize, + src_stages: PipelineStages, + src_access: AccessFlags, + dst_stages: PipelineStages, + dst_access: AccessFlags| + -> Result<(), SynchronizationError> { + for (stages, access) in [(src_stages, src_access), (dst_stages, dst_access)] { + // VUID-vkCmdSetEvent2-synchronization2-03824 + if !device.enabled_features().synchronization2 { + if stages.is_2() { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_stages` or `dst_stages` contains flags from \ + `VkPipelineStageFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + if access.is_2() { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_access` or `dst_access` contains flags from \ + `VkAccessFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + } + + // VUID-VkMemoryBarrier2-srcStageMask-parameter + // VUID-VkMemoryBarrier2-dstStageMask-parameter + // VUID-VkBufferMemoryBarrier2-srcStageMask-parameter + // VUID-VkBufferMemoryBarrier2-dstStageMask-parameter + // VUID-VkImageMemoryBarrier2-srcStageMask-parameter + // VUID-VkImageMemoryBarrier2-dstStageMask-parameter + stages.validate_device(device)?; + + // VUID-VkMemoryBarrier2-srcAccessMask-parameter + // VUID-VkMemoryBarrier2-dstAccessMask-parameter + // VUID-VkBufferMemoryBarrier2-srcAccessMask-parameter + // VUID-VkBufferMemoryBarrier2-dstAccessMask-parameter + // VUID-VkImageMemoryBarrier2-srcAccessMask-parameter + // VUID-VkImageMemoryBarrier2-dstAccessMask-parameter + access.validate_device(device)?; + + // VUID-vkCmdSetEvent2-srcStageMask-03827 + // VUID-vkCmdSetEvent2-dstStageMask-03828 + if !PipelineStages::from(queue_family_properties.queue_flags).contains(stages) { + match ty { + 'm' => { + return Err(SynchronizationError::MemoryBarrierStageNotSupported { + barrier_index, + }) + } + 'b' => { + return Err( + SynchronizationError::BufferMemoryBarrierStageNotSupported { + barrier_index, + }, + ) + } + 'i' => { + return Err(SynchronizationError::ImageMemoryBarrierStageNotSupported { + barrier_index, + }) + } + _ => unreachable!(), + } + } + + // VUID-VkMemoryBarrier2-srcStageMask-03929 + // VUID-VkMemoryBarrier2-dstStageMask-03929 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03929 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03929 + // VUID-VkImageMemoryBarrier2-srcStageMask-03930 + // VUID-VkImageMemoryBarrier2-dstStageMask-03930 + if stages.intersects(PipelineStages::GEOMETRY_SHADER) + && !device.enabled_features().geometry_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::GEOMETRY_SHADER`", + requires_one_of: RequiresOneOf { + features: &["geometry_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03930 + // VUID-VkMemoryBarrier2-dstStageMask-03930 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03930 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03930 + // VUID-VkImageMemoryBarrier2-srcStageMask-03930 + // VUID-VkImageMemoryBarrier2-dstStageMask-03930 + if stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) && !device.enabled_features().tessellation_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`", + requires_one_of: RequiresOneOf { + features: &["tessellation_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03931 + // VUID-VkMemoryBarrier2-dstStageMask-03931 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03931 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03931 + // VUID-VImagekMemoryBarrier2-srcStageMask-03931 + // VUID-VkImageMemoryBarrier2-dstStageMask-03931 + if stages.intersects(PipelineStages::CONDITIONAL_RENDERING) + && !device.enabled_features().conditional_rendering + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::CONDITIONAL_RENDERING`", + requires_one_of: RequiresOneOf { + features: &["conditional_rendering"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03932 + // VUID-VkMemoryBarrier2-dstStageMask-03932 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03932 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03932 + // VUID-VkImageMemoryBarrier2-srcStageMask-03932 + // VUID-VkImageMemoryBarrier2-dstStageMask-03932 + if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) + && !device.enabled_features().fragment_density_map + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`", + requires_one_of: RequiresOneOf { + features: &["fragment_density_map"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03933 + // VUID-VkMemoryBarrier2-dstStageMask-03933 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03933 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03933 + // VUID-VkImageMemoryBarrier2-srcStageMask-03933 + // VUID-VkImageMemoryBarrier2-dstStageMask-03933 + if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) + && !device.enabled_features().transform_feedback + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TRANSFORM_FEEDBACK`", + requires_one_of: RequiresOneOf { + features: &["transform_feedback"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03934 + // VUID-VkMemoryBarrier2-dstStageMask-03934 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03934 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03934 + // VUID-VkImageMemoryBarrier2-srcStageMask-03934 + // VUID-VkImageMemoryBarrier2-dstStageMask-03934 + if stages.intersects(PipelineStages::MESH_SHADER) + && !device.enabled_features().mesh_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::MESH_SHADER`", + requires_one_of: RequiresOneOf { + features: &["mesh_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03935 + // VUID-VkMemoryBarrier2-dstStageMask-03935 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03935 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03935 + // VUID-VkImageMemoryBarrier2-srcStageMask-03935 + // VUID-VkImageMemoryBarrier2-dstStageMask-03935 + if stages.intersects(PipelineStages::TASK_SHADER) + && !device.enabled_features().task_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TASK_SHADER`", + requires_one_of: RequiresOneOf { + features: &["task_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-shadingRateImage-07316 + // VUID-VkMemoryBarrier2-shadingRateImage-07316 + // VUID-VkBufferMemoryBarrier2-shadingRateImage-07316 + // VUID-VkBufferMemoryBarrier2-shadingRateImage-07316 + // VUID-VkImageMemoryBarrier2-shadingRateImage-07316 + // VUID-VkImageMemoryBarrier2-shadingRateImage-07316 + if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) + && !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`", + requires_one_of: RequiresOneOf { + features: &["attachment_fragment_shading_rate", "shading_rate_image"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-04957 + // VUID-VkMemoryBarrier2-dstStageMask-04957 + // VUID-VkBufferMemoryBarrier2-srcStageMask-04957 + // VUID-VkBufferMemoryBarrier2-dstStageMask-04957 + // VUID-VkImageMemoryBarrier2-srcStageMask-04957 + // VUID-VkImageMemoryBarrier2-dstStageMask-04957 + if stages.intersects(PipelineStages::SUBPASS_SHADING) + && !device.enabled_features().subpass_shading + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::SUBPASS_SHADING`", + requires_one_of: RequiresOneOf { + features: &["subpass_shading"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-04995 + // VUID-VkMemoryBarrier2-dstStageMask-04995 + // VUID-VkBufferMemoryBarrier2-srcStageMask-04995 + // VUID-VkBufferMemoryBarrier2-dstStageMask-04995 + // VUID-VkImageMemoryBarrier2-srcStageMask-04995 + // VUID-VkImageMemoryBarrier2-dstStageMask-04995 + if stages.intersects(PipelineStages::INVOCATION_MASK) + && !device.enabled_features().invocation_mask + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::INVOCATION_MASK`", + requires_one_of: RequiresOneOf { + features: &["invocation_mask"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdSetEvent-stageMask-03937 + if stages.is_empty() && !device.enabled_features().synchronization2 { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + is empty", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + // A bit of a ridiculous number of VUIDs... + + // VUID-VkMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkMemoryBarrier2-dstAccessMask-07458 + + // VUID-VkBufferMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkBufferMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkBufferMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkBufferMemoryBarrier2-dstAccessMask-07458 + + // VUID-VkImageMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkImageMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkImageMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkImageMemoryBarrier2-dstAccessMask-07458 + + if !AccessFlags::from(stages).contains(access) { + match ty { + 'm' => { + return Err( + SynchronizationError::MemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ) + } + 'b' => return Err( + SynchronizationError::BufferMemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ), + 'i' => return Err( + SynchronizationError::ImageMemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ), + _ => unreachable!(), + } + } + } + + // VUID-VkMemoryBarrier2-srcAccessMask-06256 + // VUID-VkBufferMemoryBarrier2-srcAccessMask-06256 + // VUID-VkImageMemoryBarrier2-srcAccessMask-06256 + if !device.enabled_features().ray_query + && src_access.intersects(AccessFlags::ACCELERATION_STRUCTURE_READ) + && src_stages.intersects( + PipelineStages::VERTEX_SHADER + | PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER + | PipelineStages::GEOMETRY_SHADER + | PipelineStages::FRAGMENT_SHADER + | PipelineStages::COMPUTE_SHADER + | PipelineStages::PRE_RASTERIZATION_SHADERS + | PipelineStages::TASK_SHADER + | PipelineStages::MESH_SHADER, + ) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_access` contains `ACCELERATION_STRUCTURE_READ`, and \ + `src_stages` contains a shader stage other than `RAY_TRACING_SHADER`", + requires_one_of: RequiresOneOf { + features: &["ray_query"], + ..Default::default() + }, + }); + } + + Ok(()) + }; + + let check_queue_family_ownership_transfer = |ty: char, + barrier_index: usize, + src_stages: PipelineStages, + dst_stages: PipelineStages, + queue_family_ownership_transfer: Option< + QueueFamilyOwnershipTransfer, + >, + sharing: &Sharing<_>| + -> Result<(), SynchronizationError> { + if let Some(transfer) = queue_family_ownership_transfer { + // VUID? + transfer.validate_device(device)?; + + // VUID-VkBufferMemoryBarrier2-srcQueueFamilyIndex-04087 + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04070 + // Ensured by the definition of `QueueFamilyOwnershipTransfer`. + + // VUID-VkBufferMemoryBarrier2-buffer-04088 + // VUID-VkImageMemoryBarrier2-image-04071 + // Ensured by the definition of `QueueFamilyOwnershipTransfer`. + + let queue_family_count = + device.physical_device().queue_family_properties().len() as u32; + + let provided_queue_family_index = match (sharing, transfer) { + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveBetweenLocal { + src_index, + dst_index, + }, + ) => Some(max(src_index, dst_index)), + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveToExternal { src_index } + | QueueFamilyOwnershipTransfer::ExclusiveToForeign { src_index }, + ) => Some(src_index), + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveFromExternal { dst_index } + | QueueFamilyOwnershipTransfer::ExclusiveFromForeign { dst_index }, + ) => Some(dst_index), + ( + Sharing::Concurrent(_), + QueueFamilyOwnershipTransfer::ConcurrentToExternal + | QueueFamilyOwnershipTransfer::ConcurrentFromExternal + | QueueFamilyOwnershipTransfer::ConcurrentToForeign + | QueueFamilyOwnershipTransfer::ConcurrentFromForeign, + ) => None, + _ => match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferSharingMismatch { + barrier_index, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferSharingMismatch { + barrier_index, + }), + _ => unreachable!(), + }, + }.filter(|&index| index >= queue_family_count); + + // VUID-VkBufferMemoryBarrier2-buffer-04089 + // VUID-VkImageMemoryBarrier2-image-04072 + + if let Some(provided_queue_family_index) = provided_queue_family_index { + match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + }), + _ => unreachable!(), + } + } + + // VUID-VkBufferMemoryBarrier2-srcStageMask-03851 + // VUID-VkImageMemoryBarrier2-srcStageMask-03854 + if src_stages.intersects(PipelineStages::HOST) + || dst_stages.intersects(PipelineStages::HOST) + { + match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferHostNotAllowed { + barrier_index, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferHostForbidden { + barrier_index, + }), + _ => unreachable!(), + } + } + } + + Ok(()) + }; + + for (barrier_index, barrier) in memory_barriers.iter().enumerate() { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + /* + Check stages and access + */ + + check_stages_access( + 'm', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + } + + for (barrier_index, barrier) in buffer_memory_barriers.iter().enumerate() { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + // VUID-VkBufferMemoryBarrier2-buffer-01931 + // Ensured by Buffer type construction. + + /* + Check stages and access + */ + + check_stages_access( + 'b', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + + /* + Check queue family transfer + */ + + check_queue_family_ownership_transfer( + 'b', + barrier_index, + src_stages, + dst_stages, + queue_family_ownership_transfer, + buffer.sharing(), + )?; + + /* + Check range + */ + + // VUID-VkBufferMemoryBarrier2-size-01188 + assert!(!range.is_empty()); + + // VUID-VkBufferMemoryBarrier2-offset-01187 + // VUID-VkBufferMemoryBarrier2-size-01189 + if range.end > buffer.size() { + return Err(SynchronizationError::BufferMemoryBarrierOutOfRange { + barrier_index, + range_end: range.end, + buffer_size: buffer.size(), + }); + } + } + + for (barrier_index, barrier) in image_memory_barriers.iter().enumerate() { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + // VUID-VkImageMemoryBarrier2-image-01932 + // Ensured by Image type construction. + + /* + Check stages and access + */ + + check_stages_access( + 'i', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + + /* + Check layouts + */ + + // VUID-VkImageMemoryBarrier2-oldLayout-parameter + old_layout.validate_device(device)?; + + // VUID-VkImageMemoryBarrier2-newLayout-parameter + new_layout.validate_device(device)?; + + // VUID-VkImageMemoryBarrier2-srcStageMask-03855 + if src_stages.intersects(PipelineStages::HOST) + && !matches!( + old_layout, + ImageLayout::Preinitialized | ImageLayout::Undefined | ImageLayout::General + ) + { + return Err( + SynchronizationError::ImageMemoryBarrierOldLayoutFromHostInvalid { + barrier_index, + old_layout, + }, + ); + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01197 + // Not checked yet, therefore unsafe. + + // VUID-VkImageMemoryBarrier2-newLayout-01198 + if matches!( + new_layout, + ImageLayout::Undefined | ImageLayout::Preinitialized + ) { + return Err(SynchronizationError::ImageMemoryBarrierNewLayoutInvalid { + barrier_index, + }); + } + + // VUID-VkImageMemoryBarrier2-attachmentFeedbackLoopLayout-07313 + /*if !device.enabled_features().attachment_feedback_loop_layout + && matches!(new_layout, ImageLayout::AttachmentFeedbackLoopOptimal) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element where \ + `new_layout` is `AttachmentFeedbackLoopOptimal`", + requires_one_of: RequiresOneOf { + features: &["attachment_feedback_loop_layout"], + ..Default::default() + }, + }); + }*/ + + for layout in [old_layout, new_layout] { + // VUID-VkImageMemoryBarrier2-synchronization2-06911 + /*if !device.enabled_features().synchronization2 + && matches!( + layout, + ImageLayout::AttachmentOptimal | ImageLayout::ReadOnlyOptimal + ) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element \ + where `old_layout` or `new_layout` is `AttachmentOptimal` or \ + `ReadOnlyOptimal`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + }*/ + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-07006 + /*if layout == ImageLayout::AttachmentFeedbackLoopOptimal { + if !image.usage().intersects( + ImageUsage::COLOR_ATTACHMENT | ImageUsage::DEPTH_STENCIL_ATTACHMENT, + ) { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::COLOR_ATTACHMENT + | ImageUsage::DEPTH_STENCIL_ATTACHMENT, + }, + ); + } + + if !image + .usage() + .intersects(ImageUsage::INPUT_ATTACHMENT | ImageUsage::SAMPLED) + { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::INPUT_ATTACHMENT + | ImageUsage::SAMPLED, + }, + ); + } + + if !image + .usage() + .intersects(ImageUsage::ATTACHMENT_FEEDBACK_LOOP) + { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::ATTACHMENT_FEEDBACK_LOOP, + }, + ); + } + }*/ + + let requires_one_of_usage = match layout { + // VUID-VkImageMemoryBarrier2-oldLayout-01208 + ImageLayout::ColorAttachmentOptimal => ImageUsage::COLOR_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-oldLayout-01209 + ImageLayout::DepthStencilAttachmentOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01210 + ImageLayout::DepthStencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01211 + ImageLayout::ShaderReadOnlyOptimal => { + ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01212 + ImageLayout::TransferSrcOptimal => ImageUsage::TRANSFER_SRC, + + // VUID-VkImageMemoryBarrier2-oldLayout-01213 + ImageLayout::TransferDstOptimal => ImageUsage::TRANSFER_DST, + + // VUID-VkImageMemoryBarrier2-oldLayout-01658 + ImageLayout::DepthReadOnlyStencilAttachmentOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01659 + ImageLayout::DepthAttachmentStencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + /* + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04065 + ImageLayout::DepthReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04066 + ImageLayout::DepthAttachmentOptimal => ImageUsage::DEPTH_STENCIL_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04067 + ImageLayout::StencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04068 + ImageLayout::StencilAttachmentOptimal => ImageUsage::DEPTH_STENCIL_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-03938 + ImageLayout::AttachmentOptimal => { + ImageUsage::COLOR_ATTACHMENT | ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-03939 + ImageLayout::ReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-02088 + ImageLayout::FragmentShadingRateAttachmentOptimal => { + ImageUsage::FRAGMENT_SHADING_RATE_ATTACHMENT + } + */ + _ => continue, + }; + + if !image.usage().intersects(requires_one_of_usage) { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage, + }, + ); + } + } + + /* + Check queue family tansfer + */ + + check_queue_family_ownership_transfer( + 'i', + barrier_index, + src_stages, + dst_stages, + queue_family_ownership_transfer, + image.sharing(), + )?; + + /* + Check subresource range + */ + + // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask + assert!(!subresource_range.aspects.is_empty()); + + // VUID-VkImageSubresourceRange-aspectMask-parameter + subresource_range.aspects.validate_device(device)?; + + let image_aspects = image.format().unwrap().aspects(); + + // VUID-VkImageMemoryBarrier2-image-01673 + // VUID-VkImageMemoryBarrier2-image-03319 + if image_aspects.contains(subresource_range.aspects) { + return Err(SynchronizationError::ImageMemoryBarrierAspectsNotAllowed { + barrier_index, + aspects: subresource_range.aspects - image_aspects, + }); + } + + if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { + // VUID-VkImageMemoryBarrier2-image-03320 + if !device.enabled_features().separate_depth_stencil_layouts + && image_aspects.contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + && !subresource_range + .aspects + .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element \ + where `image` has both a depth and a stencil aspect, and \ + `subresource_range.aspects` does not contain both aspects", + requires_one_of: RequiresOneOf { + features: &["separate_depth_stencil_layouts"], + ..Default::default() + }, + }); + } + } else { + // VUID-VkImageMemoryBarrier2-image-01671 + if !image.flags().intersects(ImageCreateFlags::DISJOINT) + && subresource_range.aspects != ImageAspects::COLOR + { + return Err(SynchronizationError::ImageMemoryBarrierAspectsNotAllowed { + barrier_index, + aspects: subresource_range.aspects - ImageAspects::COLOR, + }); + } + } + + // VUID-VkImageSubresourceRange-levelCount-01720 + assert!(!subresource_range.mip_levels.is_empty()); + + // VUID-VkImageMemoryBarrier2-subresourceRange-01486 + // VUID-VkImageMemoryBarrier2-subresourceRange-01724 + if subresource_range.mip_levels.end > image.mip_levels() { + return Err( + SynchronizationError::ImageMemoryBarrierMipLevelsOutOfRange { + barrier_index, + mip_levels_range_end: subresource_range.mip_levels.end, + image_mip_levels: image.mip_levels(), + }, + ); + } + + // VUID-VkImageSubresourceRange-layerCount-01721 + assert!(!subresource_range.array_layers.is_empty()); + + // VUID-VkImageMemoryBarrier2-subresourceRange-01488 + // VUID-VkImageMemoryBarrier2-subresourceRange-01725 + if subresource_range.array_layers.end > image.dimensions().array_layers() { + return Err( + SynchronizationError::ImageMemoryBarrierArrayLayersOutOfRange { + barrier_index, + array_layers_range_end: subresource_range.array_layers.end, + image_array_layers: image.dimensions().array_layers(), + }, + ); + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn set_event_unchecked( + &mut self, + event: Arc<Event>, + dependency_info: DependencyInfo, + ) -> &mut Self { + let DependencyInfo { + dependency_flags, + memory_barriers, + buffer_memory_barriers, + image_memory_barriers, + _ne: _, + } = dependency_info; + + let fns = self.device().fns(); + + if self.device().enabled_features().synchronization2 { + let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers + .iter() + .map(|barrier| { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + ash::vk::MemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + ..Default::default() + } + }) + .collect(); + + let buffer_memory_barriers_vk: SmallVec<[_; 8]> = buffer_memory_barriers + .iter() + .map(|barrier| { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::BufferMemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + src_queue_family_index, + dst_queue_family_index, + buffer: buffer.handle(), + offset: range.start, + size: range.end - range.start, + ..Default::default() + } + }) + .collect(); + + let image_memory_barriers_vk: SmallVec<[_; 8]> = image_memory_barriers + .iter() + .map(|barrier| { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::ImageMemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + old_layout: old_layout.into(), + new_layout: new_layout.into(), + src_queue_family_index, + dst_queue_family_index, + image: image.handle(), + subresource_range: subresource_range.clone().into(), + ..Default::default() + } + }) + .collect(); + + let dependency_info_vk = ash::vk::DependencyInfo { + dependency_flags: dependency_flags.into(), + memory_barrier_count: memory_barriers_vk.len() as u32, + p_memory_barriers: memory_barriers_vk.as_ptr(), + buffer_memory_barrier_count: buffer_memory_barriers_vk.len() as u32, + p_buffer_memory_barriers: buffer_memory_barriers_vk.as_ptr(), + image_memory_barrier_count: image_memory_barriers_vk.len() as u32, + p_image_memory_barriers: image_memory_barriers_vk.as_ptr(), + ..Default::default() + }; + + if self.device().api_version() >= Version::V1_3 { + (fns.v1_3.cmd_set_event2)(self.handle(), event.handle(), &dependency_info_vk); + } else { + debug_assert!(self.device().enabled_extensions().khr_synchronization2); + (fns.khr_synchronization2.cmd_set_event2_khr)( + self.handle(), + event.handle(), + &dependency_info_vk, + ); + } + } else { + // The original function only takes a source stage mask; the rest of the info is + // provided with `wait_events` instead. Therefore, we condense the source stages + // here and ignore the rest. + + let mut stage_mask = ash::vk::PipelineStageFlags::empty(); + + for barrier in memory_barriers { + stage_mask |= barrier.src_stages.into(); + } + + for barrier in buffer_memory_barriers { + stage_mask |= barrier.src_stages.into(); + } + + for barrier in image_memory_barriers { + stage_mask |= barrier.src_stages.into(); + } + + if stage_mask.is_empty() { + // "VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT is [...] equivalent to + // VK_PIPELINE_STAGE_2_NONE in the first scope." + stage_mask |= ash::vk::PipelineStageFlags::TOP_OF_PIPE; + } + + (fns.v1_0.cmd_set_event)(self.handle(), event.handle(), stage_mask); + } + + self.resources.push(Box::new(event)); + + // TODO: sync state update + + self.next_command_index += 1; + self + } + + /// Waits for one or more [`Event`]s to be signaled. + /// + /// # Safety + /// + /// - For each element in `events`, if the event is signaled by [`set_event`], that command + /// must have already been recorded or previously submitted to the queue, and the + /// `DependencyInfo` provided here must be equal to the `DependencyInfo` used in that + /// command. + /// - For each element in `events`, if the event is signaled by [`Event::set`], that function + /// must have already been called before submitting this command to a queue. + /// + /// [`set_event`]: Self::set_event + /// [`Event::set`]: Event::set + #[inline] + pub unsafe fn wait_events( + &mut self, + events: impl IntoIterator<Item = (Arc<Event>, DependencyInfo)>, + ) -> Result<&mut Self, SynchronizationError> { + let events: SmallVec<[(Arc<Event>, DependencyInfo); 4]> = events.into_iter().collect(); + self.validate_wait_events(&events)?; + + unsafe { Ok(self.wait_events_unchecked(events)) } + } + + fn validate_wait_events( + &self, + events: &[(Arc<Event>, DependencyInfo)], + ) -> Result<(), SynchronizationError> { + // VUID-vkCmdWaitEvents2-commandBuffer-03846 + // TODO: + + let device = self.device(); + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdWaitEvents2-commandBuffer-cmdpool + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(SynchronizationError::NotSupportedByQueueFamily); + } + + if events.is_empty() { + return Ok(()); + } + + for (event, dependency_info) in events { + // VUID-vkCmdWaitEvents2-commonparent + assert_eq!(device, event.device()); + + // VUID-vkCmdWaitEvents2-pEvents-03838 + // TODO: + + // VUID-vkCmdWaitEvents2-pEvents-03839 + // TODO: + + // VUID-vkCmdWaitEvents2-pEvents-03840 + // TODO: + + // VUID-vkCmdWaitEvents2-pEvents-03841 + // TODO: + + let &DependencyInfo { + dependency_flags, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne: _, + } = dependency_info; + + // VUID-VkDependencyInfo-dependencyFlags-parameter + dependency_flags.validate_device(device)?; + + let check_stages_access = |ty: char, + barrier_index: usize, + src_stages: PipelineStages, + src_access: AccessFlags, + dst_stages: PipelineStages, + dst_access: AccessFlags| + -> Result<(), SynchronizationError> { + for (stages, access) in [(src_stages, src_access), (dst_stages, dst_access)] { + // VUID-vkCmdWaitEvents2-synchronization2-03836 + if !device.enabled_features().synchronization2 { + if stages.is_2() { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_stages` or `dst_stages` contains flags from \ + `VkPipelineStageFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + if access.is_2() { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_access` or `dst_access` contains flags from \ + `VkAccessFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + } + + // VUID-VkMemoryBarrier2-srcStageMask-parameter + // VUID-VkMemoryBarrier2-dstStageMask-parameter + // VUID-VkBufferMemoryBarrier2-srcStageMask-parameter + // VUID-VkBufferMemoryBarrier2-dstStageMask-parameter + // VUID-VkImageMemoryBarrier2-srcStageMask-parameter + // VUID-VkImageMemoryBarrier2-dstStageMask-parameter + stages.validate_device(device)?; + + // VUID-VkMemoryBarrier2-srcAccessMask-parameter + // VUID-VkMemoryBarrier2-dstAccessMask-parameter + // VUID-VkBufferMemoryBarrier2-srcAccessMask-parameter + // VUID-VkBufferMemoryBarrier2-dstAccessMask-parameter + // VUID-VkImageMemoryBarrier2-srcAccessMask-parameter + // VUID-VkImageMemoryBarrier2-dstAccessMask-parameter + access.validate_device(device)?; + + // VUID-vkCmdWaitEvents2-srcStageMask-03842 + // VUID-vkCmdWaitEvents2-dstStageMask-03843 + if !PipelineStages::from(queue_family_properties.queue_flags).contains(stages) { + match ty { + 'm' => { + return Err(SynchronizationError::MemoryBarrierStageNotSupported { + barrier_index, + }) + } + 'b' => { + return Err( + SynchronizationError::BufferMemoryBarrierStageNotSupported { + barrier_index, + }, + ) + } + 'i' => { + return Err( + SynchronizationError::ImageMemoryBarrierStageNotSupported { + barrier_index, + }, + ) + } + _ => unreachable!(), + } + } + + // VUID-VkMemoryBarrier2-srcStageMask-03929 + // VUID-VkMemoryBarrier2-dstStageMask-03929 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03929 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03929 + // VUID-VkImageMemoryBarrier2-srcStageMask-03930 + // VUID-VkImageMemoryBarrier2-dstStageMask-03930 + if stages.intersects(PipelineStages::GEOMETRY_SHADER) + && !device.enabled_features().geometry_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::GEOMETRY_SHADER`", + requires_one_of: RequiresOneOf { + features: &["geometry_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03930 + // VUID-VkMemoryBarrier2-dstStageMask-03930 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03930 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03930 + // VUID-VkImageMemoryBarrier2-srcStageMask-03930 + // VUID-VkImageMemoryBarrier2-dstStageMask-03930 + if stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) && !device.enabled_features().tessellation_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`", + requires_one_of: RequiresOneOf { + features: &["tessellation_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03931 + // VUID-VkMemoryBarrier2-dstStageMask-03931 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03931 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03931 + // VUID-VImagekMemoryBarrier2-srcStageMask-03931 + // VUID-VkImageMemoryBarrier2-dstStageMask-03931 + if stages.intersects(PipelineStages::CONDITIONAL_RENDERING) + && !device.enabled_features().conditional_rendering + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::CONDITIONAL_RENDERING`", + requires_one_of: RequiresOneOf { + features: &["conditional_rendering"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03932 + // VUID-VkMemoryBarrier2-dstStageMask-03932 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03932 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03932 + // VUID-VkImageMemoryBarrier2-srcStageMask-03932 + // VUID-VkImageMemoryBarrier2-dstStageMask-03932 + if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) + && !device.enabled_features().fragment_density_map + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`", + requires_one_of: RequiresOneOf { + features: &["fragment_density_map"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03933 + // VUID-VkMemoryBarrier2-dstStageMask-03933 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03933 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03933 + // VUID-VkImageMemoryBarrier2-srcStageMask-03933 + // VUID-VkImageMemoryBarrier2-dstStageMask-03933 + if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) + && !device.enabled_features().transform_feedback + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TRANSFORM_FEEDBACK`", + requires_one_of: RequiresOneOf { + features: &["transform_feedback"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03934 + // VUID-VkMemoryBarrier2-dstStageMask-03934 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03934 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03934 + // VUID-VkImageMemoryBarrier2-srcStageMask-03934 + // VUID-VkImageMemoryBarrier2-dstStageMask-03934 + if stages.intersects(PipelineStages::MESH_SHADER) + && !device.enabled_features().mesh_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::MESH_SHADER`", + requires_one_of: RequiresOneOf { + features: &["mesh_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-03935 + // VUID-VkMemoryBarrier2-dstStageMask-03935 + // VUID-VkBufferMemoryBarrier2-srcStageMask-03935 + // VUID-VkBufferMemoryBarrier2-dstStageMask-03935 + // VUID-VkImageMemoryBarrier2-srcStageMask-03935 + // VUID-VkImageMemoryBarrier2-dstStageMask-03935 + if stages.intersects(PipelineStages::TASK_SHADER) + && !device.enabled_features().task_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::TASK_SHADER`", + requires_one_of: RequiresOneOf { + features: &["task_shader"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-shadingRateImage-07316 + // VUID-VkMemoryBarrier2-shadingRateImage-07316 + // VUID-VkBufferMemoryBarrier2-shadingRateImage-07316 + // VUID-VkBufferMemoryBarrier2-shadingRateImage-07316 + // VUID-VkImageMemoryBarrier2-shadingRateImage-07316 + // VUID-VkImageMemoryBarrier2-shadingRateImage-07316 + if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) + && !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`", + requires_one_of: RequiresOneOf { + features: &[ + "attachment_fragment_shading_rate", + "shading_rate_image", + ], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-04957 + // VUID-VkMemoryBarrier2-dstStageMask-04957 + // VUID-VkBufferMemoryBarrier2-srcStageMask-04957 + // VUID-VkBufferMemoryBarrier2-dstStageMask-04957 + // VUID-VkImageMemoryBarrier2-srcStageMask-04957 + // VUID-VkImageMemoryBarrier2-dstStageMask-04957 + if stages.intersects(PipelineStages::SUBPASS_SHADING) + && !device.enabled_features().subpass_shading + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::SUBPASS_SHADING`", + requires_one_of: RequiresOneOf { + features: &["subpass_shading"], + ..Default::default() + }, + }); + } + + // VUID-VkMemoryBarrier2-srcStageMask-04995 + // VUID-VkMemoryBarrier2-dstStageMask-04995 + // VUID-VkBufferMemoryBarrier2-srcStageMask-04995 + // VUID-VkBufferMemoryBarrier2-dstStageMask-04995 + // VUID-VkImageMemoryBarrier2-srcStageMask-04995 + // VUID-VkImageMemoryBarrier2-dstStageMask-04995 + if stages.intersects(PipelineStages::INVOCATION_MASK) + && !device.enabled_features().invocation_mask + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + contains `PipelineStages::INVOCATION_MASK`", + requires_one_of: RequiresOneOf { + features: &["invocation_mask"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdWaitEvents-srcStageMask-03937 + // VUID-vkCmdWaitEvents-dstStageMask-03937 + if stages.is_empty() && !device.enabled_features().synchronization2 { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where `stages` \ + is empty", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + // A bit of a ridiculous number of VUIDs... + + // VUID-VkMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkMemoryBarrier2-dstAccessMask-07458 + + // VUID-VkBufferMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkBufferMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkBufferMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkBufferMemoryBarrier2-dstAccessMask-07458 + + // VUID-VkImageMemoryBarrier2-srcAccessMask-03900 + // .. + // VUID-VkImageMemoryBarrier2-srcAccessMask-07458 + + // VUID-VkImageMemoryBarrier2-dstAccessMask-03900 + // .. + // VUID-VkImageMemoryBarrier2-dstAccessMask-07458 + + if !AccessFlags::from(stages).contains(access) { + match ty { + 'm' => { + return Err( + SynchronizationError::MemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ) + } + 'b' => return Err( + SynchronizationError::BufferMemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ), + 'i' => return Err( + SynchronizationError::ImageMemoryBarrierAccessNotSupportedByStages { + barrier_index, + }, + ), + _ => unreachable!(), + } + } + } + + // VUID-VkMemoryBarrier2-srcAccessMask-06256 + // VUID-VkBufferMemoryBarrier2-srcAccessMask-06256 + // VUID-VkImageMemoryBarrier2-srcAccessMask-06256 + if !device.enabled_features().ray_query + && src_access.intersects(AccessFlags::ACCELERATION_STRUCTURE_READ) + && src_stages.intersects( + PipelineStages::VERTEX_SHADER + | PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER + | PipelineStages::GEOMETRY_SHADER + | PipelineStages::FRAGMENT_SHADER + | PipelineStages::COMPUTE_SHADER + | PipelineStages::PRE_RASTERIZATION_SHADERS + | PipelineStages::TASK_SHADER + | PipelineStages::MESH_SHADER, + ) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "One of `dependency_info.memory_barriers`, \ + `dependency_info.buffer_memory_barriers` or \ + `dependency_info.image_memory_barriers` has an element where \ + `src_access` contains `ACCELERATION_STRUCTURE_READ`, and \ + `src_stages` contains a shader stage other than `RAY_TRACING_SHADER`", + requires_one_of: RequiresOneOf { + features: &["ray_query"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdWaitEvents2-dependencyFlags-03844 + if self.builder_state.render_pass.is_some() + && src_stages.intersects(PipelineStages::HOST) + { + todo!() + } + + Ok(()) + }; + + let check_queue_family_ownership_transfer = + |ty: char, + barrier_index: usize, + src_stages: PipelineStages, + dst_stages: PipelineStages, + queue_family_ownership_transfer: Option<QueueFamilyOwnershipTransfer>, + sharing: &Sharing<_>| + -> Result<(), SynchronizationError> { + if let Some(transfer) = queue_family_ownership_transfer { + // VUID? + transfer.validate_device(device)?; + + // VUID-VkBufferMemoryBarrier2-srcQueueFamilyIndex-04087 + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04070 + // Ensured by the definition of `QueueFamilyOwnershipTransfer`. + + // VUID-VkBufferMemoryBarrier2-buffer-04088 + // VUID-VkImageMemoryBarrier2-image-04071 + // Ensured by the definition of `QueueFamilyOwnershipTransfer`. + + let queue_family_count = + device.physical_device().queue_family_properties().len() as u32; + + let provided_queue_family_index = match (sharing, transfer) { + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveBetweenLocal { + src_index, + dst_index, + }, + ) => Some(max(src_index, dst_index)), + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveToExternal { src_index } + | QueueFamilyOwnershipTransfer::ExclusiveToForeign { src_index }, + ) => Some(src_index), + ( + Sharing::Exclusive, + QueueFamilyOwnershipTransfer::ExclusiveFromExternal { dst_index } + | QueueFamilyOwnershipTransfer::ExclusiveFromForeign { dst_index }, + ) => Some(dst_index), + ( + Sharing::Concurrent(_), + QueueFamilyOwnershipTransfer::ConcurrentToExternal + | QueueFamilyOwnershipTransfer::ConcurrentFromExternal + | QueueFamilyOwnershipTransfer::ConcurrentToForeign + | QueueFamilyOwnershipTransfer::ConcurrentFromForeign, + ) => None, + _ => match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferSharingMismatch { + barrier_index, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferSharingMismatch { + barrier_index, + }), + _ => unreachable!(), + }, + }.filter(|&index| index >= queue_family_count); + + // VUID-VkBufferMemoryBarrier2-buffer-04089 + // VUID-VkImageMemoryBarrier2-image-04072 + + if let Some(provided_queue_family_index) = provided_queue_family_index { + match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + }), + _ => unreachable!(), + } + } + + // VUID-VkBufferMemoryBarrier2-srcStageMask-03851 + // VUID-VkImageMemoryBarrier2-srcStageMask-03854 + if src_stages.intersects(PipelineStages::HOST) + || dst_stages.intersects(PipelineStages::HOST) + { + match ty { + 'b' => return Err(SynchronizationError::BufferMemoryBarrierOwnershipTransferHostNotAllowed { + barrier_index, + }), + 'i' => return Err(SynchronizationError::ImageMemoryBarrierOwnershipTransferHostForbidden { + barrier_index, + }), + _ => unreachable!(), + } + } + } + + Ok(()) + }; + + for (barrier_index, barrier) in memory_barriers.iter().enumerate() { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + /* + Check stages and access + */ + + check_stages_access( + 'm', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + } + + for (barrier_index, barrier) in buffer_memory_barriers.iter().enumerate() { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + // VUID-VkBufferMemoryBarrier2-buffer-01931 + // Ensured by Buffer type construction. + + /* + Check stages and access + */ + + check_stages_access( + 'b', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + + /* + Check queue family transfer + */ + + check_queue_family_ownership_transfer( + 'b', + barrier_index, + src_stages, + dst_stages, + queue_family_ownership_transfer, + buffer.sharing(), + )?; + + /* + Check range + */ + + // VUID-VkBufferMemoryBarrier2-size-01188 + assert!(!range.is_empty()); + + // VUID-VkBufferMemoryBarrier2-offset-01187 + // VUID-VkBufferMemoryBarrier2-size-01189 + if range.end > buffer.size() { + return Err(SynchronizationError::BufferMemoryBarrierOutOfRange { + barrier_index, + range_end: range.end, + buffer_size: buffer.size(), + }); + } + } + + for (barrier_index, barrier) in image_memory_barriers.iter().enumerate() { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + // VUID-VkImageMemoryBarrier2-image-01932 + // Ensured by Image type construction. + + /* + Check stages and access + */ + + check_stages_access( + 'i', + barrier_index, + src_stages, + src_access, + dst_stages, + dst_access, + )?; + + /* + Check layouts + */ + + // VUID-VkImageMemoryBarrier2-oldLayout-parameter + old_layout.validate_device(device)?; + + // VUID-VkImageMemoryBarrier2-newLayout-parameter + new_layout.validate_device(device)?; + + // VUID-VkImageMemoryBarrier2-srcStageMask-03855 + if src_stages.intersects(PipelineStages::HOST) + && !matches!( + old_layout, + ImageLayout::Preinitialized | ImageLayout::Undefined | ImageLayout::General + ) + { + return Err( + SynchronizationError::ImageMemoryBarrierOldLayoutFromHostInvalid { + barrier_index, + old_layout, + }, + ); + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01197 + // Not checked yet, therefore unsafe. + + // VUID-VkImageMemoryBarrier2-newLayout-01198 + if matches!( + new_layout, + ImageLayout::Undefined | ImageLayout::Preinitialized + ) { + return Err(SynchronizationError::ImageMemoryBarrierNewLayoutInvalid { + barrier_index, + }); + } + + // VUID-VkImageMemoryBarrier2-attachmentFeedbackLoopLayout-07313 + /*if !device.enabled_features().attachment_feedback_loop_layout + && matches!(new_layout, ImageLayout::AttachmentFeedbackLoopOptimal) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element where \ + `new_layout` is `AttachmentFeedbackLoopOptimal`", + requires_one_of: RequiresOneOf { + features: &["attachment_feedback_loop_layout"], + ..Default::default() + }, + }); + }*/ + + for layout in [old_layout, new_layout] { + // VUID-VkImageMemoryBarrier2-synchronization2-06911 + /*if !device.enabled_features().synchronization2 + && matches!( + layout, + ImageLayout::AttachmentOptimal | ImageLayout::ReadOnlyOptimal + ) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element \ + where `old_layout` or `new_layout` is `AttachmentOptimal` or \ + `ReadOnlyOptimal`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + }*/ + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-07006 + /*if layout == ImageLayout::AttachmentFeedbackLoopOptimal { + if !image.usage().intersects( + ImageUsage::COLOR_ATTACHMENT | ImageUsage::DEPTH_STENCIL_ATTACHMENT, + ) { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::COLOR_ATTACHMENT + | ImageUsage::DEPTH_STENCIL_ATTACHMENT, + }, + ); + } + + if !image + .usage() + .intersects(ImageUsage::INPUT_ATTACHMENT | ImageUsage::SAMPLED) + { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::INPUT_ATTACHMENT + | ImageUsage::SAMPLED, + }, + ); + } + + if !image + .usage() + .intersects(ImageUsage::ATTACHMENT_FEEDBACK_LOOP) + { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage: ImageUsage::ATTACHMENT_FEEDBACK_LOOP, + }, + ); + } + }*/ + + let requires_one_of_usage = match layout { + // VUID-VkImageMemoryBarrier2-oldLayout-01208 + ImageLayout::ColorAttachmentOptimal => ImageUsage::COLOR_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-oldLayout-01209 + ImageLayout::DepthStencilAttachmentOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01210 + ImageLayout::DepthStencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01211 + ImageLayout::ShaderReadOnlyOptimal => { + ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01212 + ImageLayout::TransferSrcOptimal => ImageUsage::TRANSFER_SRC, + + // VUID-VkImageMemoryBarrier2-oldLayout-01213 + ImageLayout::TransferDstOptimal => ImageUsage::TRANSFER_DST, + + // VUID-VkImageMemoryBarrier2-oldLayout-01658 + ImageLayout::DepthReadOnlyStencilAttachmentOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-01659 + ImageLayout::DepthAttachmentStencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + /* + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04065 + ImageLayout::DepthReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04066 + ImageLayout::DepthAttachmentOptimal => ImageUsage::DEPTH_STENCIL_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04067 + ImageLayout::StencilReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-04068 + ImageLayout::StencilAttachmentOptimal => ImageUsage::DEPTH_STENCIL_ATTACHMENT, + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-03938 + ImageLayout::AttachmentOptimal => { + ImageUsage::COLOR_ATTACHMENT | ImageUsage::DEPTH_STENCIL_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-srcQueueFamilyIndex-03939 + ImageLayout::ReadOnlyOptimal => { + ImageUsage::DEPTH_STENCIL_ATTACHMENT + | ImageUsage::SAMPLED + | ImageUsage::INPUT_ATTACHMENT + } + + // VUID-VkImageMemoryBarrier2-oldLayout-02088 + ImageLayout::FragmentShadingRateAttachmentOptimal => { + ImageUsage::FRAGMENT_SHADING_RATE_ATTACHMENT + } + */ + _ => continue, + }; + + if !image.usage().intersects(requires_one_of_usage) { + return Err( + SynchronizationError::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage, + }, + ); + } + } + + /* + Check queue family tansfer + */ + + check_queue_family_ownership_transfer( + 'i', + barrier_index, + src_stages, + dst_stages, + queue_family_ownership_transfer, + image.sharing(), + )?; + + /* + Check subresource range + */ + + // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask + assert!(!subresource_range.aspects.is_empty()); + + // VUID-VkImageSubresourceRange-aspectMask-parameter + subresource_range.aspects.validate_device(device)?; + + let image_aspects = image.format().unwrap().aspects(); + + // VUID-VkImageMemoryBarrier2-image-01673 + // VUID-VkImageMemoryBarrier2-image-03319 + if image_aspects.contains(subresource_range.aspects) { + return Err(SynchronizationError::ImageMemoryBarrierAspectsNotAllowed { + barrier_index, + aspects: subresource_range.aspects - image_aspects, + }); + } + + if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { + // VUID-VkImageMemoryBarrier2-image-03320 + if !device.enabled_features().separate_depth_stencil_layouts + && image_aspects.contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + && !subresource_range + .aspects + .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`dependency_info.image_memory_barriers` has an element \ + where `image` has both a depth and a stencil aspect, and \ + `subresource_range.aspects` does not contain both aspects", + requires_one_of: RequiresOneOf { + features: &["separate_depth_stencil_layouts"], + ..Default::default() + }, + }); + } + } else { + // VUID-VkImageMemoryBarrier2-image-01671 + if !image.flags().intersects(ImageCreateFlags::DISJOINT) + && subresource_range.aspects != ImageAspects::COLOR + { + return Err(SynchronizationError::ImageMemoryBarrierAspectsNotAllowed { + barrier_index, + aspects: subresource_range.aspects - ImageAspects::COLOR, + }); + } + } + + // VUID-VkImageSubresourceRange-levelCount-01720 + assert!(!subresource_range.mip_levels.is_empty()); + + // VUID-VkImageMemoryBarrier2-subresourceRange-01486 + // VUID-VkImageMemoryBarrier2-subresourceRange-01724 + if subresource_range.mip_levels.end > image.mip_levels() { + return Err( + SynchronizationError::ImageMemoryBarrierMipLevelsOutOfRange { + barrier_index, + mip_levels_range_end: subresource_range.mip_levels.end, + image_mip_levels: image.mip_levels(), + }, + ); + } + + // VUID-VkImageSubresourceRange-layerCount-01721 + assert!(!subresource_range.array_layers.is_empty()); + + // VUID-VkImageMemoryBarrier2-subresourceRange-01488 + // VUID-VkImageMemoryBarrier2-subresourceRange-01725 + if subresource_range.array_layers.end > image.dimensions().array_layers() { + return Err( + SynchronizationError::ImageMemoryBarrierArrayLayersOutOfRange { + barrier_index, + array_layers_range_end: subresource_range.array_layers.end, + image_array_layers: image.dimensions().array_layers(), + }, + ); + } + } + } + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn wait_events_unchecked( + &mut self, + events: impl IntoIterator<Item = (Arc<Event>, DependencyInfo)>, + ) -> &mut Self { + let events: SmallVec<[(Arc<Event>, DependencyInfo); 4]> = events.into_iter().collect(); + let fns = self.device().fns(); + + // VUID-vkCmdWaitEvents2-pEvents-03837 + // Ensured by using `vkCmdSetEvent2` and `vkCmdWaitEvents2` under the exact same + // conditions, i.e. when `synchronization2` is enabled. + + if self.device().enabled_features().synchronization2 { + struct PerDependencyInfo { + memory_barriers_vk: SmallVec<[ash::vk::MemoryBarrier2; 2]>, + buffer_memory_barriers_vk: SmallVec<[ash::vk::BufferMemoryBarrier2; 8]>, + image_memory_barriers_vk: SmallVec<[ash::vk::ImageMemoryBarrier2; 8]>, + } + + let mut events_vk: SmallVec<[_; 4]> = SmallVec::new(); + let mut dependency_infos_vk: SmallVec<[_; 4]> = SmallVec::new(); + let mut per_dependency_info_vk: SmallVec<[_; 4]> = SmallVec::new(); + + for (event, dependency_info) in &events { + let &DependencyInfo { + dependency_flags, + ref memory_barriers, + ref buffer_memory_barriers, + ref image_memory_barriers, + _ne: _, + } = dependency_info; + + let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers + .iter() + .map(|barrier| { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + ash::vk::MemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + ..Default::default() + } + }) + .collect(); + + let buffer_memory_barriers_vk: SmallVec<[_; 8]> = buffer_memory_barriers + .iter() + .map(|barrier| { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::BufferMemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + src_queue_family_index, + dst_queue_family_index, + buffer: buffer.handle(), + offset: range.start, + size: range.end - range.start, + ..Default::default() + } + }) + .collect(); + + let image_memory_barriers_vk: SmallVec<[_; 8]> = image_memory_barriers + .iter() + .map(|barrier| { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::ImageMemoryBarrier2 { + src_stage_mask: src_stages.into(), + src_access_mask: src_access.into(), + dst_stage_mask: dst_stages.into(), + dst_access_mask: dst_access.into(), + old_layout: old_layout.into(), + new_layout: new_layout.into(), + src_queue_family_index, + dst_queue_family_index, + image: image.handle(), + subresource_range: subresource_range.clone().into(), + ..Default::default() + } + }) + .collect(); + + events_vk.push(event.handle()); + dependency_infos_vk.push(ash::vk::DependencyInfo { + dependency_flags: dependency_flags.into(), + memory_barrier_count: 0, + p_memory_barriers: ptr::null(), + buffer_memory_barrier_count: 0, + p_buffer_memory_barriers: ptr::null(), + image_memory_barrier_count: 0, + p_image_memory_barriers: ptr::null(), + ..Default::default() + }); + per_dependency_info_vk.push(PerDependencyInfo { + memory_barriers_vk, + buffer_memory_barriers_vk, + image_memory_barriers_vk, + }); + } + + for ( + dependency_info_vk, + PerDependencyInfo { + memory_barriers_vk, + buffer_memory_barriers_vk, + image_memory_barriers_vk, + }, + ) in (dependency_infos_vk.iter_mut()).zip(per_dependency_info_vk.iter_mut()) + { + *dependency_info_vk = ash::vk::DependencyInfo { + memory_barrier_count: memory_barriers_vk.len() as u32, + p_memory_barriers: memory_barriers_vk.as_ptr(), + buffer_memory_barrier_count: buffer_memory_barriers_vk.len() as u32, + p_buffer_memory_barriers: buffer_memory_barriers_vk.as_ptr(), + image_memory_barrier_count: image_memory_barriers_vk.len() as u32, + p_image_memory_barriers: image_memory_barriers_vk.as_ptr(), + ..*dependency_info_vk + } + } + + if self.device().api_version() >= Version::V1_3 { + (fns.v1_3.cmd_wait_events2)( + self.handle(), + events_vk.len() as u32, + events_vk.as_ptr(), + dependency_infos_vk.as_ptr(), + ); + } else { + debug_assert!(self.device().enabled_extensions().khr_synchronization2); + (fns.khr_synchronization2.cmd_wait_events2_khr)( + self.handle(), + events_vk.len() as u32, + events_vk.as_ptr(), + dependency_infos_vk.as_ptr(), + ); + } + } else { + // With the original function, you can only specify a single dependency info for all + // events at once, rather than separately for each event. Therefore, to achieve the + // same behaviour as the "2" function, we split it up into multiple Vulkan API calls, + // one per event. + + for (event, dependency_info) in &events { + let events_vk = [event.handle()]; + + let DependencyInfo { + dependency_flags: _, + memory_barriers, + buffer_memory_barriers, + image_memory_barriers, + _ne: _, + } = dependency_info; + + let mut src_stage_mask = ash::vk::PipelineStageFlags::empty(); + let mut dst_stage_mask = ash::vk::PipelineStageFlags::empty(); + + let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers + .iter() + .map(|barrier| { + let &MemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + _ne: _, + } = barrier; + + src_stage_mask |= src_stages.into(); + dst_stage_mask |= dst_stages.into(); + + ash::vk::MemoryBarrier { + src_access_mask: src_access.into(), + dst_access_mask: dst_access.into(), + ..Default::default() + } + }) + .collect(); + + let buffer_memory_barriers_vk: SmallVec<[_; 8]> = buffer_memory_barriers + .iter() + .map(|barrier| { + let &BufferMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + queue_family_ownership_transfer, + ref buffer, + ref range, + _ne: _, + } = barrier; + + src_stage_mask |= src_stages.into(); + dst_stage_mask |= dst_stages.into(); + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::BufferMemoryBarrier { + src_access_mask: src_access.into(), + dst_access_mask: dst_access.into(), + src_queue_family_index, + dst_queue_family_index, + buffer: buffer.handle(), + offset: range.start, + size: range.end - range.start, + ..Default::default() + } + }) + .collect(); + + let image_memory_barriers_vk: SmallVec<[_; 8]> = image_memory_barriers + .iter() + .map(|barrier| { + let &ImageMemoryBarrier { + src_stages, + src_access, + dst_stages, + dst_access, + old_layout, + new_layout, + queue_family_ownership_transfer, + ref image, + ref subresource_range, + _ne: _, + } = barrier; + + src_stage_mask |= src_stages.into(); + dst_stage_mask |= dst_stages.into(); + + let (src_queue_family_index, dst_queue_family_index) = + queue_family_ownership_transfer.map_or( + (ash::vk::QUEUE_FAMILY_IGNORED, ash::vk::QUEUE_FAMILY_IGNORED), + Into::into, + ); + + ash::vk::ImageMemoryBarrier { + src_access_mask: src_access.into(), + dst_access_mask: dst_access.into(), + old_layout: old_layout.into(), + new_layout: new_layout.into(), + src_queue_family_index, + dst_queue_family_index, + image: image.handle(), + subresource_range: subresource_range.clone().into(), + ..Default::default() + } + }) + .collect(); + + if src_stage_mask.is_empty() { + // "VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT is [...] equivalent to + // VK_PIPELINE_STAGE_2_NONE in the first scope." + src_stage_mask |= ash::vk::PipelineStageFlags::TOP_OF_PIPE; + } + + if dst_stage_mask.is_empty() { + // "VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT is [...] equivalent to + // VK_PIPELINE_STAGE_2_NONE in the second scope." + dst_stage_mask |= ash::vk::PipelineStageFlags::BOTTOM_OF_PIPE; + } + + (fns.v1_0.cmd_wait_events)( + self.handle(), + 1, + events_vk.as_ptr(), + src_stage_mask, + dst_stage_mask, + memory_barriers_vk.len() as u32, + memory_barriers_vk.as_ptr(), + buffer_memory_barriers_vk.len() as u32, + buffer_memory_barriers_vk.as_ptr(), + image_memory_barriers_vk.len() as u32, + image_memory_barriers_vk.as_ptr(), + ); + } + } + + self.resources + .extend(events.into_iter().map(|(event, _)| Box::new(event) as _)); + + // TODO: sync state update + + self.next_command_index += 1; + self + } + + /// Resets an [`Event`] back to the unsignaled state. + /// + /// # Safety + /// + /// - Appropriate synchronization must be provided for `event` against any previous + /// [`set_event`] or [`wait_events`] command. + /// + /// [`set_event`]: Self::set_event + /// [`wait_events`]: Self::wait_events + #[inline] + pub unsafe fn reset_event( + &mut self, + event: Arc<Event>, + stages: PipelineStages, + ) -> Result<&mut Self, SynchronizationError> { + self.validate_reset_event(&event, stages)?; + + unsafe { Ok(self.reset_event_unchecked(event, stages)) } + } + + fn validate_reset_event( + &self, + event: &Event, + stages: PipelineStages, + ) -> Result<(), SynchronizationError> { + // VUID-vkCmdResetEvent2-renderpass + if self.builder_state.render_pass.is_some() { + return Err(SynchronizationError::ForbiddenInsideRenderPass); + } + + // VUID-vkCmdResetEvent2-commandBuffer-03833 + // TODO: + + let device = self.device(); + let queue_family_properties = self.queue_family_properties(); + + // VUID-vkCmdResetEvent2-commandBuffer-cmdpool + if !queue_family_properties.queue_flags.intersects( + QueueFlags::GRAPHICS + | QueueFlags::COMPUTE + | QueueFlags::VIDEO_DECODE + | QueueFlags::VIDEO_ENCODE, + ) { + return Err(SynchronizationError::NotSupportedByQueueFamily); + } + + // VUID-vkCmdResetEvent2-commonparent + assert_eq!(device, event.device()); + + // VUID-vkCmdResetEvent2-stageMask-parameter + stages.validate_device(device)?; + + // VUID-vkCmdResetEvent2-synchronization2-03829 + if !device.enabled_features().synchronization2 { + if stages.is_2() { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains flags from `VkPipelineStageFlagBits2`", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + } + + // VUID-vkCmdResetEvent2-stageMask-03929 + if stages.intersects(PipelineStages::GEOMETRY_SHADER) + && !device.enabled_features().geometry_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::GEOMETRY_SHADER`", + requires_one_of: RequiresOneOf { + features: &["geometry_shader"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-03930 + if stages.intersects( + PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER, + ) && !device.enabled_features().tessellation_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \ + `PipelineStages::TESSELLATION_EVALUATION_SHADER`", + requires_one_of: RequiresOneOf { + features: &["tessellation_shader"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-03931 + if stages.intersects(PipelineStages::CONDITIONAL_RENDERING) + && !device.enabled_features().conditional_rendering + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::CONDITIONAL_RENDERING`", + requires_one_of: RequiresOneOf { + features: &["conditional_rendering"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-03932 + if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) + && !device.enabled_features().fragment_density_map + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`", + requires_one_of: RequiresOneOf { + features: &["fragment_density_map"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-03933 + if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) + && !device.enabled_features().transform_feedback + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::TRANSFORM_FEEDBACK`", + requires_one_of: RequiresOneOf { + features: &["transform_feedback"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-03934 + if stages.intersects(PipelineStages::MESH_SHADER) && !device.enabled_features().mesh_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::MESH_SHADER`", + requires_one_of: RequiresOneOf { + features: &["mesh_shader"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-03935 + if stages.intersects(PipelineStages::TASK_SHADER) && !device.enabled_features().task_shader + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::TASK_SHADER`", + requires_one_of: RequiresOneOf { + features: &["task_shader"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-shadingRateImage-07316 + if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) + && !(device.enabled_features().attachment_fragment_shading_rate + || device.enabled_features().shading_rate_image) + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains \ + `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`", + requires_one_of: RequiresOneOf { + features: &["attachment_fragment_shading_rate", "shading_rate_image"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-04957 + if stages.intersects(PipelineStages::SUBPASS_SHADING) + && !device.enabled_features().subpass_shading + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::SUBPASS_SHADING`", + requires_one_of: RequiresOneOf { + features: &["subpass_shading"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-04995 + if stages.intersects(PipelineStages::INVOCATION_MASK) + && !device.enabled_features().invocation_mask + { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` contains `PipelineStages::INVOCATION_MASK`", + requires_one_of: RequiresOneOf { + features: &["invocation_mask"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent-stageMask-03937 + if stages.is_empty() && !device.enabled_features().synchronization2 { + return Err(SynchronizationError::RequirementNotMet { + required_for: "`stages` is empty", + requires_one_of: RequiresOneOf { + features: &["synchronization2"], + ..Default::default() + }, + }); + } + + // VUID-vkCmdResetEvent2-stageMask-03830 + if stages.intersects(PipelineStages::HOST) { + todo!() + } + + // VUID-vkCmdResetEvent2-event-03831 + // VUID-vkCmdResetEvent2-event-03832 + // TODO: + + Ok(()) + } + + #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))] + pub unsafe fn reset_event_unchecked( + &mut self, + event: Arc<Event>, + stages: PipelineStages, + ) -> &mut Self { + let fns = self.device().fns(); + + if self.device().enabled_features().synchronization2 { + if self.device().api_version() >= Version::V1_3 { + (fns.v1_3.cmd_reset_event2)(self.handle(), event.handle(), stages.into()); + } else { + debug_assert!(self.device().enabled_extensions().khr_synchronization2); + (fns.khr_synchronization2.cmd_reset_event2_khr)( + self.handle(), + event.handle(), + stages.into(), + ); + } + } else { + (fns.v1_0.cmd_reset_event)(self.handle(), event.handle(), stages.into()); + } + + self.resources.push(Box::new(event)); + + // TODO: sync state update + + self.next_command_index += 1; + self + } +} + +/// Error that can happen when recording a synchronization command. +#[derive(Clone, Debug)] +pub enum SynchronizationError { + RequirementNotMet { + required_for: &'static str, + requires_one_of: RequiresOneOf, + }, + + /// One or more accesses of a buffer memory barrier are not supported by the corresponding + /// pipeline stages. + BufferMemoryBarrierAccessNotSupportedByStages { barrier_index: usize }, + + /// Buffer memory barriers are forbidden inside a render pass instance. + BufferMemoryBarrierForbiddenInsideRenderPass, + + /// The end of `range` of a buffer memory barrier is greater than the size of `buffer`. + BufferMemoryBarrierOutOfRange { + barrier_index: usize, + range_end: DeviceSize, + buffer_size: DeviceSize, + }, + + /// A buffer memory barrier contains a queue family ownership transfer, but either the + /// `src_stages` or `dst_stages` contain [`HOST`]. + /// + /// [`HOST`]: crate::sync::PipelineStages::HOST + BufferMemoryBarrierOwnershipTransferHostNotAllowed { barrier_index: usize }, + + /// The provided `src_index` or `dst_index` in the queue family ownership transfer of a + /// buffer memory barrier is not less than the number of queue families in the physical device. + BufferMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index: usize, + provided_queue_family_index: u32, + queue_family_count: u32, + }, + + /// The provided `queue_family_ownership_transfer` value of a buffer memory barrier does not + /// match the sharing mode of `buffer`. + BufferMemoryBarrierOwnershipTransferSharingMismatch { barrier_index: usize }, + + /// One or more pipeline stages of a buffer memory barrier are not supported by the queue + /// family of the command buffer. + BufferMemoryBarrierStageNotSupported { barrier_index: usize }, + + /// A render pass instance is not active, and the `VIEW_LOCAL` dependency flag was provided. + DependencyFlagsViewLocalNotAllowed, + + /// Operation forbidden inside a render pass. + ForbiddenInsideRenderPass, + + /// Operation forbidden inside a render pass instance that was begun with `begin_rendering`. + ForbiddenWithBeginRendering, + + /// One or more accesses of an image memory barrier are not supported by the corresponding + /// pipeline stages. + ImageMemoryBarrierAccessNotSupportedByStages { barrier_index: usize }, + + /// The end of the range of array layers of the subresource range of an image memory barrier + /// is greater than the number of array layers in the image. + ImageMemoryBarrierArrayLayersOutOfRange { + barrier_index: usize, + array_layers_range_end: u32, + image_array_layers: u32, + }, + + /// The aspects of the subresource range of an image memory barrier contain aspects that are + /// not present in the image, or that are not allowed. + ImageMemoryBarrierAspectsNotAllowed { + barrier_index: usize, + aspects: ImageAspects, + }, + + /// For the `old_layout` or `new_layout` of an image memory barrier, `image` does not have a + /// usage that is required. + ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index: usize, + layout: ImageLayout, + requires_one_of_usage: ImageUsage, + }, + + /// An image memory barrier contains an image layout transition, but a render pass + /// instance is active. + ImageMemoryBarrierLayoutTransitionForbiddenInsideRenderPass { barrier_index: usize }, + + /// The end of the range of mip levels of the subresource range of an image memory barrier + /// is greater than the number of mip levels in the image. + ImageMemoryBarrierMipLevelsOutOfRange { + barrier_index: usize, + mip_levels_range_end: u32, + image_mip_levels: u32, + }, + + /// The `new_layout` of an image memory barrier is `Undefined` or `Preinitialized`. + ImageMemoryBarrierNewLayoutInvalid { barrier_index: usize }, + + /// A render pass instance is active, and the image of an image memory barrier is not a color + /// or depth/stencil attachment of the current subpass. + ImageMemoryBarrierNotColorDepthStencilAttachment { barrier_index: usize }, + + /// A render pass instance is active, and the image of an image memory barrier is not an input + /// attachment of the current subpass. + ImageMemoryBarrierNotInputAttachment { barrier_index: usize }, + + /// The `src_stages` of an image memory barrier contains [`HOST`], but `old_layout` is not + /// `Preinitialized`, `Undefined` or `General`. + /// + /// [`HOST`]: crate::sync::PipelineStages::HOST + ImageMemoryBarrierOldLayoutFromHostInvalid { + barrier_index: usize, + old_layout: ImageLayout, + }, + + /// An image memory barrier contains a queue family ownership transfer, but a render pass + /// instance is active. + ImageMemoryBarrierOwnershipTransferForbiddenInsideRenderPass { barrier_index: usize }, + + /// An image memory barrier contains a queue family ownership transfer, but either the + /// `src_stages` or `dst_stages` contain [`HOST`]. + /// + /// [`HOST`]: crate::sync::PipelineStages::HOST + ImageMemoryBarrierOwnershipTransferHostForbidden { barrier_index: usize }, + + /// The provided `src_index` or `dst_index` in the queue family ownership transfer of an + /// image memory barrier is not less than the number of queue families in the physical device. + ImageMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index: usize, + provided_queue_family_index: u32, + queue_family_count: u32, + }, + + /// The provided `queue_family_ownership_transfer` value of an image memory barrier does not + /// match the sharing mode of `image`. + ImageMemoryBarrierOwnershipTransferSharingMismatch { barrier_index: usize }, + + /// One or more pipeline stages of an image memory barrier are not supported by the queue + /// family of the command buffer. + ImageMemoryBarrierStageNotSupported { barrier_index: usize }, + + /// One or more accesses of a memory barrier are not supported by the corresponding + /// pipeline stages. + MemoryBarrierAccessNotSupportedByStages { barrier_index: usize }, + + /// A render pass instance is active, but the render pass does not have a subpass + /// self-dependency for the current subpass that is a superset of the barriers. + MemoryBarrierNoMatchingSubpassSelfDependency, + + /// One or more pipeline stages of a memory barrier are not supported by the queue + /// family of the command buffer. + MemoryBarrierStageNotSupported { barrier_index: usize }, + + /// The queue family doesn't allow this operation. + NotSupportedByQueueFamily, +} + +impl Error for SynchronizationError {} + +impl Display for SynchronizationError { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { + match self { + Self::RequirementNotMet { + required_for, + requires_one_of, + } => write!( + f, + "a requirement was not met for: {}; requires one of: {}", + required_for, requires_one_of, + ), + + Self::BufferMemoryBarrierAccessNotSupportedByStages { barrier_index } => write!( + f, + "one or more accesses of buffer memory barrier {} are not supported by the \ + corresponding pipeline stages", + barrier_index, + ), + Self::BufferMemoryBarrierForbiddenInsideRenderPass => write!( + f, + "buffer memory barriers are forbidden inside a render pass instance", + ), + Self::BufferMemoryBarrierOutOfRange { + barrier_index, + range_end, + buffer_size, + } => write!( + f, + "the end of `range` ({}) of buffer memory barrier {} is greater than the size of \ + `buffer` ({})", + range_end, barrier_index, buffer_size, + ), + Self::BufferMemoryBarrierOwnershipTransferHostNotAllowed { barrier_index } => write!( + f, + "buffer memory barrier {} contains a queue family ownership transfer, but either \ + the `src_stages` or `dst_stages` contain `HOST`", + barrier_index, + ), + Self::BufferMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + } => write!( + f, + "the provided `src_index` or `dst_index` ({}) in the queue family ownership \ + transfer of buffer memory barrier {} is not less than the number of queue \ + families in the physical device ({})", + provided_queue_family_index, barrier_index, queue_family_count, + ), + Self::BufferMemoryBarrierOwnershipTransferSharingMismatch { barrier_index } => write!( + f, + "the provided `queue_family_ownership_transfer` value of buffer memory barrier {} \ + does not match the sharing mode of `buffer`", + barrier_index, + ), + Self::BufferMemoryBarrierStageNotSupported { barrier_index } => write!( + f, + "one or more pipeline stages of buffer memory barrier {} are not supported by the \ + queue family of the command buffer", + barrier_index, + ), + Self::DependencyFlagsViewLocalNotAllowed => write!( + f, + "a render pass instance is not active, and the `VIEW_LOCAL` dependency flag was \ + provided", + ), + Self::ForbiddenInsideRenderPass => { + write!(f, "operation forbidden inside a render pass") + } + Self::ForbiddenWithBeginRendering => write!( + f, + "operation forbidden inside a render pass instance that was begun with \ + `begin_rendering`", + ), + Self::ImageMemoryBarrierAccessNotSupportedByStages { barrier_index } => write!( + f, + "one or more accesses of image memory barrier {} are not supported by the \ + corresponding pipeline stages", + barrier_index, + ), + Self::ImageMemoryBarrierArrayLayersOutOfRange { + barrier_index, + array_layers_range_end, + image_array_layers, + } => write!( + f, + "the end of the range of array layers ({}) of the subresource range of image \ + memory barrier {} is greater than the number of array layers in the image ({})", + array_layers_range_end, barrier_index, image_array_layers, + ), + Self::ImageMemoryBarrierAspectsNotAllowed { + barrier_index, + aspects, + } => write!( + f, + "the aspects of the subresource range of image memory barrier {} contain aspects \ + that are not present in the image, or that are not allowed ({:?})", + barrier_index, aspects, + ), + Self::ImageMemoryBarrierImageMissingUsageForLayout { + barrier_index, + layout, + requires_one_of_usage, + } => write!( + f, + "for the `old_layout` or `new_layout` ({:?}) of image memory barrier {}, `image` \ + does not have a usage that is required ({}{:?})", + layout, + barrier_index, + if requires_one_of_usage.count() > 1 { + "one of " + } else { + "" + }, + requires_one_of_usage, + ), + Self::ImageMemoryBarrierLayoutTransitionForbiddenInsideRenderPass { barrier_index } => { + write!( + f, + "image memory barrier {} contains an image layout transition, but a render \ + pass instance is active", + barrier_index, + ) + } + Self::ImageMemoryBarrierMipLevelsOutOfRange { + barrier_index, + mip_levels_range_end, + image_mip_levels, + } => write!( + f, + "the end of the range of mip levels ({}) of the subresource range of image \ + memory barrier {} is greater than the number of mip levels in the image ({})", + mip_levels_range_end, barrier_index, image_mip_levels, + ), + Self::ImageMemoryBarrierNewLayoutInvalid { barrier_index } => write!( + f, + "the `new_layout` of image memory barrier {} is `Undefined` or `Preinitialized`", + barrier_index, + ), + Self::ImageMemoryBarrierNotColorDepthStencilAttachment { barrier_index } => write!( + f, + "a render pass instance is active, and the image of image memory barrier {} is \ + not a color or depth/stencil attachment of the current subpass", + barrier_index, + ), + Self::ImageMemoryBarrierNotInputAttachment { barrier_index } => write!( + f, + "a render pass instance is active, and the image of image memory barrier {} is \ + not an input attachment of the current subpass", + barrier_index, + ), + Self::ImageMemoryBarrierOldLayoutFromHostInvalid { + barrier_index, + old_layout, + } => write!( + f, + "the `src_stages` of image memory barrier {} contains `HOST`, but `old_layout` + ({:?}) is not `Preinitialized`, `Undefined` or `General`", + barrier_index, old_layout, + ), + Self::ImageMemoryBarrierOwnershipTransferForbiddenInsideRenderPass { + barrier_index, + } => write!( + f, + "image memory barrier {} contains a queue family ownership transfer, but a render \ + pass instance is active", + barrier_index, + ), + Self::ImageMemoryBarrierOwnershipTransferHostForbidden { barrier_index } => write!( + f, + "image memory barrier {} contains a queue family ownership transfer, but either \ + the `src_stages` or `dst_stages` contain `HOST`", + barrier_index, + ), + Self::ImageMemoryBarrierOwnershipTransferIndexOutOfRange { + barrier_index, + provided_queue_family_index, + queue_family_count, + } => write!( + f, + "the provided `src_index` or `dst_index` ({}) in the queue family ownership \ + transfer of image memory barrier {} is not less than the number of queue + families in the physical device ({})", + provided_queue_family_index, barrier_index, queue_family_count, + ), + Self::ImageMemoryBarrierOwnershipTransferSharingMismatch { barrier_index } => write!( + f, + "the provided `queue_family_ownership_transfer` value of image memory barrier {} \ + does not match the sharing mode of `image`", + barrier_index, + ), + Self::ImageMemoryBarrierStageNotSupported { barrier_index } => write!( + f, + "one or more pipeline stages of image memory barrier {} are not supported by the \ + queue family of the command buffer", + barrier_index, + ), + Self::MemoryBarrierAccessNotSupportedByStages { barrier_index } => write!( + f, + "one or more accesses of memory barrier {} are not supported by the \ + corresponding pipeline stages", + barrier_index, + ), + Self::MemoryBarrierNoMatchingSubpassSelfDependency => write!( + f, + "a render pass instance is active, but the render pass does not have a subpass \ + self-dependency for the current subpass that is a superset of the barriers", + ), + Self::MemoryBarrierStageNotSupported { barrier_index } => write!( + f, + "one or more pipeline stages of memory barrier {} are not supported by the \ + queue family of the command buffer", + barrier_index, + ), + Self::NotSupportedByQueueFamily => { + write!(f, "the queue family doesn't allow this operation") + } + } + } +} + +impl From<RequirementNotMet> for SynchronizationError { + fn from(err: RequirementNotMet) -> Self { + Self::RequirementNotMet { + required_for: err.required_for, + requires_one_of: err.requires_one_of, + } + } +} |