diff options
author | Kaiyi Li <kaiyili@google.com> | 2023-08-03 22:20:57 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-08-03 22:20:57 +0000 |
commit | 308c941f21a4d9a4bf66f64c811b91237922c2e6 (patch) | |
tree | f204b748c5dd920d6176f3d0e10b820dafbb5784 /src/sync/pipeline.rs | |
parent | 45f20af9022b6c3786a61123a169bb355053ee5a (diff) | |
parent | 0f5fd0001fdc80b81802004262fdf62405329a3c (diff) | |
download | vulkano-308c941f21a4d9a4bf66f64c811b91237922c2e6.tar.gz |
Upgrade vulkano to 0.33.0 am: cfa3173d1d am: 6fb41c608d am: 570756a047 am: 0f5fd0001f
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/vulkano/+/2690275
Change-Id: Ib65dfbc52fe90e151ca7e4ec13ff1af8e498a582
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
Diffstat (limited to 'src/sync/pipeline.rs')
-rw-r--r-- | src/sync/pipeline.rs | 2840 |
1 files changed, 2650 insertions, 190 deletions
diff --git a/src/sync/pipeline.rs b/src/sync/pipeline.rs index 62cd95c..53e3941 100644 --- a/src/sync/pipeline.rs +++ b/src/sync/pipeline.rs @@ -7,80 +7,550 @@ // notice may not be copied, modified, or distributed except // according to those terms. -use std::ops; +use crate::{ + buffer::Buffer, + descriptor_set::layout::DescriptorType, + device::{Device, QueueFlags}, + image::{sys::Image, ImageAspects, ImageLayout, ImageSubresourceRange}, + macros::{vulkan_bitflags, vulkan_bitflags_enum}, + shader::ShaderStages, + DeviceSize, RequirementNotMet, Version, +}; +use ahash::HashMap; +use once_cell::sync::Lazy; +use smallvec::SmallVec; +use std::{ops::Range, sync::Arc}; -macro_rules! pipeline_stages { - ($($elem:ident, $var:ident => $val:expr, $queue:expr;)+) => ( - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub struct PipelineStages { - $( - pub $elem: bool, - )+ +vulkan_bitflags_enum! { + #[non_exhaustive] + /// A set of [`PipelineStage`] values. + PipelineStages impl { + /// Returns whether `self` contains stages that are only available in + /// `VkPipelineStageFlagBits2`. + pub(crate) fn is_2(self) -> bool { + !(self + - (PipelineStages::TOP_OF_PIPE + | PipelineStages::DRAW_INDIRECT + | PipelineStages::VERTEX_INPUT + | PipelineStages::VERTEX_SHADER + | PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER + | PipelineStages::GEOMETRY_SHADER + | PipelineStages::FRAGMENT_SHADER + | PipelineStages::EARLY_FRAGMENT_TESTS + | PipelineStages::LATE_FRAGMENT_TESTS + | PipelineStages::COLOR_ATTACHMENT_OUTPUT + | PipelineStages::COMPUTE_SHADER + | PipelineStages::ALL_TRANSFER + | PipelineStages::BOTTOM_OF_PIPE + | PipelineStages::HOST + | PipelineStages::ALL_GRAPHICS + | PipelineStages::ALL_COMMANDS + | PipelineStages::TRANSFORM_FEEDBACK + | PipelineStages::CONDITIONAL_RENDERING + | PipelineStages::ACCELERATION_STRUCTURE_BUILD + | PipelineStages::RAY_TRACING_SHADER + | PipelineStages::FRAGMENT_DENSITY_PROCESS + | PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT + | PipelineStages::COMMAND_PREPROCESS + | PipelineStages::TASK_SHADER + | PipelineStages::MESH_SHADER)) + .is_empty() } - impl PipelineStages { - /// Builds an `PipelineStages` struct with none of the stages set. - pub fn none() -> PipelineStages { - PipelineStages { - $( - $elem: false, - )+ - } + /// Replaces and unsets flags that are equivalent to multiple other flags. + /// + /// This may set flags that are not supported by the device, so this is for internal use only + /// and should not be passed on to Vulkan. + pub(crate) fn expand(mut self, queue_flags: QueueFlags) -> Self { + if self.intersects(PipelineStages::ALL_COMMANDS) { + self -= PipelineStages::ALL_COMMANDS; + self |= queue_flags.into(); } - } - impl From<PipelineStages> for ash::vk::PipelineStageFlags { - #[inline] - fn from(val: PipelineStages) -> Self { - let mut result = ash::vk::PipelineStageFlags::empty(); - $( - if val.$elem { result |= $val } - )+ - result + if self.intersects(PipelineStages::ALL_GRAPHICS) { + self -= PipelineStages::ALL_GRAPHICS; + self |= QueueFlags::GRAPHICS.into(); } - } - impl ops::BitOr for PipelineStages { - type Output = PipelineStages; + if self.intersects(PipelineStages::VERTEX_INPUT) { + self -= PipelineStages::VERTEX_INPUT; + self |= PipelineStages::INDEX_INPUT | PipelineStages::VERTEX_ATTRIBUTE_INPUT; + } - #[inline] - fn bitor(self, rhs: PipelineStages) -> PipelineStages { - PipelineStages { - $( - $elem: self.$elem || rhs.$elem, - )+ - } + if self.intersects(PipelineStages::PRE_RASTERIZATION_SHADERS) { + self -= PipelineStages::PRE_RASTERIZATION_SHADERS; + self |= PipelineStages::VERTEX_SHADER + | PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER + | PipelineStages::GEOMETRY_SHADER + | PipelineStages::TASK_SHADER + | PipelineStages::MESH_SHADER; } - } - impl ops::BitOrAssign for PipelineStages { - #[inline] - fn bitor_assign(&mut self, rhs: PipelineStages) { - $( - self.$elem = self.$elem || rhs.$elem; - )+ + if self.intersects(PipelineStages::ALL_TRANSFER) { + self -= PipelineStages::ALL_TRANSFER; + self |= PipelineStages::COPY + | PipelineStages::RESOLVE + | PipelineStages::BLIT + | PipelineStages::CLEAR + | PipelineStages::ACCELERATION_STRUCTURE_COPY; } + + self } - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - #[repr(u32)] - pub enum PipelineStage { + pub(crate) fn with_earlier(self) -> Self { + STAGE_ORDER.iter().rev().fold( + self, + |stages, &(before, after)| if stages.intersects(after) { + stages.union(before) + } else { + stages + } + ) + } + + pub(crate) fn with_later(self) -> Self { + STAGE_ORDER.iter().fold( + self, + |stages, &(before, after)| if stages.intersects(before) { + stages.union(after) + } else { + stages + } + ) + } + }, + + /// A single stage in the device's processing pipeline. + PipelineStage, + + = PipelineStageFlags2(u64); + + /// A pseudo-stage representing the start of the pipeline. + TOP_OF_PIPE, TopOfPipe = TOP_OF_PIPE, + + /// Indirect buffers are read. + DRAW_INDIRECT, DrawIndirect = DRAW_INDIRECT, + + /// Vertex and index buffers are read. + /// + /// It is currently equivalent to setting all of the following flags, but automatically + /// omitting any that are not supported in a given context. It also implicitly includes future + /// flags that are added to Vulkan, if they are not yet supported by Vulkano. + /// - `index_input` + /// - `vertex_attribute_input` + VERTEX_INPUT, VertexInput = VERTEX_INPUT, + + /// Vertex shaders are executed. + VERTEX_SHADER, VertexShader = VERTEX_SHADER, + + /// Tessellation control shaders are executed. + TESSELLATION_CONTROL_SHADER, TessellationControlShader = TESSELLATION_CONTROL_SHADER, + + /// Tessellation evaluation shaders are executed. + TESSELLATION_EVALUATION_SHADER, TessellationEvaluationShader = TESSELLATION_EVALUATION_SHADER, + + /// Geometry shaders are executed. + GEOMETRY_SHADER, GeometryShader = GEOMETRY_SHADER, + + /// Fragment shaders are executed. + FRAGMENT_SHADER, FragmentShader = FRAGMENT_SHADER, + + /// Early fragment tests (depth and stencil tests before fragment shading) are performed. + /// Subpass load operations for framebuffer attachments with a depth/stencil format are + /// performed. + EARLY_FRAGMENT_TESTS, EarlyFragmentTests = EARLY_FRAGMENT_TESTS, + + /// Late fragment tests (depth and stencil tests after fragment shading) are performed. + /// Subpass store operations for framebuffer attachments with a depth/stencil format are + /// performed. + LATE_FRAGMENT_TESTS, LateFragmentTests = LATE_FRAGMENT_TESTS, + + /// The final color values are output from the pipeline after blending. + /// Subpass load and store operations, multisample resolve operations for framebuffer + /// attachments with a color or depth/stencil format, and `clear_attachments` are performed. + COLOR_ATTACHMENT_OUTPUT, ColorAttachmentOutput = COLOR_ATTACHMENT_OUTPUT, + + /// Compute shaders are executed. + COMPUTE_SHADER, ComputeShader = COMPUTE_SHADER, + + /// The set of all current and future transfer pipeline stages. + /// + /// It is currently equivalent to setting all of the following flags, but automatically + /// omitting any that are not supported in a given context. It also implicitly includes future + /// flags that are added to Vulkan, if they are not yet supported by Vulkano. + /// - `copy` + /// - `blit` + /// - `resolve` + /// - `clear` + /// - `acceleration_structure_copy` + ALL_TRANSFER, AllTransfer = ALL_TRANSFER, + + /// A pseudo-stage representing the end of the pipeline. + BOTTOM_OF_PIPE, BottomOfPipe = BOTTOM_OF_PIPE, + + /// A pseudo-stage representing reads and writes to device memory on the host. + HOST, Host = HOST, + + /// The set of all current and future graphics pipeline stages. + /// + /// It is currently equivalent to setting all of the following flags, but automatically + /// omitting any that are not supported in a given context. It also implicitly includes future + /// flags that are added to Vulkan, if they are not yet supported by Vulkano. + /// - `draw_indirect` + /// - `task_shader` + /// - `mesh_shader` + /// - `vertex_input` + /// - `vertex_shader` + /// - `tessellation_control_shader` + /// - `tessellation_evaluation_shader` + /// - `geometry_shader` + /// - `fragment_shader` + /// - `early_fragment_tests` + /// - `late_fragment_tests` + /// - `color_attachment_output` + /// - `conditional_rendering` + /// - `transform_feedback` + /// - `fragment_shading_rate_attachment` + /// - `fragment_density_process` + /// - `invocation_mask` + ALL_GRAPHICS, AllGraphics = ALL_GRAPHICS, + + /// The set of all current and future pipeline stages of all types. + /// + /// It is currently equivalent to setting all flags in `PipelineStages`, but automatically + /// omitting any that are not supported in a given context. It also implicitly includes future + /// flags that are added to Vulkan, if they are not yet supported by Vulkano. + ALL_COMMANDS, AllCommands = ALL_COMMANDS, + + /// The `copy_buffer`, `copy_image`, `copy_buffer_to_image`, `copy_image_to_buffer` and + /// `copy_query_pool_results` commands are executed. + COPY, Copy = COPY { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// The `resolve_image` command is executed. + RESOLVE, Resolve = RESOLVE { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// The `blit_image` command is executed. + BLIT, Blit = BLIT { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// The `clear_color_image`, `clear_depth_stencil_image`, `fill_buffer` and `update_buffer` + /// commands are executed. + CLEAR, Clear = CLEAR { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// Index buffers are read. + INDEX_INPUT, IndexInput = INDEX_INPUT { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// Vertex buffers are read. + VERTEX_ATTRIBUTE_INPUT, VertexAttributeInput = VERTEX_ATTRIBUTE_INPUT { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// The various pre-rasterization shader types are executed. + /// + /// It is currently equivalent to setting all of the following flags, but automatically + /// omitting any that are not supported in a given context. It also implicitly includes future + /// flags that are added to Vulkan, if they are not yet supported by Vulkano. + /// - `vertex_shader` + /// - `tessellation_control_shader` + /// - `tessellation_evaluation_shader` + /// - `geometry_shader` + /// - `task_shader` + /// - `mesh_shader` + PRE_RASTERIZATION_SHADERS, PreRasterizationShaders = PRE_RASTERIZATION_SHADERS { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// Video decode operations are performed. + VIDEO_DECODE, VideoDecode = VIDEO_DECODE_KHR { + device_extensions: [khr_video_decode_queue], + }, + + /// Video encode operations are performed. + VIDEO_ENCODE, VideoEncode = VIDEO_ENCODE_KHR { + device_extensions: [khr_video_encode_queue], + }, + + /// Vertex attribute output values are written to the transform feedback buffers. + TRANSFORM_FEEDBACK, TransformFeedback = TRANSFORM_FEEDBACK_EXT { + device_extensions: [ext_transform_feedback], + }, + + /// The predicate of conditional rendering is read. + CONDITIONAL_RENDERING, ConditionalRendering = CONDITIONAL_RENDERING_EXT { + device_extensions: [ext_conditional_rendering], + }, + + /// Acceleration_structure commands are executed. + ACCELERATION_STRUCTURE_BUILD, AccelerationStructureBuild = ACCELERATION_STRUCTURE_BUILD_KHR { + device_extensions: [khr_acceleration_structure, nv_ray_tracing], + }, + + /// The various ray tracing shader types are executed. + RAY_TRACING_SHADER, RayTracingShader = RAY_TRACING_SHADER_KHR { + device_extensions: [khr_ray_tracing_pipeline, nv_ray_tracing], + }, + + /// The fragment density map is read to generate the fragment areas. + FRAGMENT_DENSITY_PROCESS, FragmentDensityProcess = FRAGMENT_DENSITY_PROCESS_EXT { + device_extensions: [ext_fragment_density_map], + }, + + /// The fragment shading rate attachment or shading rate image is read to determine the + /// fragment shading rate for portions of a rasterized primitive. + FRAGMENT_SHADING_RATE_ATTACHMENT, FragmentShadingRateAttachment = FRAGMENT_SHADING_RATE_ATTACHMENT_KHR { + device_extensions: [khr_fragment_shading_rate], + }, + + /// Device-side preprocessing for generated commands via the `preprocess_generated_commands` + /// command is handled. + COMMAND_PREPROCESS, CommandPreprocess = COMMAND_PREPROCESS_NV { + device_extensions: [nv_device_generated_commands], + }, + + /// Task shaders are executed. + TASK_SHADER, TaskShader = TASK_SHADER_EXT { + device_extensions: [ext_mesh_shader, nv_mesh_shader], + }, + + /// Mesh shaders are executed. + MESH_SHADER, MeshShader = MESH_SHADER_EXT { + device_extensions: [ext_mesh_shader, nv_mesh_shader], + }, + + /// Subpass shading shaders are executed. + SUBPASS_SHADING, SubpassShading = SUBPASS_SHADING_HUAWEI { + device_extensions: [huawei_subpass_shading], + }, + + /// The invocation mask image is read to optimize ray dispatch. + INVOCATION_MASK, InvocationMask = INVOCATION_MASK_HUAWEI { + device_extensions: [huawei_invocation_mask], + }, + + /// The `copy_acceleration_structure` command is executed. + ACCELERATION_STRUCTURE_COPY, AccelerationStructureCopy = ACCELERATION_STRUCTURE_COPY_KHR { + device_extensions: [khr_ray_tracing_maintenance1], + }, + + /// Micromap commands are executed. + MICROMAP_BUILD, MicromapBuild = MICROMAP_BUILD_EXT { + device_extensions: [ext_opacity_micromap], + }, + + /// Optical flow operations are performed. + OPTICAL_FLOW, OpticalFlow = OPTICAL_FLOW_NV { + device_extensions: [nv_optical_flow], + }, +} + +macro_rules! stage_order { + { + $(( + $($before:ident)|+, + $($after:ident)|+, + ),)+ + } => { + static STAGE_ORDER: [(PipelineStages, PipelineStages); 15] = [ $( - $var = $val.as_raw(), + ( + PipelineStages::empty() + $(.union(PipelineStages::$before))+ + , + PipelineStages::empty() + $(.union(PipelineStages::$after))+ + ), )+ + ]; + }; +} + +// Per +// https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-stages-types +stage_order! { + ( + TOP_OF_PIPE, + DRAW_INDIRECT + | COPY | RESOLVE | BLIT | CLEAR + | VIDEO_DECODE | VIDEO_ENCODE + | CONDITIONAL_RENDERING + | COMMAND_PREPROCESS + | ACCELERATION_STRUCTURE_BUILD + | SUBPASS_SHADING + | ACCELERATION_STRUCTURE_COPY + | MICROMAP_BUILD + | OPTICAL_FLOW, + ), + + ( + DRAW_INDIRECT, + COMPUTE_SHADER | INDEX_INPUT | RAY_TRACING_SHADER | TASK_SHADER, + ), + + ( + INDEX_INPUT, + VERTEX_ATTRIBUTE_INPUT, + ), + + ( + VERTEX_ATTRIBUTE_INPUT, + VERTEX_SHADER, + ), + + ( + VERTEX_SHADER, + TESSELLATION_CONTROL_SHADER, + ), + + ( + TESSELLATION_CONTROL_SHADER, + TESSELLATION_EVALUATION_SHADER, + ), + + ( + TESSELLATION_EVALUATION_SHADER, + GEOMETRY_SHADER, + ), + + ( + GEOMETRY_SHADER, + TRANSFORM_FEEDBACK, + ), + + ( + TASK_SHADER, + MESH_SHADER, + ), + + ( + TRANSFORM_FEEDBACK | MESH_SHADER, + FRAGMENT_SHADING_RATE_ATTACHMENT, + ), + + ( + FRAGMENT_DENSITY_PROCESS | FRAGMENT_SHADING_RATE_ATTACHMENT, + EARLY_FRAGMENT_TESTS, + ), + + ( + EARLY_FRAGMENT_TESTS, + FRAGMENT_SHADER, + ), + + ( + FRAGMENT_SHADER, + LATE_FRAGMENT_TESTS, + ), + + ( + LATE_FRAGMENT_TESTS, + COLOR_ATTACHMENT_OUTPUT, + ), + + ( + COLOR_ATTACHMENT_OUTPUT + | COMPUTE_SHADER + | COPY | RESOLVE | BLIT | CLEAR + | VIDEO_DECODE | VIDEO_ENCODE + | CONDITIONAL_RENDERING + | COMMAND_PREPROCESS + | ACCELERATION_STRUCTURE_BUILD | RAY_TRACING_SHADER + | SUBPASS_SHADING + | ACCELERATION_STRUCTURE_COPY + | MICROMAP_BUILD + | OPTICAL_FLOW, + BOTTOM_OF_PIPE, + ), +} + +impl From<QueueFlags> for PipelineStages { + /// Corresponds to the table "[Supported pipeline stage flags]" in the Vulkan specification. + /// + /// [Supported pipeline stage flags]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-pipeline-stages-supported + #[inline] + fn from(val: QueueFlags) -> Self { + let mut result = PipelineStages::TOP_OF_PIPE + | PipelineStages::BOTTOM_OF_PIPE + | PipelineStages::HOST + | PipelineStages::ALL_COMMANDS; + + if val.intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE | QueueFlags::TRANSFER) { + result |= PipelineStages::ALL_TRANSFER + | PipelineStages::COPY + | PipelineStages::RESOLVE + | PipelineStages::BLIT + | PipelineStages::CLEAR + | PipelineStages::ACCELERATION_STRUCTURE_COPY; } - impl PipelineStage { - #[inline] - pub fn required_queue_flags(&self) -> ash::vk::QueueFlags { - match self { - $( - Self::$var => $queue, - )+ - } - } + if val.intersects(QueueFlags::GRAPHICS) { + result |= PipelineStages::DRAW_INDIRECT + | PipelineStages::VERTEX_INPUT + | PipelineStages::VERTEX_SHADER + | PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER + | PipelineStages::GEOMETRY_SHADER + | PipelineStages::FRAGMENT_SHADER + | PipelineStages::EARLY_FRAGMENT_TESTS + | PipelineStages::LATE_FRAGMENT_TESTS + | PipelineStages::COLOR_ATTACHMENT_OUTPUT + | PipelineStages::ALL_GRAPHICS + | PipelineStages::INDEX_INPUT + | PipelineStages::VERTEX_ATTRIBUTE_INPUT + | PipelineStages::PRE_RASTERIZATION_SHADERS + | PipelineStages::CONDITIONAL_RENDERING + | PipelineStages::TRANSFORM_FEEDBACK + | PipelineStages::COMMAND_PREPROCESS + | PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT + | PipelineStages::TASK_SHADER + | PipelineStages::MESH_SHADER + | PipelineStages::FRAGMENT_DENSITY_PROCESS + | PipelineStages::SUBPASS_SHADING + | PipelineStages::INVOCATION_MASK; + } + + if val.intersects(QueueFlags::COMPUTE) { + result |= PipelineStages::DRAW_INDIRECT + | PipelineStages::COMPUTE_SHADER + | PipelineStages::CONDITIONAL_RENDERING + | PipelineStages::COMMAND_PREPROCESS + | PipelineStages::ACCELERATION_STRUCTURE_BUILD + | PipelineStages::RAY_TRACING_SHADER + | PipelineStages::MICROMAP_BUILD; + } + + if val.intersects(QueueFlags::VIDEO_DECODE) { + result |= PipelineStages::VIDEO_DECODE; + } + + if val.intersects(QueueFlags::VIDEO_ENCODE) { + result |= PipelineStages::VIDEO_ENCODE; } - ); + + if val.intersects(QueueFlags::OPTICAL_FLOW) { + result |= PipelineStages::OPTICAL_FLOW; + } + + result + } } impl From<PipelineStage> for ash::vk::PipelineStageFlags { @@ -90,177 +560,434 @@ impl From<PipelineStage> for ash::vk::PipelineStageFlags { } } -pipeline_stages! { - top_of_pipe, TopOfPipe => ash::vk::PipelineStageFlags::TOP_OF_PIPE, ash::vk::QueueFlags::empty(); - draw_indirect, DrawIndirect => ash::vk::PipelineStageFlags::DRAW_INDIRECT, ash::vk::QueueFlags::GRAPHICS | ash::vk::QueueFlags::COMPUTE; - vertex_input, VertexInput => ash::vk::PipelineStageFlags::VERTEX_INPUT, ash::vk::QueueFlags::GRAPHICS; - vertex_shader, VertexShader => ash::vk::PipelineStageFlags::VERTEX_SHADER, ash::vk::QueueFlags::GRAPHICS; - tessellation_control_shader, TessellationControlShader => ash::vk::PipelineStageFlags::TESSELLATION_CONTROL_SHADER, ash::vk::QueueFlags::GRAPHICS; - tessellation_evaluation_shader, TessellationEvaluationShader => ash::vk::PipelineStageFlags::TESSELLATION_EVALUATION_SHADER, ash::vk::QueueFlags::GRAPHICS; - geometry_shader, GeometryShader => ash::vk::PipelineStageFlags::GEOMETRY_SHADER, ash::vk::QueueFlags::GRAPHICS; - fragment_shader, FragmentShader => ash::vk::PipelineStageFlags::FRAGMENT_SHADER, ash::vk::QueueFlags::GRAPHICS; - early_fragment_tests, EarlyFragmentTests => ash::vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS, ash::vk::QueueFlags::GRAPHICS; - late_fragment_tests, LateFragmentTests => ash::vk::PipelineStageFlags::LATE_FRAGMENT_TESTS, ash::vk::QueueFlags::GRAPHICS; - color_attachment_output, ColorAttachmentOutput => ash::vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT, ash::vk::QueueFlags::GRAPHICS; - compute_shader, ComputeShader => ash::vk::PipelineStageFlags::COMPUTE_SHADER, ash::vk::QueueFlags::COMPUTE; - transfer, Transfer => ash::vk::PipelineStageFlags::TRANSFER, ash::vk::QueueFlags::GRAPHICS | ash::vk::QueueFlags::COMPUTE | ash::vk::QueueFlags::TRANSFER; - bottom_of_pipe, BottomOfPipe => ash::vk::PipelineStageFlags::BOTTOM_OF_PIPE, ash::vk::QueueFlags::empty(); - host, Host => ash::vk::PipelineStageFlags::HOST, ash::vk::QueueFlags::empty(); - all_graphics, AllGraphics => ash::vk::PipelineStageFlags::ALL_GRAPHICS, ash::vk::QueueFlags::GRAPHICS; - all_commands, AllCommands => ash::vk::PipelineStageFlags::ALL_COMMANDS, ash::vk::QueueFlags::empty(); -} - -macro_rules! access_flags { - ($($elem:ident => $val:expr,)+) => ( - #[derive(Debug, Copy, Clone)] - #[allow(missing_docs)] - pub struct AccessFlags { - $( - pub $elem: bool, - )+ +impl From<PipelineStages> for ash::vk::PipelineStageFlags { + #[inline] + fn from(val: PipelineStages) -> Self { + Self::from_raw(ash::vk::PipelineStageFlags2::from(val).as_raw() as u32) + } +} + +vulkan_bitflags! { + #[non_exhaustive] + + /// A set of memory access types that are included in a memory dependency. + AccessFlags impl { + /// Returns whether `self` contains stages that are only available in + /// `VkAccessFlagBits2`. + pub(crate) fn is_2(self) -> bool { + !(self + - (AccessFlags::INDIRECT_COMMAND_READ + | AccessFlags::INDEX_READ + | AccessFlags::VERTEX_ATTRIBUTE_READ + | AccessFlags::UNIFORM_READ + | AccessFlags::INPUT_ATTACHMENT_READ + | AccessFlags::SHADER_READ + | AccessFlags::SHADER_WRITE + | AccessFlags::COLOR_ATTACHMENT_READ + | AccessFlags::COLOR_ATTACHMENT_WRITE + | AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ + | AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE + | AccessFlags::TRANSFER_READ + | AccessFlags::TRANSFER_WRITE + | AccessFlags::HOST_READ + | AccessFlags::HOST_WRITE + | AccessFlags::MEMORY_READ + | AccessFlags::MEMORY_WRITE + | AccessFlags::SHADER_SAMPLED_READ + | AccessFlags::SHADER_STORAGE_READ + | AccessFlags::SHADER_STORAGE_WRITE + | AccessFlags::VIDEO_DECODE_READ + | AccessFlags::VIDEO_DECODE_WRITE + | AccessFlags::VIDEO_ENCODE_READ + | AccessFlags::VIDEO_ENCODE_WRITE + | AccessFlags::TRANSFORM_FEEDBACK_WRITE + | AccessFlags::TRANSFORM_FEEDBACK_COUNTER_READ + | AccessFlags::TRANSFORM_FEEDBACK_COUNTER_WRITE + | AccessFlags::CONDITIONAL_RENDERING_READ + | AccessFlags::COMMAND_PREPROCESS_READ + | AccessFlags::COMMAND_PREPROCESS_WRITE + | AccessFlags::FRAGMENT_SHADING_RATE_ATTACHMENT_READ + | AccessFlags::ACCELERATION_STRUCTURE_READ + | AccessFlags::ACCELERATION_STRUCTURE_WRITE + | AccessFlags::FRAGMENT_DENSITY_MAP_READ + | AccessFlags::COLOR_ATTACHMENT_READ_NONCOHERENT + | AccessFlags::INVOCATION_MASK_READ)) + .is_empty() } - impl AccessFlags { - /// Builds an `AccessFlags` struct with all bits set. - pub fn all() -> AccessFlags { - AccessFlags { - $( - $elem: true, - )+ - } + /// Replaces and unsets flags that are equivalent to multiple other flags. + /// + /// This may set flags that are not supported by the device, so this is for internal use + /// only and should not be passed on to Vulkan. + #[allow(dead_code)] // TODO: use this function + pub(crate) fn expand(mut self) -> Self { + if self.intersects(AccessFlags::SHADER_READ) { + self -= AccessFlags::SHADER_READ; + self |= AccessFlags::UNIFORM_READ + | AccessFlags::SHADER_SAMPLED_READ + | AccessFlags::SHADER_STORAGE_READ + | AccessFlags::SHADER_BINDING_TABLE_READ; } - /// Builds an `AccessFlags` struct with none of the bits set. - pub fn none() -> AccessFlags { - AccessFlags { - $( - $elem: false, - )+ - } + if self.intersects(AccessFlags::SHADER_WRITE) { + self -= AccessFlags::SHADER_WRITE; + self |= AccessFlags::SHADER_STORAGE_WRITE; } - } - impl From<AccessFlags> for ash::vk::AccessFlags { - #[inline] - fn from(val: AccessFlags) -> Self { - let mut result = ash::vk::AccessFlags::empty(); - $( - if val.$elem { result |= $val } - )+ - result - } + self } + } + = AccessFlags2(u64); - impl ops::BitOr for AccessFlags { - type Output = AccessFlags; + /// Read access to an indirect buffer. + INDIRECT_COMMAND_READ = INDIRECT_COMMAND_READ, - #[inline] - fn bitor(self, rhs: AccessFlags) -> AccessFlags { - AccessFlags { - $( - $elem: self.$elem || rhs.$elem, - )+ - } - } - } + /// Read access to an index buffer. + INDEX_READ = INDEX_READ, - impl ops::BitOrAssign for AccessFlags { - #[inline] - fn bitor_assign(&mut self, rhs: AccessFlags) { - $( - self.$elem = self.$elem || rhs.$elem; - )+ - } - } - ); -} + /// Read access to a vertex buffer. + VERTEX_ATTRIBUTE_READ = VERTEX_ATTRIBUTE_READ, + + /// Read access to a uniform buffer in a shader. + UNIFORM_READ = UNIFORM_READ, + + /// Read access to an input attachment in a fragment shader, within a render pass. + INPUT_ATTACHMENT_READ = INPUT_ATTACHMENT_READ, + + /// Read access to a buffer or image in a shader. + /// + /// It is currently equivalent to setting all of the following flags, but automatically + /// omitting any that are not supported in a given context. It also implicitly includes future + /// flags that are added to Vulkan, if they are not yet supported by Vulkano. + /// - `uniform_read` + /// - `shader_sampled_read` + /// - `shader_storage_read` + /// - `shader_binding_table_read` + SHADER_READ = SHADER_READ, + + /// Write access to a buffer or image in a shader. + /// + /// It is currently equivalent to `shader_storage_write`. It also implicitly includes future + /// flags that are added to Vulkan, if they are not yet supported by Vulkano. + SHADER_WRITE = SHADER_WRITE, + + /// Read access to a color attachment during blending, logic operations or + /// subpass load operations. + COLOR_ATTACHMENT_READ = COLOR_ATTACHMENT_READ, + + /// Write access to a color, resolve or depth/stencil resolve attachment during a render pass + /// or subpass store operations. + COLOR_ATTACHMENT_WRITE = COLOR_ATTACHMENT_WRITE, + + /// Read access to a depth/stencil attachment during depth/stencil operations or + /// subpass load operations. + DEPTH_STENCIL_ATTACHMENT_READ = DEPTH_STENCIL_ATTACHMENT_READ, + + /// Write access to a depth/stencil attachment during depth/stencil operations or + /// subpass store operations. + DEPTH_STENCIL_ATTACHMENT_WRITE = DEPTH_STENCIL_ATTACHMENT_WRITE, + + /// Read access to a buffer or image during a copy, blit or resolve command. + TRANSFER_READ = TRANSFER_READ, + + /// Write access to a buffer or image during a copy, blit, resolve or clear command. + TRANSFER_WRITE = TRANSFER_WRITE, + + /// Read access performed by the host. + HOST_READ = HOST_READ, + + /// Write access performed by the host. + HOST_WRITE = HOST_WRITE, + + /// Any type of read access. + /// + /// This is equivalent to setting all `_read` flags that are allowed in the given context. + MEMORY_READ = MEMORY_READ, + + /// Any type of write access. + /// + /// This is equivalent to setting all `_write` flags that are allowed in the given context. + MEMORY_WRITE = MEMORY_WRITE, + + /// Read access to a uniform texel buffer or sampled image in a shader. + SHADER_SAMPLED_READ = SHADER_SAMPLED_READ { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// Read access to a storage buffer, storage texel buffer or storage image in a shader. + SHADER_STORAGE_READ = SHADER_STORAGE_READ { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// Write access to a storage buffer, storage texel buffer or storage image in a shader. + SHADER_STORAGE_WRITE = SHADER_STORAGE_WRITE { + api_version: V1_3, + device_extensions: [khr_synchronization2], + }, + + /// Read access to an image or buffer as part of a video decode operation. + VIDEO_DECODE_READ = VIDEO_DECODE_READ_KHR { + device_extensions: [khr_video_decode_queue], + }, + + /// Write access to an image or buffer as part of a video decode operation. + VIDEO_DECODE_WRITE = VIDEO_DECODE_WRITE_KHR { + device_extensions: [khr_video_decode_queue], + }, + + /// Read access to an image or buffer as part of a video encode operation. + VIDEO_ENCODE_READ = VIDEO_ENCODE_READ_KHR { + device_extensions: [khr_video_encode_queue], + }, + + /// Write access to an image or buffer as part of a video encode operation. + VIDEO_ENCODE_WRITE = VIDEO_ENCODE_WRITE_KHR { + device_extensions: [khr_video_encode_queue], + }, + + /// Write access to a transform feedback buffer during transform feedback operations. + TRANSFORM_FEEDBACK_WRITE = TRANSFORM_FEEDBACK_WRITE_EXT { + device_extensions: [ext_transform_feedback], + }, + + /// Read access to a transform feedback counter buffer during transform feedback operations. + TRANSFORM_FEEDBACK_COUNTER_READ = TRANSFORM_FEEDBACK_COUNTER_READ_EXT { + device_extensions: [ext_transform_feedback], + }, + + /// Write access to a transform feedback counter buffer during transform feedback operations. + TRANSFORM_FEEDBACK_COUNTER_WRITE = TRANSFORM_FEEDBACK_COUNTER_WRITE_EXT { + device_extensions: [ext_transform_feedback], + }, + + /// Read access to a predicate during conditional rendering. + CONDITIONAL_RENDERING_READ = CONDITIONAL_RENDERING_READ_EXT { + device_extensions: [ext_conditional_rendering], + }, + + /// Read access to preprocess buffers input to `preprocess_generated_commands`. + COMMAND_PREPROCESS_READ = COMMAND_PREPROCESS_READ_NV { + device_extensions: [nv_device_generated_commands], + }, + + /// Read access to sequences buffers output by `preprocess_generated_commands`. + COMMAND_PREPROCESS_WRITE = COMMAND_PREPROCESS_WRITE_NV { + device_extensions: [nv_device_generated_commands], + }, + + /// Read access to a fragment shading rate attachment during rasterization. + FRAGMENT_SHADING_RATE_ATTACHMENT_READ = FRAGMENT_SHADING_RATE_ATTACHMENT_READ_KHR { + device_extensions: [khr_fragment_shading_rate], + }, -access_flags! { - indirect_command_read => ash::vk::AccessFlags::INDIRECT_COMMAND_READ, - index_read => ash::vk::AccessFlags::INDEX_READ, - vertex_attribute_read => ash::vk::AccessFlags::VERTEX_ATTRIBUTE_READ, - uniform_read => ash::vk::AccessFlags::UNIFORM_READ, - input_attachment_read => ash::vk::AccessFlags::INPUT_ATTACHMENT_READ, - shader_read => ash::vk::AccessFlags::SHADER_READ, - shader_write => ash::vk::AccessFlags::SHADER_WRITE, - color_attachment_read => ash::vk::AccessFlags::COLOR_ATTACHMENT_READ, - color_attachment_write => ash::vk::AccessFlags::COLOR_ATTACHMENT_WRITE, - depth_stencil_attachment_read => ash::vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ, - depth_stencil_attachment_write => ash::vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE, - transfer_read => ash::vk::AccessFlags::TRANSFER_READ, - transfer_write => ash::vk::AccessFlags::TRANSFER_WRITE, - host_read => ash::vk::AccessFlags::HOST_READ, - host_write => ash::vk::AccessFlags::HOST_WRITE, - memory_read => ash::vk::AccessFlags::MEMORY_READ, - memory_write => ash::vk::AccessFlags::MEMORY_WRITE, + /// Read access to an acceleration structure or acceleration structure scratch buffer during + /// trace, build or copy commands. + ACCELERATION_STRUCTURE_READ = ACCELERATION_STRUCTURE_READ_KHR { + device_extensions: [khr_acceleration_structure, nv_ray_tracing], + }, + + /// Write access to an acceleration structure or acceleration structure scratch buffer during + /// trace, build or copy commands. + ACCELERATION_STRUCTURE_WRITE = ACCELERATION_STRUCTURE_WRITE_KHR { + device_extensions: [khr_acceleration_structure, nv_ray_tracing], + }, + + /// Read access to a fragment density map attachment during dynamic fragment density map + /// operations. + FRAGMENT_DENSITY_MAP_READ = FRAGMENT_DENSITY_MAP_READ_EXT { + device_extensions: [ext_fragment_density_map], + }, + + /// Read access to color attachments when performing advanced blend operations. + COLOR_ATTACHMENT_READ_NONCOHERENT = COLOR_ATTACHMENT_READ_NONCOHERENT_EXT { + device_extensions: [ext_blend_operation_advanced], + }, + + /// Read access to an invocation mask image. + INVOCATION_MASK_READ = INVOCATION_MASK_READ_HUAWEI { + device_extensions: [huawei_invocation_mask], + }, + + /// Read access to a shader binding table. + SHADER_BINDING_TABLE_READ = SHADER_BINDING_TABLE_READ_KHR { + device_extensions: [khr_ray_tracing_maintenance1], + }, + + /// Read access to a micromap object. + MICROMAP_READ = MICROMAP_READ_EXT { + device_extensions: [ext_opacity_micromap], + }, + + /// Write access to a micromap object. + MICROMAP_WRITE = MICROMAP_WRITE_EXT { + device_extensions: [ext_opacity_micromap], + }, + + /// Read access to a buffer or image during optical flow operations. + OPTICAL_FLOW_READ = OPTICAL_FLOW_READ_NV { + device_extensions: [nv_optical_flow], + }, + + /// Write access to a buffer or image during optical flow operations. + OPTICAL_FLOW_WRITE = OPTICAL_FLOW_WRITE_NV { + device_extensions: [nv_optical_flow], + }, } -impl AccessFlags { - /// Returns true if the access flags can be used with the given pipeline stages. +impl From<PipelineStages> for AccessFlags { + /// Corresponds to the table "[Supported access types]" in the Vulkan specification. /// - /// Corresponds to `Table 4. Supported access types` in section `6.1.3. Access Types` of the - /// Vulkan specs. - pub fn is_compatible_with(&self, stages: &PipelineStages) -> bool { - if stages.all_commands { - return true; + /// [Supported access types]: https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap7.html#synchronization-access-types-supported + #[inline] + fn from(mut val: PipelineStages) -> Self { + if val.is_empty() { + return AccessFlags::empty(); } - if self.indirect_command_read && !stages.draw_indirect && !stages.all_graphics { - return false; + val = val.expand(QueueFlags::GRAPHICS | QueueFlags::COMPUTE | QueueFlags::TRANSFER); + let mut result = AccessFlags::MEMORY_READ | AccessFlags::MEMORY_WRITE; + + if val.intersects(PipelineStages::DRAW_INDIRECT) { + result |= + AccessFlags::INDIRECT_COMMAND_READ | AccessFlags::TRANSFORM_FEEDBACK_COUNTER_READ; } - if (self.index_read || self.vertex_attribute_read) - && !stages.vertex_input - && !stages.all_graphics - { - return false; + if val.intersects( + PipelineStages::VERTEX_SHADER + | PipelineStages::TESSELLATION_CONTROL_SHADER + | PipelineStages::TESSELLATION_EVALUATION_SHADER + | PipelineStages::GEOMETRY_SHADER + | PipelineStages::FRAGMENT_SHADER + | PipelineStages::COMPUTE_SHADER + | PipelineStages::RAY_TRACING_SHADER + | PipelineStages::TASK_SHADER + | PipelineStages::MESH_SHADER, + ) { + result |= AccessFlags::SHADER_READ + | AccessFlags::UNIFORM_READ + | AccessFlags::SHADER_SAMPLED_READ + | AccessFlags::SHADER_STORAGE_READ + | AccessFlags::SHADER_WRITE + | AccessFlags::SHADER_STORAGE_WRITE + | AccessFlags::ACCELERATION_STRUCTURE_READ; } - if (self.uniform_read || self.shader_read || self.shader_write) - && !stages.vertex_shader - && !stages.tessellation_control_shader - && !stages.tessellation_evaluation_shader - && !stages.geometry_shader - && !stages.fragment_shader - && !stages.compute_shader - && !stages.all_graphics + if val.intersects(PipelineStages::FRAGMENT_SHADER | PipelineStages::SUBPASS_SHADING) { + result |= AccessFlags::INPUT_ATTACHMENT_READ; + } + + if val + .intersects(PipelineStages::EARLY_FRAGMENT_TESTS | PipelineStages::LATE_FRAGMENT_TESTS) { - return false; + result |= AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ + | AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE; } - if self.input_attachment_read && !stages.fragment_shader && !stages.all_graphics { - return false; + if val.intersects(PipelineStages::COLOR_ATTACHMENT_OUTPUT) { + result |= AccessFlags::COLOR_ATTACHMENT_READ + | AccessFlags::COLOR_ATTACHMENT_WRITE + | AccessFlags::COLOR_ATTACHMENT_READ_NONCOHERENT; } - if (self.color_attachment_read || self.color_attachment_write) - && !stages.color_attachment_output - && !stages.all_graphics - { - return false; + if val.intersects(PipelineStages::HOST) { + result |= AccessFlags::HOST_READ | AccessFlags::HOST_WRITE; } - if (self.depth_stencil_attachment_read || self.depth_stencil_attachment_write) - && !stages.early_fragment_tests - && !stages.late_fragment_tests - && !stages.all_graphics - { - return false; + if val.intersects( + PipelineStages::COPY + | PipelineStages::RESOLVE + | PipelineStages::BLIT + | PipelineStages::ACCELERATION_STRUCTURE_COPY, + ) { + result |= AccessFlags::TRANSFER_READ | AccessFlags::TRANSFER_WRITE; + } + + if val.intersects(PipelineStages::CLEAR) { + result |= AccessFlags::TRANSFER_WRITE; } - if (self.transfer_read || self.transfer_write) && !stages.transfer { - return false; + if val.intersects(PipelineStages::INDEX_INPUT) { + result |= AccessFlags::INDEX_READ; } - if (self.host_read || self.host_write) && !stages.host { - return false; + if val.intersects(PipelineStages::VERTEX_ATTRIBUTE_INPUT) { + result |= AccessFlags::VERTEX_ATTRIBUTE_READ; } - true + if val.intersects(PipelineStages::VIDEO_DECODE) { + result |= AccessFlags::VIDEO_DECODE_READ | AccessFlags::VIDEO_DECODE_WRITE; + } + + if val.intersects(PipelineStages::VIDEO_ENCODE) { + result |= AccessFlags::VIDEO_ENCODE_READ | AccessFlags::VIDEO_ENCODE_WRITE; + } + + if val.intersects(PipelineStages::TRANSFORM_FEEDBACK) { + result |= AccessFlags::TRANSFORM_FEEDBACK_WRITE + | AccessFlags::TRANSFORM_FEEDBACK_COUNTER_WRITE + | AccessFlags::TRANSFORM_FEEDBACK_COUNTER_READ; + } + + if val.intersects(PipelineStages::CONDITIONAL_RENDERING) { + result |= AccessFlags::CONDITIONAL_RENDERING_READ; + } + + if val.intersects(PipelineStages::ACCELERATION_STRUCTURE_BUILD) { + result |= AccessFlags::INDIRECT_COMMAND_READ + | AccessFlags::SHADER_READ + | AccessFlags::SHADER_SAMPLED_READ + | AccessFlags::SHADER_STORAGE_READ + | AccessFlags::SHADER_STORAGE_WRITE + | AccessFlags::TRANSFER_READ + | AccessFlags::TRANSFER_WRITE + | AccessFlags::ACCELERATION_STRUCTURE_READ + | AccessFlags::ACCELERATION_STRUCTURE_WRITE + | AccessFlags::MICROMAP_READ; + } + + if val.intersects(PipelineStages::RAY_TRACING_SHADER) { + result |= AccessFlags::SHADER_BINDING_TABLE_READ; + } + + if val.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) { + result |= AccessFlags::FRAGMENT_DENSITY_MAP_READ; + } + + if val.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) { + result |= AccessFlags::FRAGMENT_SHADING_RATE_ATTACHMENT_READ; + } + + if val.intersects(PipelineStages::COMMAND_PREPROCESS) { + result |= AccessFlags::COMMAND_PREPROCESS_READ | AccessFlags::COMMAND_PREPROCESS_WRITE; + } + + if val.intersects(PipelineStages::INVOCATION_MASK) { + result |= AccessFlags::INVOCATION_MASK_READ; + } + + if val.intersects(PipelineStages::MICROMAP_BUILD) { + result |= AccessFlags::MICROMAP_READ | AccessFlags::MICROMAP_WRITE; + } + + if val.intersects(PipelineStages::OPTICAL_FLOW) { + result |= AccessFlags::OPTICAL_FLOW_READ | AccessFlags::OPTICAL_FLOW_WRITE; + } + + result + } +} + +impl From<AccessFlags> for ash::vk::AccessFlags { + #[inline] + fn from(val: AccessFlags) -> Self { + Self::from_raw(ash::vk::AccessFlags2::from(val).as_raw() as u32) } } /// The full specification of memory access by the pipeline for a particular resource. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct PipelineMemoryAccess { /// The pipeline stages the resource will be accessed in. pub stages: PipelineStages, @@ -269,3 +996,1736 @@ pub struct PipelineMemoryAccess { /// Whether the resource needs exclusive (mutable) access or can be shared. pub exclusive: bool, } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[allow(non_camel_case_types, dead_code)] +#[repr(u8)] +pub(crate) enum PipelineStageAccess { + // There is no stage/access for this, but it is a memory write operation nonetheless. + ImageLayoutTransition, + + DrawIndirect_IndirectCommandRead, + DrawIndirect_TransformFeedbackCounterRead, + VertexShader_UniformRead, + VertexShader_ShaderSampledRead, + VertexShader_ShaderStorageRead, + VertexShader_ShaderStorageWrite, + VertexShader_AccelerationStructureRead, + TessellationControlShader_UniformRead, + TessellationControlShader_ShaderSampledRead, + TessellationControlShader_ShaderStorageRead, + TessellationControlShader_ShaderStorageWrite, + TessellationControlShader_AccelerationStructureRead, + TessellationEvaluationShader_UniformRead, + TessellationEvaluationShader_ShaderSampledRead, + TessellationEvaluationShader_ShaderStorageRead, + TessellationEvaluationShader_ShaderStorageWrite, + TessellationEvaluationShader_AccelerationStructureRead, + GeometryShader_UniformRead, + GeometryShader_ShaderSampledRead, + GeometryShader_ShaderStorageRead, + GeometryShader_ShaderStorageWrite, + GeometryShader_AccelerationStructureRead, + FragmentShader_UniformRead, + FragmentShader_InputAttachmentRead, + FragmentShader_ShaderSampledRead, + FragmentShader_ShaderStorageRead, + FragmentShader_ShaderStorageWrite, + FragmentShader_AccelerationStructureRead, + EarlyFragmentTests_DepthStencilAttachmentRead, + EarlyFragmentTests_DepthStencilAttachmentWrite, + LateFragmentTests_DepthStencilAttachmentRead, + LateFragmentTests_DepthStencilAttachmentWrite, + ColorAttachmentOutput_ColorAttachmentRead, + ColorAttachmentOutput_ColorAttachmentWrite, + ColorAttachmentOutput_ColorAttachmentReadNoncoherent, + ComputeShader_UniformRead, + ComputeShader_ShaderSampledRead, + ComputeShader_ShaderStorageRead, + ComputeShader_ShaderStorageWrite, + ComputeShader_AccelerationStructureRead, + Host_HostRead, + Host_HostWrite, + Copy_TransferRead, + Copy_TransferWrite, + Resolve_TransferRead, + Resolve_TransferWrite, + Blit_TransferRead, + Blit_TransferWrite, + Clear_TransferWrite, + IndexInput_IndexRead, + VertexAttributeInput_VertexAttributeRead, + VideoDecode_VideoDecodeRead, + VideoDecode_VideoDecodeWrite, + VideoEncode_VideoEncodeRead, + VideoEncode_VideoEncodeWrite, + TransformFeedback_TransformFeedbackWrite, + TransformFeedback_TransformFeedbackCounterRead, + TransformFeedback_TransformFeedbackCounterWrite, + ConditionalRendering_ConditionalRenderingRead, + AccelerationStructureBuild_IndirectCommandRead, + AccelerationStructureBuild_UniformRead, + AccelerationStructureBuild_TransferRead, + AccelerationStructureBuild_TransferWrite, + AccelerationStructureBuild_ShaderSampledRead, + AccelerationStructureBuild_ShaderStorageRead, + AccelerationStructureBuild_AccelerationStructureRead, + AccelerationStructureBuild_AccelerationStructureWrite, + AccelerationStructureBuild_MicromapRead, + RayTracingShader_UniformRead, + RayTracingShader_ShaderSampledRead, + RayTracingShader_ShaderStorageRead, + RayTracingShader_ShaderStorageWrite, + RayTracingShader_AccelerationStructureRead, + RayTracingShader_ShaderBindingTableRead, + FragmentDensityProcess_FragmentDensityMapRead, + FragmentShadingRateAttachment_FragmentShadingRateAttachmentRead, + CommandPreprocess_CommandPreprocessRead, + CommandPreprocess_CommandPreprocessWrite, + TaskShader_UniformRead, + TaskShader_ShaderSampledRead, + TaskShader_ShaderStorageRead, + TaskShader_ShaderStorageWrite, + TaskShader_AccelerationStructureRead, + MeshShader_UniformRead, + MeshShader_ShaderSampledRead, + MeshShader_ShaderStorageRead, + MeshShader_ShaderStorageWrite, + MeshShader_AccelerationStructureRead, + SubpassShading_InputAttachmentRead, + InvocationMask_InvocationMaskRead, + AccelerationStructureCopy_TransferRead, + AccelerationStructureCopy_TransferWrite, + OpticalFlow_OpticalFlowRead, + OpticalFlow_OpticalFlowWrite, + MicromapBuild_MicromapRead, + MicromapBuild_MicromapWrite, + + // If there are ever more than 128 preceding values, then there will be a compile error: + // "discriminant value `128` assigned more than once" + __MAX_VALUE__ = 128, +} + +impl PipelineStageAccess { + #[inline] + pub(crate) const fn is_write(self) -> bool { + matches!( + self, + PipelineStageAccess::ImageLayoutTransition + | PipelineStageAccess::VertexShader_ShaderStorageWrite + | PipelineStageAccess::TessellationControlShader_ShaderStorageWrite + | PipelineStageAccess::TessellationEvaluationShader_ShaderStorageWrite + | PipelineStageAccess::GeometryShader_ShaderStorageWrite + | PipelineStageAccess::FragmentShader_ShaderStorageWrite + | PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite + | PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite + | PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentWrite + | PipelineStageAccess::ComputeShader_ShaderStorageWrite + | PipelineStageAccess::Host_HostWrite + | PipelineStageAccess::Copy_TransferWrite + | PipelineStageAccess::Resolve_TransferWrite + | PipelineStageAccess::Blit_TransferWrite + | PipelineStageAccess::Clear_TransferWrite + | PipelineStageAccess::VideoDecode_VideoDecodeWrite + | PipelineStageAccess::VideoEncode_VideoEncodeWrite + | PipelineStageAccess::TransformFeedback_TransformFeedbackWrite + | PipelineStageAccess::TransformFeedback_TransformFeedbackCounterWrite + | PipelineStageAccess::AccelerationStructureBuild_TransferWrite + | PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureWrite + | PipelineStageAccess::RayTracingShader_ShaderStorageWrite + | PipelineStageAccess::CommandPreprocess_CommandPreprocessWrite + | PipelineStageAccess::TaskShader_ShaderStorageWrite + | PipelineStageAccess::MeshShader_ShaderStorageWrite + | PipelineStageAccess::AccelerationStructureCopy_TransferWrite + | PipelineStageAccess::OpticalFlow_OpticalFlowWrite + | PipelineStageAccess::MicromapBuild_MicromapWrite + ) + } + + pub(crate) fn iter_descriptor_stages( + descriptor_type: DescriptorType, + stages_read: ShaderStages, + stages_write: ShaderStages, + ) -> impl Iterator<Item = Self> + 'static { + static MAP_READ: Lazy< + HashMap<DescriptorType, HashMap<PipelineStage, PipelineStageAccess>>, + > = Lazy::new(|| { + let uniform_read = [ + DescriptorType::UniformBuffer, + DescriptorType::UniformBufferDynamic, + ] + .into_iter() + .map(|descriptor_type| { + ( + descriptor_type, + [ + ( + PipelineStage::VertexShader, + PipelineStageAccess::VertexShader_UniformRead, + ), + ( + PipelineStage::TessellationControlShader, + PipelineStageAccess::TessellationControlShader_UniformRead, + ), + ( + PipelineStage::TessellationEvaluationShader, + PipelineStageAccess::TessellationControlShader_UniformRead, + ), + ( + PipelineStage::GeometryShader, + PipelineStageAccess::GeometryShader_UniformRead, + ), + ( + PipelineStage::FragmentShader, + PipelineStageAccess::FragmentShader_UniformRead, + ), + ( + PipelineStage::ComputeShader, + PipelineStageAccess::ComputeShader_UniformRead, + ), + ( + PipelineStage::RayTracingShader, + PipelineStageAccess::RayTracingShader_UniformRead, + ), + ( + PipelineStage::TaskShader, + PipelineStageAccess::TaskShader_UniformRead, + ), + ( + PipelineStage::MeshShader, + PipelineStageAccess::MeshShader_UniformRead, + ), + ] + .into_iter() + .collect(), + ) + }); + + let shader_sampled_read = [ + DescriptorType::CombinedImageSampler, + DescriptorType::SampledImage, + DescriptorType::UniformTexelBuffer, + ] + .into_iter() + .map(|descriptor_type| { + ( + descriptor_type, + [ + ( + PipelineStage::VertexShader, + PipelineStageAccess::VertexShader_ShaderSampledRead, + ), + ( + PipelineStage::TessellationControlShader, + PipelineStageAccess::TessellationControlShader_ShaderSampledRead, + ), + ( + PipelineStage::TessellationEvaluationShader, + PipelineStageAccess::TessellationControlShader_ShaderSampledRead, + ), + ( + PipelineStage::GeometryShader, + PipelineStageAccess::GeometryShader_ShaderSampledRead, + ), + ( + PipelineStage::FragmentShader, + PipelineStageAccess::FragmentShader_ShaderSampledRead, + ), + ( + PipelineStage::ComputeShader, + PipelineStageAccess::ComputeShader_ShaderSampledRead, + ), + ( + PipelineStage::RayTracingShader, + PipelineStageAccess::RayTracingShader_ShaderSampledRead, + ), + ( + PipelineStage::TaskShader, + PipelineStageAccess::TaskShader_ShaderSampledRead, + ), + ( + PipelineStage::MeshShader, + PipelineStageAccess::MeshShader_ShaderSampledRead, + ), + ] + .into_iter() + .collect(), + ) + }); + + let shader_storage_read = [ + DescriptorType::StorageImage, + DescriptorType::StorageTexelBuffer, + DescriptorType::StorageBuffer, + DescriptorType::StorageBufferDynamic, + ] + .into_iter() + .map(|descriptor_type| { + ( + descriptor_type, + [ + ( + PipelineStage::VertexShader, + PipelineStageAccess::VertexShader_ShaderStorageRead, + ), + ( + PipelineStage::TessellationControlShader, + PipelineStageAccess::TessellationControlShader_ShaderStorageRead, + ), + ( + PipelineStage::TessellationEvaluationShader, + PipelineStageAccess::TessellationControlShader_ShaderStorageRead, + ), + ( + PipelineStage::GeometryShader, + PipelineStageAccess::GeometryShader_ShaderStorageRead, + ), + ( + PipelineStage::FragmentShader, + PipelineStageAccess::FragmentShader_ShaderStorageRead, + ), + ( + PipelineStage::ComputeShader, + PipelineStageAccess::ComputeShader_ShaderStorageRead, + ), + ( + PipelineStage::RayTracingShader, + PipelineStageAccess::RayTracingShader_ShaderStorageRead, + ), + ( + PipelineStage::TaskShader, + PipelineStageAccess::TaskShader_ShaderStorageRead, + ), + ( + PipelineStage::MeshShader, + PipelineStageAccess::MeshShader_ShaderStorageRead, + ), + ] + .into_iter() + .collect(), + ) + }); + + let input_attachment_read = + [DescriptorType::InputAttachment] + .into_iter() + .map(|descriptor_type| { + ( + descriptor_type, + [( + PipelineStage::FragmentShader, + PipelineStageAccess::FragmentShader_InputAttachmentRead, + )] + .into_iter() + .collect(), + ) + }); + + uniform_read + .chain(shader_sampled_read) + .chain(shader_storage_read) + .chain(input_attachment_read) + .collect() + }); + static MAP_WRITE: Lazy< + HashMap<DescriptorType, HashMap<PipelineStage, PipelineStageAccess>>, + > = Lazy::new(|| { + let shader_storage_write = [ + DescriptorType::StorageImage, + DescriptorType::StorageTexelBuffer, + DescriptorType::StorageBuffer, + DescriptorType::StorageBufferDynamic, + ] + .into_iter() + .map(|descriptor_type| { + ( + descriptor_type, + [ + ( + PipelineStage::VertexShader, + PipelineStageAccess::VertexShader_ShaderStorageWrite, + ), + ( + PipelineStage::TessellationControlShader, + PipelineStageAccess::TessellationControlShader_ShaderStorageWrite, + ), + ( + PipelineStage::TessellationEvaluationShader, + PipelineStageAccess::TessellationControlShader_ShaderStorageWrite, + ), + ( + PipelineStage::GeometryShader, + PipelineStageAccess::GeometryShader_ShaderStorageWrite, + ), + ( + PipelineStage::FragmentShader, + PipelineStageAccess::FragmentShader_ShaderStorageWrite, + ), + ( + PipelineStage::ComputeShader, + PipelineStageAccess::ComputeShader_ShaderStorageWrite, + ), + ( + PipelineStage::RayTracingShader, + PipelineStageAccess::RayTracingShader_ShaderStorageWrite, + ), + ( + PipelineStage::TaskShader, + PipelineStageAccess::TaskShader_ShaderStorageWrite, + ), + ( + PipelineStage::MeshShader, + PipelineStageAccess::MeshShader_ShaderStorageWrite, + ), + ] + .into_iter() + .collect(), + ) + }); + + shader_storage_write.collect() + }); + + [ + (stages_read, &*MAP_READ, "read"), + (stages_write, &*MAP_WRITE, "write"), + ] + .into_iter() + .filter(|(stages, _, _)| !stages.is_empty()) + .flat_map(move |(stages, descriptor_map, access)| { + let stages_map = descriptor_map.get(&descriptor_type).unwrap_or_else(|| { + panic!( + "DescriptorType::{:?} does not {} memory", + descriptor_type, access, + ) + }); + + PipelineStages::from(stages).into_iter().map(move |stage| { + *stages_map.get(&stage).unwrap_or_else(|| { + panic!( + "DescriptorType::{:?} does not {} memory in PipelineStage::{:?}", + descriptor_type, access, stage, + ) + }) + }) + }) + } +} + +impl TryFrom<PipelineStageAccess> for PipelineStage { + type Error = (); + + #[inline] + fn try_from(val: PipelineStageAccess) -> Result<Self, Self::Error> { + Ok(match val { + PipelineStageAccess::ImageLayoutTransition => return Err(()), + PipelineStageAccess::DrawIndirect_IndirectCommandRead + | PipelineStageAccess::DrawIndirect_TransformFeedbackCounterRead => PipelineStage::DrawIndirect, + PipelineStageAccess::VertexShader_UniformRead + | PipelineStageAccess::VertexShader_ShaderSampledRead + | PipelineStageAccess::VertexShader_ShaderStorageRead + | PipelineStageAccess::VertexShader_ShaderStorageWrite + | PipelineStageAccess::VertexShader_AccelerationStructureRead => PipelineStage::VertexShader, + PipelineStageAccess::TessellationControlShader_UniformRead + | PipelineStageAccess::TessellationControlShader_ShaderSampledRead + | PipelineStageAccess::TessellationControlShader_ShaderStorageRead + | PipelineStageAccess::TessellationControlShader_ShaderStorageWrite + | PipelineStageAccess::TessellationControlShader_AccelerationStructureRead => PipelineStage::TessellationControlShader, + PipelineStageAccess::TessellationEvaluationShader_UniformRead + | PipelineStageAccess::TessellationEvaluationShader_ShaderSampledRead + | PipelineStageAccess::TessellationEvaluationShader_ShaderStorageRead + | PipelineStageAccess::TessellationEvaluationShader_ShaderStorageWrite + | PipelineStageAccess::TessellationEvaluationShader_AccelerationStructureRead => PipelineStage::TessellationEvaluationShader, + PipelineStageAccess::GeometryShader_UniformRead + | PipelineStageAccess::GeometryShader_ShaderSampledRead + | PipelineStageAccess::GeometryShader_ShaderStorageRead + | PipelineStageAccess::GeometryShader_ShaderStorageWrite + | PipelineStageAccess::GeometryShader_AccelerationStructureRead => PipelineStage::GeometryShader, + PipelineStageAccess::FragmentShader_UniformRead + | PipelineStageAccess::FragmentShader_InputAttachmentRead + | PipelineStageAccess::FragmentShader_ShaderSampledRead + | PipelineStageAccess::FragmentShader_ShaderStorageRead + | PipelineStageAccess::FragmentShader_ShaderStorageWrite + | PipelineStageAccess::FragmentShader_AccelerationStructureRead => PipelineStage::FragmentShader, + PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentRead + | PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite => PipelineStage::EarlyFragmentTests, + PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentRead + | PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite => PipelineStage::LateFragmentTests, + PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentRead + | PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentWrite + | PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentReadNoncoherent => PipelineStage::ColorAttachmentOutput, + PipelineStageAccess::ComputeShader_UniformRead + | PipelineStageAccess::ComputeShader_ShaderSampledRead + | PipelineStageAccess::ComputeShader_ShaderStorageRead + | PipelineStageAccess::ComputeShader_ShaderStorageWrite + | PipelineStageAccess::ComputeShader_AccelerationStructureRead => PipelineStage::ComputeShader, + PipelineStageAccess::Host_HostRead + | PipelineStageAccess::Host_HostWrite => PipelineStage::Host, + PipelineStageAccess::Copy_TransferRead + | PipelineStageAccess::Copy_TransferWrite => PipelineStage::Copy, + PipelineStageAccess::Resolve_TransferRead + | PipelineStageAccess::Resolve_TransferWrite => PipelineStage::Resolve, + PipelineStageAccess::Blit_TransferRead + | PipelineStageAccess::Blit_TransferWrite => PipelineStage::Blit, + PipelineStageAccess::Clear_TransferWrite => PipelineStage::Clear, + PipelineStageAccess::IndexInput_IndexRead => PipelineStage::IndexInput, + PipelineStageAccess::VertexAttributeInput_VertexAttributeRead => PipelineStage::VertexAttributeInput, + PipelineStageAccess::VideoDecode_VideoDecodeRead + | PipelineStageAccess::VideoDecode_VideoDecodeWrite => PipelineStage::VideoDecode, + PipelineStageAccess::VideoEncode_VideoEncodeRead + | PipelineStageAccess::VideoEncode_VideoEncodeWrite => PipelineStage::VideoEncode, + PipelineStageAccess::TransformFeedback_TransformFeedbackWrite + | PipelineStageAccess::TransformFeedback_TransformFeedbackCounterRead + | PipelineStageAccess::TransformFeedback_TransformFeedbackCounterWrite => PipelineStage::TransformFeedback, + PipelineStageAccess::ConditionalRendering_ConditionalRenderingRead => PipelineStage::ConditionalRendering, + PipelineStageAccess::AccelerationStructureBuild_IndirectCommandRead + | PipelineStageAccess::AccelerationStructureBuild_UniformRead + | PipelineStageAccess::AccelerationStructureBuild_TransferRead + | PipelineStageAccess::AccelerationStructureBuild_TransferWrite + | PipelineStageAccess::AccelerationStructureBuild_ShaderSampledRead + | PipelineStageAccess::AccelerationStructureBuild_ShaderStorageRead + | PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureRead + | PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureWrite + | PipelineStageAccess::AccelerationStructureBuild_MicromapRead => PipelineStage::AccelerationStructureBuild, + PipelineStageAccess::RayTracingShader_UniformRead + | PipelineStageAccess::RayTracingShader_ShaderSampledRead + | PipelineStageAccess::RayTracingShader_ShaderStorageRead + | PipelineStageAccess::RayTracingShader_ShaderStorageWrite + | PipelineStageAccess::RayTracingShader_AccelerationStructureRead => PipelineStage::RayTracingShader, + | PipelineStageAccess::RayTracingShader_ShaderBindingTableRead => PipelineStage::RayTracingShader, + PipelineStageAccess::FragmentDensityProcess_FragmentDensityMapRead => PipelineStage::FragmentDensityProcess, + PipelineStageAccess::FragmentShadingRateAttachment_FragmentShadingRateAttachmentRead => PipelineStage::FragmentShadingRateAttachment, + PipelineStageAccess::CommandPreprocess_CommandPreprocessRead + | PipelineStageAccess::CommandPreprocess_CommandPreprocessWrite => PipelineStage::CommandPreprocess, + PipelineStageAccess::TaskShader_UniformRead + | PipelineStageAccess::TaskShader_ShaderSampledRead + | PipelineStageAccess::TaskShader_ShaderStorageRead + | PipelineStageAccess::TaskShader_ShaderStorageWrite + | PipelineStageAccess::TaskShader_AccelerationStructureRead => PipelineStage::TaskShader, + PipelineStageAccess::MeshShader_UniformRead + | PipelineStageAccess::MeshShader_ShaderSampledRead + | PipelineStageAccess::MeshShader_ShaderStorageRead + | PipelineStageAccess::MeshShader_ShaderStorageWrite + | PipelineStageAccess::MeshShader_AccelerationStructureRead => PipelineStage::MeshShader, + PipelineStageAccess::SubpassShading_InputAttachmentRead => PipelineStage::SubpassShading, + PipelineStageAccess::InvocationMask_InvocationMaskRead => PipelineStage::InvocationMask, + PipelineStageAccess::AccelerationStructureCopy_TransferRead + | PipelineStageAccess::AccelerationStructureCopy_TransferWrite => PipelineStage::AccelerationStructureCopy, + PipelineStageAccess::OpticalFlow_OpticalFlowRead + | PipelineStageAccess::OpticalFlow_OpticalFlowWrite => PipelineStage::OpticalFlow, + PipelineStageAccess::MicromapBuild_MicromapRead + | PipelineStageAccess::MicromapBuild_MicromapWrite => PipelineStage::MicromapBuild, + PipelineStageAccess::__MAX_VALUE__ => unreachable!(), + }) + } +} + +impl From<PipelineStageAccess> for AccessFlags { + #[inline] + fn from(val: PipelineStageAccess) -> Self { + match val { + PipelineStageAccess::ImageLayoutTransition => AccessFlags::empty(), + PipelineStageAccess::DrawIndirect_IndirectCommandRead + | PipelineStageAccess::AccelerationStructureBuild_IndirectCommandRead => AccessFlags::INDIRECT_COMMAND_READ, + PipelineStageAccess::IndexInput_IndexRead => AccessFlags::INDEX_READ, + PipelineStageAccess::VertexAttributeInput_VertexAttributeRead => AccessFlags::VERTEX_ATTRIBUTE_READ, + PipelineStageAccess::VertexShader_UniformRead + | PipelineStageAccess::TessellationControlShader_UniformRead + | PipelineStageAccess::TessellationEvaluationShader_UniformRead + | PipelineStageAccess::GeometryShader_UniformRead + | PipelineStageAccess::FragmentShader_UniformRead + | PipelineStageAccess::ComputeShader_UniformRead + | PipelineStageAccess::AccelerationStructureBuild_UniformRead + | PipelineStageAccess::RayTracingShader_UniformRead + | PipelineStageAccess::TaskShader_UniformRead + | PipelineStageAccess::MeshShader_UniformRead => AccessFlags::UNIFORM_READ, + PipelineStageAccess::FragmentShader_InputAttachmentRead + | PipelineStageAccess::SubpassShading_InputAttachmentRead => AccessFlags::INPUT_ATTACHMENT_READ, + PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentRead => AccessFlags::COLOR_ATTACHMENT_READ, + PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentWrite => AccessFlags::COLOR_ATTACHMENT_WRITE, + PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentRead + | PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentRead => AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ, + PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite + | PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite => AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE, + PipelineStageAccess::Copy_TransferRead + | PipelineStageAccess::Resolve_TransferRead + | PipelineStageAccess::Blit_TransferRead + | PipelineStageAccess::AccelerationStructureBuild_TransferRead + | PipelineStageAccess::AccelerationStructureCopy_TransferRead => AccessFlags::TRANSFER_READ, + PipelineStageAccess::Copy_TransferWrite + | PipelineStageAccess::Resolve_TransferWrite + | PipelineStageAccess::Blit_TransferWrite + | PipelineStageAccess::Clear_TransferWrite + | PipelineStageAccess::AccelerationStructureBuild_TransferWrite + | PipelineStageAccess::AccelerationStructureCopy_TransferWrite => AccessFlags::TRANSFER_WRITE, + PipelineStageAccess::Host_HostRead => AccessFlags::HOST_READ, + PipelineStageAccess::Host_HostWrite => AccessFlags::HOST_WRITE, + PipelineStageAccess::VertexShader_ShaderSampledRead + | PipelineStageAccess::TessellationControlShader_ShaderSampledRead + | PipelineStageAccess::TessellationEvaluationShader_ShaderSampledRead + | PipelineStageAccess::GeometryShader_ShaderSampledRead + | PipelineStageAccess::FragmentShader_ShaderSampledRead + | PipelineStageAccess::ComputeShader_ShaderSampledRead + | PipelineStageAccess::AccelerationStructureBuild_ShaderSampledRead + | PipelineStageAccess::RayTracingShader_ShaderSampledRead + | PipelineStageAccess::TaskShader_ShaderSampledRead + | PipelineStageAccess::MeshShader_ShaderSampledRead => AccessFlags::SHADER_SAMPLED_READ, + PipelineStageAccess::VertexShader_ShaderStorageRead + | PipelineStageAccess::TessellationControlShader_ShaderStorageRead + | PipelineStageAccess::TessellationEvaluationShader_ShaderStorageRead + | PipelineStageAccess::GeometryShader_ShaderStorageRead + | PipelineStageAccess::FragmentShader_ShaderStorageRead + | PipelineStageAccess::ComputeShader_ShaderStorageRead + | PipelineStageAccess::AccelerationStructureBuild_ShaderStorageRead + | PipelineStageAccess::RayTracingShader_ShaderStorageRead + | PipelineStageAccess::TaskShader_ShaderStorageRead + | PipelineStageAccess::MeshShader_ShaderStorageRead => AccessFlags::SHADER_STORAGE_READ, + PipelineStageAccess::VertexShader_ShaderStorageWrite + | PipelineStageAccess::TessellationControlShader_ShaderStorageWrite + | PipelineStageAccess::TessellationEvaluationShader_ShaderStorageWrite + | PipelineStageAccess::GeometryShader_ShaderStorageWrite + | PipelineStageAccess::FragmentShader_ShaderStorageWrite + | PipelineStageAccess::ComputeShader_ShaderStorageWrite + | PipelineStageAccess::RayTracingShader_ShaderStorageWrite + | PipelineStageAccess::TaskShader_ShaderStorageWrite + | PipelineStageAccess::MeshShader_ShaderStorageWrite => AccessFlags::SHADER_STORAGE_WRITE, + PipelineStageAccess::VideoDecode_VideoDecodeRead => AccessFlags::VIDEO_DECODE_READ, + PipelineStageAccess::VideoDecode_VideoDecodeWrite => AccessFlags::VIDEO_DECODE_WRITE, + PipelineStageAccess::VideoEncode_VideoEncodeRead => AccessFlags::VIDEO_ENCODE_READ, + PipelineStageAccess::VideoEncode_VideoEncodeWrite => AccessFlags::VIDEO_ENCODE_WRITE, + PipelineStageAccess::TransformFeedback_TransformFeedbackWrite => AccessFlags::TRANSFORM_FEEDBACK_WRITE, + PipelineStageAccess::DrawIndirect_TransformFeedbackCounterRead + | PipelineStageAccess::TransformFeedback_TransformFeedbackCounterRead => AccessFlags::TRANSFORM_FEEDBACK_COUNTER_READ, + PipelineStageAccess::TransformFeedback_TransformFeedbackCounterWrite => AccessFlags::TRANSFORM_FEEDBACK_COUNTER_WRITE, + PipelineStageAccess::ConditionalRendering_ConditionalRenderingRead => AccessFlags::CONDITIONAL_RENDERING_READ, + PipelineStageAccess::CommandPreprocess_CommandPreprocessRead => AccessFlags::COMMAND_PREPROCESS_READ, + PipelineStageAccess::CommandPreprocess_CommandPreprocessWrite => AccessFlags::COMMAND_PREPROCESS_WRITE, + PipelineStageAccess::FragmentShadingRateAttachment_FragmentShadingRateAttachmentRead => AccessFlags::FRAGMENT_SHADING_RATE_ATTACHMENT_READ, + PipelineStageAccess::VertexShader_AccelerationStructureRead + | PipelineStageAccess::TessellationControlShader_AccelerationStructureRead + | PipelineStageAccess::TessellationEvaluationShader_AccelerationStructureRead + | PipelineStageAccess::GeometryShader_AccelerationStructureRead + | PipelineStageAccess::FragmentShader_AccelerationStructureRead + | PipelineStageAccess::ComputeShader_AccelerationStructureRead + | PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureRead + | PipelineStageAccess::RayTracingShader_AccelerationStructureRead + | PipelineStageAccess::TaskShader_AccelerationStructureRead + | PipelineStageAccess::MeshShader_AccelerationStructureRead => AccessFlags::ACCELERATION_STRUCTURE_READ, + PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureWrite => AccessFlags::ACCELERATION_STRUCTURE_WRITE, + PipelineStageAccess::FragmentDensityProcess_FragmentDensityMapRead => AccessFlags::FRAGMENT_DENSITY_MAP_READ, + PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentReadNoncoherent => AccessFlags::COLOR_ATTACHMENT_READ_NONCOHERENT, + PipelineStageAccess::InvocationMask_InvocationMaskRead => AccessFlags::INVOCATION_MASK_READ, + PipelineStageAccess::RayTracingShader_ShaderBindingTableRead => AccessFlags::SHADER_BINDING_TABLE_READ, + PipelineStageAccess::AccelerationStructureBuild_MicromapRead + | PipelineStageAccess::MicromapBuild_MicromapRead => AccessFlags::MICROMAP_READ, + PipelineStageAccess::MicromapBuild_MicromapWrite => AccessFlags::MICROMAP_WRITE, + PipelineStageAccess::OpticalFlow_OpticalFlowRead => AccessFlags::OPTICAL_FLOW_READ, + PipelineStageAccess::OpticalFlow_OpticalFlowWrite => AccessFlags::OPTICAL_FLOW_WRITE, + PipelineStageAccess::__MAX_VALUE__ => unreachable!(), + } + } +} + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] +pub(crate) struct PipelineStageAccessSet(u128); + +#[allow(dead_code)] +impl PipelineStageAccessSet { + #[inline] + pub(crate) const fn empty() -> Self { + Self(0) + } + + #[inline] + pub(crate) const fn count(self) -> u32 { + self.0.count_ones() + } + + #[inline] + pub(crate) const fn is_empty(self) -> bool { + self.0 == 0 + } + + #[inline] + pub(crate) const fn intersects(self, other: Self) -> bool { + self.0 & other.0 != 0 + } + + #[inline] + pub(crate) const fn contains(self, other: Self) -> bool { + self.0 & other.0 == other.0 + } + + #[inline] + pub(crate) const fn union(self, other: Self) -> Self { + Self(self.0 | other.0) + } + + #[inline] + pub(crate) const fn intersection(self, other: Self) -> Self { + Self(self.0 & other.0) + } + + #[inline] + pub(crate) const fn difference(self, other: Self) -> Self { + Self(self.0 & !other.0) + } + + #[inline] + pub(crate) const fn symmetric_difference(self, other: Self) -> Self { + Self(self.0 ^ other.0) + } + + #[inline] + pub(crate) fn contains_enum(self, val: PipelineStageAccess) -> bool { + self.intersects(val.into()) + } +} + +impl std::ops::BitAnd for PipelineStageAccessSet { + type Output = Self; + + #[inline] + fn bitand(self, rhs: Self) -> Self { + self.intersection(rhs) + } +} + +impl std::ops::BitAndAssign for PipelineStageAccessSet { + #[inline] + fn bitand_assign(&mut self, rhs: Self) { + *self = self.intersection(rhs); + } +} + +impl std::ops::BitOr for PipelineStageAccessSet { + type Output = Self; + + #[inline] + fn bitor(self, rhs: Self) -> Self { + self.union(rhs) + } +} + +impl std::ops::BitOrAssign for PipelineStageAccessSet { + #[inline] + fn bitor_assign(&mut self, rhs: Self) { + *self = self.union(rhs); + } +} + +impl std::ops::BitXor for PipelineStageAccessSet { + type Output = Self; + + #[inline] + fn bitxor(self, rhs: Self) -> Self { + self.symmetric_difference(rhs) + } +} + +impl std::ops::BitXorAssign for PipelineStageAccessSet { + #[inline] + fn bitxor_assign(&mut self, rhs: Self) { + *self = self.symmetric_difference(rhs); + } +} + +impl std::ops::Sub for PipelineStageAccessSet { + type Output = Self; + + #[inline] + fn sub(self, rhs: Self) -> Self { + self.difference(rhs) + } +} + +impl std::ops::SubAssign for PipelineStageAccessSet { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = self.difference(rhs); + } +} + +impl From<PipelineStageAccess> for PipelineStageAccessSet { + #[inline] + fn from(val: PipelineStageAccess) -> Self { + debug_assert!(val != PipelineStageAccess::__MAX_VALUE__); // You did something very dumb... + Self(1u128 << val as u8) + } +} + +impl From<PipelineStages> for PipelineStageAccessSet { + #[inline] + fn from(stages: PipelineStages) -> Self { + let mut result = Self::empty(); + + if stages.intersects(PipelineStages::DRAW_INDIRECT) { + result |= Self::from(PipelineStageAccess::DrawIndirect_IndirectCommandRead) + | Self::from(PipelineStageAccess::DrawIndirect_TransformFeedbackCounterRead) + } + + if stages.intersects(PipelineStages::VERTEX_SHADER) { + result |= Self::from(PipelineStageAccess::VertexShader_UniformRead) + | Self::from(PipelineStageAccess::VertexShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::VertexShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::VertexShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::VertexShader_AccelerationStructureRead) + } + + if stages.intersects(PipelineStages::TESSELLATION_CONTROL_SHADER) { + result |= Self::from(PipelineStageAccess::TessellationControlShader_UniformRead) + | Self::from(PipelineStageAccess::TessellationControlShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::TessellationControlShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::TessellationControlShader_ShaderStorageWrite) + | Self::from( + PipelineStageAccess::TessellationControlShader_AccelerationStructureRead, + ) + } + + if stages.intersects(PipelineStages::TESSELLATION_EVALUATION_SHADER) { + result |= Self::from(PipelineStageAccess::TessellationEvaluationShader_UniformRead) + | Self::from(PipelineStageAccess::TessellationEvaluationShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::TessellationEvaluationShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::TessellationEvaluationShader_ShaderStorageWrite) + | Self::from( + PipelineStageAccess::TessellationEvaluationShader_AccelerationStructureRead, + ) + } + + if stages.intersects(PipelineStages::GEOMETRY_SHADER) { + result |= Self::from(PipelineStageAccess::GeometryShader_UniformRead) + | Self::from(PipelineStageAccess::GeometryShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::GeometryShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::GeometryShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::GeometryShader_AccelerationStructureRead) + } + + if stages.intersects(PipelineStages::FRAGMENT_SHADER) { + result |= Self::from(PipelineStageAccess::FragmentShader_UniformRead) + | Self::from(PipelineStageAccess::FragmentShader_InputAttachmentRead) + | Self::from(PipelineStageAccess::FragmentShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::FragmentShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::FragmentShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::FragmentShader_AccelerationStructureRead) + } + + if stages.intersects(PipelineStages::EARLY_FRAGMENT_TESTS) { + result |= Self::from(PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentRead) + | Self::from(PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite) + } + + if stages.intersects(PipelineStages::LATE_FRAGMENT_TESTS) { + result |= Self::from(PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentRead) + | Self::from(PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite) + } + + if stages.intersects(PipelineStages::COLOR_ATTACHMENT_OUTPUT) { + result |= Self::from(PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentRead) + | Self::from(PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentWrite) + | Self::from( + PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentReadNoncoherent, + ) + } + + if stages.intersects(PipelineStages::COMPUTE_SHADER) { + result |= Self::from(PipelineStageAccess::ComputeShader_UniformRead) + | Self::from(PipelineStageAccess::ComputeShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::ComputeShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::ComputeShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::ComputeShader_AccelerationStructureRead) + } + + if stages.intersects(PipelineStages::HOST) { + result |= Self::from(PipelineStageAccess::Host_HostRead) + | Self::from(PipelineStageAccess::Host_HostWrite) + } + + if stages.intersects(PipelineStages::COPY) { + result |= Self::from(PipelineStageAccess::Copy_TransferRead) + | Self::from(PipelineStageAccess::Copy_TransferWrite) + } + + if stages.intersects(PipelineStages::RESOLVE) { + result |= Self::from(PipelineStageAccess::Resolve_TransferRead) + | Self::from(PipelineStageAccess::Resolve_TransferWrite) + } + + if stages.intersects(PipelineStages::BLIT) { + result |= Self::from(PipelineStageAccess::Blit_TransferRead) + | Self::from(PipelineStageAccess::Blit_TransferWrite) + } + + if stages.intersects(PipelineStages::CLEAR) { + result |= Self::from(PipelineStageAccess::Clear_TransferWrite) + } + + if stages.intersects(PipelineStages::INDEX_INPUT) { + result |= Self::from(PipelineStageAccess::IndexInput_IndexRead) + } + + if stages.intersects(PipelineStages::VERTEX_ATTRIBUTE_INPUT) { + result |= Self::from(PipelineStageAccess::VertexAttributeInput_VertexAttributeRead) + } + + if stages.intersects(PipelineStages::VIDEO_DECODE) { + result |= Self::from(PipelineStageAccess::VideoDecode_VideoDecodeRead) + | Self::from(PipelineStageAccess::VideoDecode_VideoDecodeWrite) + } + + if stages.intersects(PipelineStages::VIDEO_ENCODE) { + result |= Self::from(PipelineStageAccess::VideoEncode_VideoEncodeRead) + | Self::from(PipelineStageAccess::VideoEncode_VideoEncodeWrite) + } + + if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK) { + result |= Self::from(PipelineStageAccess::TransformFeedback_TransformFeedbackWrite) + | Self::from(PipelineStageAccess::TransformFeedback_TransformFeedbackCounterRead) + | Self::from(PipelineStageAccess::TransformFeedback_TransformFeedbackCounterWrite) + } + + if stages.intersects(PipelineStages::CONDITIONAL_RENDERING) { + result |= Self::from(PipelineStageAccess::ConditionalRendering_ConditionalRenderingRead) + } + + if stages.intersects(PipelineStages::ACCELERATION_STRUCTURE_BUILD) { + result |= + Self::from(PipelineStageAccess::AccelerationStructureBuild_IndirectCommandRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_UniformRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_TransferRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_TransferWrite) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_ShaderSampledRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_ShaderStorageRead) + | Self::from( + PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureRead, + ) + | Self::from( + PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureWrite, + ) + // | Self::from(PipelineStageAccess::AccelerationStructureBuild_MicromapRead) + } + + if stages.intersects(PipelineStages::RAY_TRACING_SHADER) { + result |= Self::from(PipelineStageAccess::RayTracingShader_UniformRead) + | Self::from(PipelineStageAccess::RayTracingShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::RayTracingShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::RayTracingShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::RayTracingShader_AccelerationStructureRead) + // | Self::from(PipelineStageAccess::RayTracingShader_ShaderBindingTableRead) + } + + if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS) { + result |= Self::from(PipelineStageAccess::FragmentDensityProcess_FragmentDensityMapRead) + } + + if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT) { + result |= + PipelineStageAccess::FragmentShadingRateAttachment_FragmentShadingRateAttachmentRead + .into() + } + + if stages.intersects(PipelineStages::COMMAND_PREPROCESS) { + result |= Self::from(PipelineStageAccess::CommandPreprocess_CommandPreprocessRead) + | Self::from(PipelineStageAccess::CommandPreprocess_CommandPreprocessWrite) + } + + if stages.intersects(PipelineStages::TASK_SHADER) { + result |= Self::from(PipelineStageAccess::TaskShader_UniformRead) + | Self::from(PipelineStageAccess::TaskShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::TaskShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::TaskShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::TaskShader_AccelerationStructureRead) + } + + if stages.intersects(PipelineStages::MESH_SHADER) { + result |= Self::from(PipelineStageAccess::MeshShader_UniformRead) + | Self::from(PipelineStageAccess::MeshShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::MeshShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::MeshShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::MeshShader_AccelerationStructureRead) + } + + if stages.intersects(PipelineStages::SUBPASS_SHADING) { + result |= Self::from(PipelineStageAccess::SubpassShading_InputAttachmentRead) + } + + if stages.intersects(PipelineStages::INVOCATION_MASK) { + result |= Self::from(PipelineStageAccess::InvocationMask_InvocationMaskRead) + } + + /* + if stages.intersects(PipelineStages::OPTICAL_FLOW) { + result |= Self::from(PipelineStageAccess::OpticalFlow_OpticalFlowRead) + | Self::from(PipelineStageAccess::OpticalFlow_OpticalFlowWrite) + } + + if stages.intersects(PipelineStages::MICROMAP_BUILD) { + result |= Self::from(PipelineStageAccess::MicromapBuild_MicromapWrite) + | Self::from(PipelineStageAccess::MicromapBuild_MicromapRead) + } + */ + + result + } +} + +impl From<AccessFlags> for PipelineStageAccessSet { + #[inline] + fn from(access: AccessFlags) -> Self { + let mut result = Self::empty(); + + if access.intersects(AccessFlags::INDIRECT_COMMAND_READ) { + result |= Self::from(PipelineStageAccess::DrawIndirect_IndirectCommandRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_IndirectCommandRead) + } + + if access.intersects(AccessFlags::INDEX_READ) { + result |= Self::from(PipelineStageAccess::IndexInput_IndexRead) + } + + if access.intersects(AccessFlags::VERTEX_ATTRIBUTE_READ) { + result |= Self::from(PipelineStageAccess::VertexAttributeInput_VertexAttributeRead) + } + + if access.intersects(AccessFlags::UNIFORM_READ) { + result |= Self::from(PipelineStageAccess::VertexShader_UniformRead) + | Self::from(PipelineStageAccess::TessellationControlShader_UniformRead) + | Self::from(PipelineStageAccess::TessellationEvaluationShader_UniformRead) + | Self::from(PipelineStageAccess::GeometryShader_UniformRead) + | Self::from(PipelineStageAccess::FragmentShader_UniformRead) + | Self::from(PipelineStageAccess::ComputeShader_UniformRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_UniformRead) + | Self::from(PipelineStageAccess::RayTracingShader_UniformRead) + | Self::from(PipelineStageAccess::TaskShader_UniformRead) + | Self::from(PipelineStageAccess::MeshShader_UniformRead) + } + + if access.intersects(AccessFlags::INPUT_ATTACHMENT_READ) { + result |= Self::from(PipelineStageAccess::FragmentShader_InputAttachmentRead) + | Self::from(PipelineStageAccess::SubpassShading_InputAttachmentRead) + } + + if access.intersects(AccessFlags::COLOR_ATTACHMENT_READ) { + result |= Self::from(PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentRead) + } + + if access.intersects(AccessFlags::COLOR_ATTACHMENT_WRITE) { + result |= Self::from(PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentWrite) + } + + if access.intersects(AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ) { + result |= Self::from(PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentRead) + | Self::from(PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentRead) + } + + if access.intersects(AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE) { + result |= + Self::from(PipelineStageAccess::EarlyFragmentTests_DepthStencilAttachmentWrite) + | Self::from(PipelineStageAccess::LateFragmentTests_DepthStencilAttachmentWrite) + } + + if access.intersects(AccessFlags::TRANSFER_READ) { + result |= Self::from(PipelineStageAccess::Copy_TransferRead) + | Self::from(PipelineStageAccess::Resolve_TransferRead) + | Self::from(PipelineStageAccess::Blit_TransferRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_TransferRead) + } + + if access.intersects(AccessFlags::TRANSFER_WRITE) { + result |= Self::from(PipelineStageAccess::Copy_TransferWrite) + | Self::from(PipelineStageAccess::Resolve_TransferWrite) + | Self::from(PipelineStageAccess::Blit_TransferWrite) + | Self::from(PipelineStageAccess::Clear_TransferWrite) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_TransferWrite) + } + + if access.intersects(AccessFlags::HOST_READ) { + result |= Self::from(PipelineStageAccess::Host_HostRead) + } + + if access.intersects(AccessFlags::HOST_WRITE) { + result |= Self::from(PipelineStageAccess::Host_HostWrite) + } + + if access.intersects(AccessFlags::SHADER_SAMPLED_READ) { + result |= Self::from(PipelineStageAccess::VertexShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::TessellationControlShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::TessellationEvaluationShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::GeometryShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::FragmentShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::ComputeShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_ShaderSampledRead) + | Self::from(PipelineStageAccess::RayTracingShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::TaskShader_ShaderSampledRead) + | Self::from(PipelineStageAccess::MeshShader_ShaderSampledRead) + } + + if access.intersects(AccessFlags::SHADER_STORAGE_READ) { + result |= Self::from(PipelineStageAccess::VertexShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::TessellationControlShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::TessellationEvaluationShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::GeometryShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::FragmentShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::ComputeShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::AccelerationStructureBuild_ShaderStorageRead) + | Self::from(PipelineStageAccess::RayTracingShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::TaskShader_ShaderStorageRead) + | Self::from(PipelineStageAccess::MeshShader_ShaderStorageRead) + } + + if access.intersects(AccessFlags::SHADER_STORAGE_WRITE) { + result |= Self::from(PipelineStageAccess::VertexShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::TessellationControlShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::TessellationEvaluationShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::GeometryShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::FragmentShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::ComputeShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::RayTracingShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::TaskShader_ShaderStorageWrite) + | Self::from(PipelineStageAccess::MeshShader_ShaderStorageWrite) + } + + if access.intersects(AccessFlags::VIDEO_DECODE_READ) { + result |= Self::from(PipelineStageAccess::VideoDecode_VideoDecodeRead) + } + + if access.intersects(AccessFlags::VIDEO_DECODE_WRITE) { + result |= Self::from(PipelineStageAccess::VideoDecode_VideoDecodeWrite) + } + + if access.intersects(AccessFlags::VIDEO_ENCODE_READ) { + result |= Self::from(PipelineStageAccess::VideoEncode_VideoEncodeRead) + } + + if access.intersects(AccessFlags::VIDEO_ENCODE_WRITE) { + result |= Self::from(PipelineStageAccess::VideoEncode_VideoEncodeWrite) + } + + if access.intersects(AccessFlags::TRANSFORM_FEEDBACK_WRITE) { + result |= Self::from(PipelineStageAccess::TransformFeedback_TransformFeedbackWrite) + } + + if access.intersects(AccessFlags::TRANSFORM_FEEDBACK_COUNTER_READ) { + result |= Self::from(PipelineStageAccess::DrawIndirect_TransformFeedbackCounterRead) + | Self::from(PipelineStageAccess::TransformFeedback_TransformFeedbackCounterRead) + } + + if access.intersects(AccessFlags::TRANSFORM_FEEDBACK_COUNTER_WRITE) { + result |= + Self::from(PipelineStageAccess::TransformFeedback_TransformFeedbackCounterWrite) + } + + if access.intersects(AccessFlags::CONDITIONAL_RENDERING_READ) { + result |= Self::from(PipelineStageAccess::ConditionalRendering_ConditionalRenderingRead) + } + + if access.intersects(AccessFlags::COMMAND_PREPROCESS_READ) { + result |= Self::from(PipelineStageAccess::CommandPreprocess_CommandPreprocessRead) + } + + if access.intersects(AccessFlags::COMMAND_PREPROCESS_WRITE) { + result |= Self::from(PipelineStageAccess::CommandPreprocess_CommandPreprocessWrite) + } + + if access.intersects(AccessFlags::FRAGMENT_SHADING_RATE_ATTACHMENT_READ) { + result |= + Self::from(PipelineStageAccess::FragmentShadingRateAttachment_FragmentShadingRateAttachmentRead) + } + + if access.intersects(AccessFlags::ACCELERATION_STRUCTURE_READ) { + result |= Self::from(PipelineStageAccess::VertexShader_AccelerationStructureRead) + | Self::from( + PipelineStageAccess::TessellationControlShader_AccelerationStructureRead, + ) + | Self::from( + PipelineStageAccess::TessellationEvaluationShader_AccelerationStructureRead, + ) + | Self::from(PipelineStageAccess::GeometryShader_AccelerationStructureRead) + | Self::from(PipelineStageAccess::FragmentShader_AccelerationStructureRead) + | Self::from(PipelineStageAccess::ComputeShader_AccelerationStructureRead) + | Self::from( + PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureRead, + ) + | Self::from(PipelineStageAccess::RayTracingShader_AccelerationStructureRead) + | Self::from(PipelineStageAccess::TaskShader_AccelerationStructureRead) + | Self::from(PipelineStageAccess::MeshShader_AccelerationStructureRead) + } + + if access.intersects(AccessFlags::ACCELERATION_STRUCTURE_WRITE) { + result |= Self::from( + PipelineStageAccess::AccelerationStructureBuild_AccelerationStructureWrite, + ) + } + + if access.intersects(AccessFlags::FRAGMENT_DENSITY_MAP_READ) { + result |= Self::from(PipelineStageAccess::FragmentDensityProcess_FragmentDensityMapRead) + } + + if access.intersects(AccessFlags::COLOR_ATTACHMENT_READ_NONCOHERENT) { + result |= Self::from( + PipelineStageAccess::ColorAttachmentOutput_ColorAttachmentReadNoncoherent, + ) + } + + if access.intersects(AccessFlags::INVOCATION_MASK_READ) { + result |= Self::from(PipelineStageAccess::InvocationMask_InvocationMaskRead) + } + + /* + if access.intersects(AccessFlags::SHADER_BINDING_TABLE_READ) { + result |= Self::from(PipelineStageAccess::RayTracingShader_ShaderBindingTableRead) + } + + if access.intersects(AccessFlags::MICROMAP_READ) { + result |= Self::from(PipelineStageAccess::AccelerationStructureBuild_MicromapRead) + | Self::from(PipelineStageAccess::MicromapBuild_MicromapRead) + } + + if access.intersects(AccessFlags::MICROMAP_WRITE) { + result |= Self::from(PipelineStageAccess::MicromapBuild_MicromapWrite) + } + + if access.intersects(AccessFlags::OPTICAL_FLOW_READ) { + result |= Self::from(PipelineStageAccess::OpticalFlow_OpticalFlowRead) + } + + if access.intersects(AccessFlags::OPTICAL_FLOW_WRITE) { + result |= Self::from(PipelineStageAccess::OpticalFlow_OpticalFlowWrite) + } + */ + + result + } +} + +/// Dependency info for barriers in a pipeline barrier or event command. +/// +/// A pipeline barrier creates a dependency between commands submitted before the barrier (the +/// source scope) and commands submitted after it (the destination scope). An event command acts +/// like a split pipeline barrier: the source scope and destination scope are defined +/// relative to different commands. Each `DependencyInfo` consists of multiple individual barriers +/// that concern a either single resource or operate globally. +/// +/// Each barrier has a set of source/destination pipeline stages and source/destination memory +/// access types. The pipeline stages create an *execution dependency*: the `src_stages` of +/// commands submitted before the barrier must be completely finished before before any of the +/// `dst_stages` of commands after the barrier are allowed to start. The memory access types +/// create a *memory dependency*: in addition to the execution dependency, any `src_access` +/// performed before the barrier must be made available and visible before any `dst_access` +/// are made after the barrier. +#[derive(Clone, Debug)] +pub struct DependencyInfo { + /// Flags to modify how the execution and memory dependencies are formed. + /// + /// The default value is empty. + pub dependency_flags: DependencyFlags, + + /// Memory barriers for global operations and accesses, not limited to a single resource. + /// + /// The default value is empty. + pub memory_barriers: SmallVec<[MemoryBarrier; 2]>, + + /// Memory barriers for individual buffers. + /// + /// The default value is empty. + pub buffer_memory_barriers: SmallVec<[BufferMemoryBarrier; 8]>, + + /// Memory barriers for individual images. + /// + /// The default value is empty. + pub image_memory_barriers: SmallVec<[ImageMemoryBarrier; 8]>, + + pub _ne: crate::NonExhaustive, +} + +impl DependencyInfo { + /// Returns whether `self` contains any barriers. + #[inline] + pub fn is_empty(&self) -> bool { + self.memory_barriers.is_empty() + && self.buffer_memory_barriers.is_empty() + && self.image_memory_barriers.is_empty() + } + + /// Clears all barriers. + #[inline] + pub fn clear(&mut self) { + self.memory_barriers.clear(); + self.buffer_memory_barriers.clear(); + self.image_memory_barriers.clear(); + } +} + +impl Default for DependencyInfo { + #[inline] + fn default() -> Self { + Self { + dependency_flags: DependencyFlags::empty(), + memory_barriers: SmallVec::new(), + buffer_memory_barriers: SmallVec::new(), + image_memory_barriers: SmallVec::new(), + _ne: crate::NonExhaustive(()), + } + } +} + +vulkan_bitflags! { + #[non_exhaustive] + + /// Flags that modify how execution and memory dependencies are formed. + DependencyFlags = DependencyFlags(u32); + + /// For framebuffer-space pipeline stages, specifies that the dependency is framebuffer-local. + /// The implementation can start the destination operation for some given pixels as long as the + /// source operation is finished for these given pixels. + /// + /// Framebuffer-local dependencies are usually more efficient, especially on tile-based + /// architectures. + BY_REGION = BY_REGION, + + /// For devices that consist of multiple physical devices, specifies that the dependency is + /// device-local. The dependency will only apply to the operations on each physical device + /// individually, rather than applying to all physical devices as a whole. This allows each + /// physical device to operate independently of the others. + /// + /// The device API version must be at least 1.1, or the [`khr_device_group`] extension must be + /// enabled on the device. + /// + /// [`khr_device_group`]: crate::device::DeviceExtensions::khr_device_group + DEVICE_GROUP = DEVICE_GROUP { + api_version: V1_1, + device_extensions: [khr_device_group], + }, + + + /// For subpass dependencies, and pipeline barriers executing within a render pass instance, + /// if the render pass uses multiview rendering, specifies that the dependency is view-local. + /// Each view in the destination subpass will only depend on a single view in the destination + /// subpass, instead of all views. + /// + /// The device API version must be at least 1.1, or the [`khr_multiview`] extension must be + /// enabled on the device. + /// + /// [`khr_multiview`]: crate::device::DeviceExtensions::khr_multiview + VIEW_LOCAL = VIEW_LOCAL { + api_version: V1_1, + device_extensions: [khr_multiview], + }, +} + +/// A memory barrier that is applied globally. +#[derive(Clone, Debug)] +pub struct MemoryBarrier { + /// The pipeline stages in the source scope to wait for. + /// + /// The default value is [`PipelineStages::empty()`]. + pub src_stages: PipelineStages, + + /// The memory accesses in the source scope to make available and visible. + /// + /// The default value is [`AccessFlags::empty()`]. + pub src_access: AccessFlags, + + /// The pipeline stages in the destination scope that must wait for `src_stages`. + /// + /// The default value is [`PipelineStages::empty()`]. + pub dst_stages: PipelineStages, + + /// The memory accesses in the destination scope that must wait for `src_access` to be made + /// available and visible. + pub dst_access: AccessFlags, + + pub _ne: crate::NonExhaustive, +} + +impl Default for MemoryBarrier { + #[inline] + fn default() -> Self { + Self { + src_stages: PipelineStages::empty(), + src_access: AccessFlags::empty(), + dst_stages: PipelineStages::empty(), + dst_access: AccessFlags::empty(), + _ne: crate::NonExhaustive(()), + } + } +} + +/// A memory barrier that is applied to a single buffer. +#[derive(Clone, Debug)] +pub struct BufferMemoryBarrier { + /// The pipeline stages in the source scope to wait for. + /// + /// The default value is [`PipelineStages::empty()`]. + pub src_stages: PipelineStages, + + /// The memory accesses in the source scope to make available and visible. + /// + /// The default value is [`AccessFlags::empty()`]. + pub src_access: AccessFlags, + + /// The pipeline stages in the destination scope that must wait for `src_stages`. + /// + /// The default value is [`PipelineStages::empty()`]. + pub dst_stages: PipelineStages, + + /// The memory accesses in the destination scope that must wait for `src_access` to be made + /// available and visible. + pub dst_access: AccessFlags, + + /// For resources created with [`Sharing::Exclusive`](crate::sync::Sharing), transfers + /// ownership of a resource from one queue family to another. + pub queue_family_ownership_transfer: Option<QueueFamilyOwnershipTransfer>, + + /// The buffer to apply the barrier to. + pub buffer: Arc<Buffer>, + + /// The byte range of `buffer` to apply the barrier to. + pub range: Range<DeviceSize>, + + pub _ne: crate::NonExhaustive, +} + +impl BufferMemoryBarrier { + #[inline] + pub fn buffer(buffer: Arc<Buffer>) -> Self { + Self { + src_stages: PipelineStages::empty(), + src_access: AccessFlags::empty(), + dst_stages: PipelineStages::empty(), + dst_access: AccessFlags::empty(), + queue_family_ownership_transfer: None, + buffer, + range: 0..0, + _ne: crate::NonExhaustive(()), + } + } +} + +/// A memory barrier that is applied to a single image. +#[derive(Clone, Debug)] +pub struct ImageMemoryBarrier { + /// The pipeline stages in the source scope to wait for. + /// + /// The default value is [`PipelineStages::empty()`]. + pub src_stages: PipelineStages, + + /// The memory accesses in the source scope to make available and visible. + /// + /// The default value is [`AccessFlags::empty()`]. + pub src_access: AccessFlags, + + /// The pipeline stages in the destination scope that must wait for `src_stages`. + /// + /// The default value is [`PipelineStages::empty()`]. + pub dst_stages: PipelineStages, + + /// The memory accesses in the destination scope that must wait for `src_access` to be made + /// available and visible. + pub dst_access: AccessFlags, + + /// The layout that the specified `subresource_range` of `image` is expected to be in when the + /// source scope completes. + pub old_layout: ImageLayout, + + /// The layout that the specified `subresource_range` of `image` will be transitioned to before + /// the destination scope begins. + pub new_layout: ImageLayout, + + /// For resources created with [`Sharing::Exclusive`](crate::sync::Sharing), transfers + /// ownership of a resource from one queue family to another. + pub queue_family_ownership_transfer: Option<QueueFamilyOwnershipTransfer>, + + /// The image to apply the barrier to. + pub image: Arc<Image>, + + /// The subresource range of `image` to apply the barrier to. + pub subresource_range: ImageSubresourceRange, + + pub _ne: crate::NonExhaustive, +} + +impl ImageMemoryBarrier { + #[inline] + pub fn image(image: Arc<Image>) -> Self { + Self { + src_stages: PipelineStages::empty(), + src_access: AccessFlags::empty(), + dst_stages: PipelineStages::empty(), + dst_access: AccessFlags::empty(), + old_layout: ImageLayout::Undefined, + new_layout: ImageLayout::Undefined, + queue_family_ownership_transfer: None, + image, + subresource_range: ImageSubresourceRange { + aspects: ImageAspects::empty(), // Can't use image format aspects because `color` can't be specified with `planeN`. + mip_levels: 0..0, + array_layers: 0..0, + }, + _ne: crate::NonExhaustive(()), + } + } +} + +/// Specifies a queue family ownership transfer for a resource. +/// +/// There are three classes of queues that can be used in an ownership transfer: +/// - A **local** queue exists on the current [`Instance`] and [`Device`]. +/// - An **external** queue does not exist on the current [`Instance`], but has the same +/// [`device_uuid`] and [`driver_uuid`] as the current [`Device`]. +/// - A **foreign** queue can be an external queue, or any queue on another device for which the +/// mentioned parameters do not match. +/// +/// [`Instance`]: crate::instance::Instance +/// [`Device`]: crate::device::Device +/// [`device_uuid`]: crate::device::Properties::device_uuid +/// [`driver_uuid`]: crate::device::Properties::driver_uuid +#[derive(Clone, Copy, Debug)] +pub enum QueueFamilyOwnershipTransfer { + /// For a resource with [`Sharing::Exclusive`], transfers ownership between two local queues. + /// + /// [`Sharing::Exclusive`]: crate::sync::Sharing::Exclusive + ExclusiveBetweenLocal { + /// The queue family that currently owns the resource. + src_index: u32, + + /// The queue family to transfer ownership to. + dst_index: u32, + }, + + /// For a resource with [`Sharing::Exclusive`], transfers ownership from a local queue to an + /// external queue. + /// + /// The device API version must be at least 1.1, or the [`khr_external_memory`] extension must + /// be enabled on the device. + /// + /// [`Sharing::Exclusive`]: crate::sync::Sharing::Exclusive + /// [`khr_external_memory`]: crate::device::DeviceExtensions::khr_external_memory + ExclusiveToExternal { + /// The queue family that currently owns the resource. + src_index: u32, + }, + + /// For a resource with [`Sharing::Exclusive`], transfers ownership from an external queue to a + /// local queue. + /// + /// The device API version must be at least 1.1, or the [`khr_external_memory`] extension must + /// be enabled on the device. + /// + /// [`Sharing::Exclusive`]: crate::sync::Sharing::Exclusive + /// [`khr_external_memory`]: crate::device::DeviceExtensions::khr_external_memory + ExclusiveFromExternal { + /// The queue family to transfer ownership to. + dst_index: u32, + }, + + /// For a resource with [`Sharing::Exclusive`], transfers ownership from a local queue to a + /// foreign queue. + /// + /// The [`ext_queue_family_foreign`] extension must be enabled on the device. + /// + /// [`Sharing::Exclusive`]: crate::sync::Sharing::Exclusive + /// [`ext_queue_family_foreign`]: crate::device::DeviceExtensions::ext_queue_family_foreign + ExclusiveToForeign { + /// The queue family that currently owns the resource. + src_index: u32, + }, + + /// For a resource with [`Sharing::Exclusive`], transfers ownership from a foreign queue to a + /// local queue. + /// + /// The [`ext_queue_family_foreign`] extension must be enabled on the device. + /// + /// [`Sharing::Exclusive`]: crate::sync::Sharing::Exclusive + /// [`ext_queue_family_foreign`]: crate::device::DeviceExtensions::ext_queue_family_foreign + ExclusiveFromForeign { + /// The queue family to transfer ownership to. + dst_index: u32, + }, + + /// For a resource with [`Sharing::Concurrent`], transfers ownership from its local queues to + /// an external queue. + /// + /// The device API version must be at least 1.1, or the [`khr_external_memory`] extension must + /// be enabled on the device. + /// + /// [`Sharing::Concurrent`]: crate::sync::Sharing::Concurrent + /// [`khr_external_memory`]: crate::device::DeviceExtensions::khr_external_memory + ConcurrentToExternal, + + /// For a resource with [`Sharing::Concurrent`], transfers ownership from an external queue to + /// its local queues. + /// + /// The device API version must be at least 1.1, or the [`khr_external_memory`] extension must + /// be enabled on the device. + /// + /// [`Sharing::Concurrent`]: crate::sync::Sharing::Concurrent + /// [`khr_external_memory`]: crate::device::DeviceExtensions::khr_external_memory + ConcurrentFromExternal, + + /// For a resource with [`Sharing::Concurrent`], transfers ownership from its local queues to + /// a foreign queue. + /// + /// The [`ext_queue_family_foreign`] extension must be enabled on the device. + /// + /// [`Sharing::Concurrent`]: crate::sync::Sharing::Concurrent + /// [`ext_queue_family_foreign`]: crate::device::DeviceExtensions::ext_queue_family_foreign + ConcurrentToForeign, + + /// For a resource with [`Sharing::Concurrent`], transfers ownership from a foreign queue to + /// its local queues. + /// + /// The [`ext_queue_family_foreign`] extension must be enabled on the device. + /// + /// [`Sharing::Concurrent`]: crate::sync::Sharing::Concurrent + /// [`ext_queue_family_foreign`]: crate::device::DeviceExtensions::ext_queue_family_foreign + ConcurrentFromForeign, +} + +impl QueueFamilyOwnershipTransfer { + pub(crate) fn validate_device(self, device: &Device) -> Result<(), RequirementNotMet> { + match self { + QueueFamilyOwnershipTransfer::ExclusiveToExternal { .. } => { + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_external_memory) + { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ExclusiveToExternal", + requires_one_of: crate::RequiresOneOf { + api_version: Some(Version::V1_1), + device_extensions: &["khr_external_memory"], + ..Default::default() + }, + }); + } + } + QueueFamilyOwnershipTransfer::ExclusiveFromExternal { .. } => { + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_external_memory) + { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ExclusiveFromExternal", + requires_one_of: crate::RequiresOneOf { + api_version: Some(Version::V1_1), + device_extensions: &["khr_external_memory"], + ..Default::default() + }, + }); + } + } + QueueFamilyOwnershipTransfer::ExclusiveToForeign { .. } => { + if !device.enabled_extensions().ext_queue_family_foreign { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ExclusiveToForeign", + requires_one_of: crate::RequiresOneOf { + device_extensions: &["ext_queue_family_foreign"], + ..Default::default() + }, + }); + } + } + QueueFamilyOwnershipTransfer::ExclusiveFromForeign { .. } => { + if !device.enabled_extensions().ext_queue_family_foreign { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ExclusiveFromForeign", + requires_one_of: crate::RequiresOneOf { + device_extensions: &["ext_queue_family_foreign"], + ..Default::default() + }, + }); + } + } + QueueFamilyOwnershipTransfer::ConcurrentToExternal => { + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_external_memory) + { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ConcurrentToExternal", + requires_one_of: crate::RequiresOneOf { + api_version: Some(Version::V1_1), + device_extensions: &["khr_external_memory"], + ..Default::default() + }, + }); + } + } + QueueFamilyOwnershipTransfer::ConcurrentFromExternal => { + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_external_memory) + { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ConcurrentFromExternal", + requires_one_of: crate::RequiresOneOf { + api_version: Some(Version::V1_1), + device_extensions: &["khr_external_memory"], + ..Default::default() + }, + }); + } + } + QueueFamilyOwnershipTransfer::ConcurrentToForeign => { + if !device.enabled_extensions().ext_queue_family_foreign { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ConcurrentToForeign", + requires_one_of: crate::RequiresOneOf { + device_extensions: &["ext_queue_family_foreign"], + ..Default::default() + }, + }); + } + } + QueueFamilyOwnershipTransfer::ConcurrentFromForeign => { + if !device.enabled_extensions().ext_queue_family_foreign { + return Err(crate::RequirementNotMet { + required_for: "`QueueFamilyOwnershipTransfer::ConcurrentFromForeign", + requires_one_of: crate::RequiresOneOf { + device_extensions: &["ext_queue_family_foreign"], + ..Default::default() + }, + }); + } + } + _ => (), + } + + Ok(()) + } +} + +impl From<QueueFamilyOwnershipTransfer> for (u32, u32) { + fn from(val: QueueFamilyOwnershipTransfer) -> Self { + match val { + QueueFamilyOwnershipTransfer::ExclusiveBetweenLocal { + src_index, + dst_index, + } => (src_index, dst_index), + QueueFamilyOwnershipTransfer::ExclusiveToExternal { src_index } => { + (src_index, ash::vk::QUEUE_FAMILY_EXTERNAL) + } + QueueFamilyOwnershipTransfer::ExclusiveFromExternal { dst_index } => { + (ash::vk::QUEUE_FAMILY_EXTERNAL, dst_index) + } + QueueFamilyOwnershipTransfer::ExclusiveToForeign { src_index } => { + (src_index, ash::vk::QUEUE_FAMILY_FOREIGN_EXT) + } + QueueFamilyOwnershipTransfer::ExclusiveFromForeign { dst_index } => { + (ash::vk::QUEUE_FAMILY_FOREIGN_EXT, dst_index) + } + QueueFamilyOwnershipTransfer::ConcurrentToExternal => ( + ash::vk::QUEUE_FAMILY_IGNORED, + ash::vk::QUEUE_FAMILY_EXTERNAL, + ), + QueueFamilyOwnershipTransfer::ConcurrentFromExternal => ( + ash::vk::QUEUE_FAMILY_EXTERNAL, + ash::vk::QUEUE_FAMILY_IGNORED, + ), + QueueFamilyOwnershipTransfer::ConcurrentToForeign => ( + ash::vk::QUEUE_FAMILY_IGNORED, + ash::vk::QUEUE_FAMILY_FOREIGN_EXT, + ), + QueueFamilyOwnershipTransfer::ConcurrentFromForeign => ( + ash::vk::QUEUE_FAMILY_FOREIGN_EXT, + ash::vk::QUEUE_FAMILY_IGNORED, + ), + } + } +} |