// Copyright (c) 2017 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use 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};

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()
        }

        /// 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();
            }

            if self.intersects(PipelineStages::ALL_GRAPHICS) {
                self -= PipelineStages::ALL_GRAPHICS;
                self |= QueueFlags::GRAPHICS.into();
            }

            if self.intersects(PipelineStages::VERTEX_INPUT) {
                self -= PipelineStages::VERTEX_INPUT;
                self |= PipelineStages::INDEX_INPUT | PipelineStages::VERTEX_ATTRIBUTE_INPUT;
            }

            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;
            }

            if self.intersects(PipelineStages::ALL_TRANSFER) {
                self -= PipelineStages::ALL_TRANSFER;
                self |= PipelineStages::COPY
                    | PipelineStages::RESOLVE
                    | PipelineStages::BLIT
                    | PipelineStages::CLEAR
                    | PipelineStages::ACCELERATION_STRUCTURE_COPY;
            }

            self
        }

        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] = [
            $(
                (
                    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;
        }

        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 {
    #[inline]
    fn from(val: PipelineStage) -> Self {
        Self::from_raw(val as u32)
    }
}

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()
        }

        /// 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;
            }

            if self.intersects(AccessFlags::SHADER_WRITE) {
                self -= AccessFlags::SHADER_WRITE;
                self |= AccessFlags::SHADER_STORAGE_WRITE;
            }

            self
        }
    }
    = AccessFlags2(u64);

    /// Read access to an indirect buffer.
    INDIRECT_COMMAND_READ = INDIRECT_COMMAND_READ,

    /// Read access to an index buffer.
    INDEX_READ = INDEX_READ,

    /// 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],
    },

    /// 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 From<PipelineStages> for AccessFlags {
    /// Corresponds to the table "[Supported access types]" in the Vulkan specification.
    ///
    /// [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();
        }

        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 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 val.intersects(PipelineStages::FRAGMENT_SHADER | PipelineStages::SUBPASS_SHADING) {
            result |= AccessFlags::INPUT_ATTACHMENT_READ;
        }

        if val
            .intersects(PipelineStages::EARLY_FRAGMENT_TESTS | PipelineStages::LATE_FRAGMENT_TESTS)
        {
            result |= AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ
                | AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE;
        }

        if val.intersects(PipelineStages::COLOR_ATTACHMENT_OUTPUT) {
            result |= AccessFlags::COLOR_ATTACHMENT_READ
                | AccessFlags::COLOR_ATTACHMENT_WRITE
                | AccessFlags::COLOR_ATTACHMENT_READ_NONCOHERENT;
        }

        if val.intersects(PipelineStages::HOST) {
            result |= AccessFlags::HOST_READ | AccessFlags::HOST_WRITE;
        }

        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 val.intersects(PipelineStages::INDEX_INPUT) {
            result |= AccessFlags::INDEX_READ;
        }

        if val.intersects(PipelineStages::VERTEX_ATTRIBUTE_INPUT) {
            result |= AccessFlags::VERTEX_ATTRIBUTE_READ;
        }

        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, Default, PartialEq, Eq)]
pub struct PipelineMemoryAccess {
    /// The pipeline stages the resource will be accessed in.
    pub stages: PipelineStages,
    /// The type of memory access that will be performed.
    pub access: AccessFlags,
    /// 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,
            ),
        }
    }
}
