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

use super::{
    AttachmentDescription, AttachmentReference, LoadOp, RenderPass, RenderPassCreateInfo,
    SubpassDependency, SubpassDescription,
};
use crate::{
    device::Device,
    format::FormatFeatures,
    image::{ImageAspects, ImageLayout, SampleCount},
    sync::{AccessFlags, DependencyFlags, PipelineStages},
    OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
};
use smallvec::SmallVec;
use std::{
    error::Error,
    fmt::{Display, Error as FmtError, Formatter},
    mem::MaybeUninit,
    ptr,
};

impl RenderPass {
    pub(super) fn validate(
        device: &Device,
        create_info: &mut RenderPassCreateInfo,
    ) -> Result<(), RenderPassCreationError> {
        let properties = device.physical_device().properties();

        let RenderPassCreateInfo {
            attachments,
            subpasses,
            dependencies,
            correlated_view_masks,
            _ne: _,
        } = create_info;

        /*
            Attachments
        */

        let mut attachment_potential_format_features = Vec::with_capacity(attachments.len());

        for (atch_num, attachment) in attachments.iter().enumerate() {
            let &AttachmentDescription {
                format,
                samples,
                load_op,
                store_op,
                stencil_load_op,
                stencil_store_op,
                initial_layout,
                final_layout,
                _ne: _,
            } = attachment;
            let atch_num = atch_num as u32;

            // VUID-VkAttachmentDescription2-finalLayout-03061
            if matches!(
                final_layout,
                ImageLayout::Undefined | ImageLayout::Preinitialized
            ) {
                return Err(RenderPassCreationError::AttachmentLayoutInvalid {
                    attachment: atch_num,
                });
            }

            let format = format.unwrap();
            let aspects = format.aspects();

            // VUID-VkAttachmentDescription2-format-parameter
            format.validate_device(device)?;

            // VUID-VkAttachmentDescription2-samples-parameter
            samples.validate_device(device)?;

            for load_op in [load_op, stencil_load_op] {
                // VUID-VkAttachmentDescription2-loadOp-parameter
                // VUID-VkAttachmentDescription2-stencilLoadOp-parameter
                load_op.validate_device(device)?;
            }

            for store_op in [store_op, stencil_store_op] {
                // VUID-VkAttachmentDescription2-storeOp-parameter
                // VUID-VkAttachmentDescription2-stencilStoreOp-parameter
                store_op.validate_device(device)?;
            }

            for layout in [initial_layout, final_layout] {
                // VUID-VkAttachmentDescription2-initialLayout-parameter
                // VUID-VkAttachmentDescription2-finalLayout-parameter
                layout.validate_device(device)?;

                if aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
                    // VUID-VkAttachmentDescription2-format-03281
                    // VUID-VkAttachmentDescription2-format-03283
                    if matches!(layout, ImageLayout::ColorAttachmentOptimal) {
                        return Err(RenderPassCreationError::AttachmentLayoutInvalid {
                            attachment: atch_num,
                        });
                    }
                } else {
                    // VUID-VkAttachmentDescription2-format-03280
                    // VUID-VkAttachmentDescription2-format-03282
                    // VUID-VkAttachmentDescription2-format-06487
                    // VUID-VkAttachmentDescription2-format-06488
                    if matches!(
                        layout,
                        ImageLayout::DepthStencilAttachmentOptimal
                            | ImageLayout::DepthStencilReadOnlyOptimal
                            | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
                            | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
                    ) {
                        return Err(RenderPassCreationError::AttachmentLayoutInvalid {
                            attachment: atch_num,
                        });
                    }
                }
            }

            // Use unchecked, because all validation has been done above.
            attachment_potential_format_features.push(unsafe {
                device
                    .physical_device()
                    .format_properties_unchecked(format)
                    .potential_format_features()
            });
        }

        /*
            Subpasses
        */

        // VUID-VkRenderPassCreateInfo2-subpassCount-arraylength
        assert!(!subpasses.is_empty());

        let is_multiview = subpasses[0].view_mask != 0;

        // VUID-VkSubpassDescription2-multiview-06558
        if is_multiview && !device.enabled_features().multiview {
            return Err(RenderPassCreationError::RequirementNotMet {
                required_for: "`create_info.subpasses[0].view_mask` is not `0`",
                requires_one_of: RequiresOneOf {
                    features: &["multiview"],
                    ..Default::default()
                },
            });
        }

        let mut attachment_used = vec![false; attachments.len()];

        for (subpass_num, subpass) in subpasses.iter_mut().enumerate() {
            let &mut SubpassDescription {
                view_mask,
                ref mut input_attachments,
                ref color_attachments,
                ref resolve_attachments,
                ref depth_stencil_attachment,
                ref preserve_attachments,
                _ne: _,
            } = subpass;
            let subpass_num = subpass_num as u32;

            // VUID-VkRenderPassCreateInfo2-viewMask-03058
            if (view_mask != 0) != is_multiview {
                return Err(RenderPassCreationError::SubpassMultiviewMismatch {
                    subpass: subpass_num,
                    multiview: subpass.view_mask != 0,
                    first_subpass_multiview: is_multiview,
                });
            }

            let view_count = u32::BITS - view_mask.leading_zeros();

            // VUID-VkSubpassDescription2-viewMask-06706
            if view_count > properties.max_multiview_view_count.unwrap_or(0) {
                return Err(
                    RenderPassCreationError::SubpassMaxMultiviewViewCountExceeded {
                        subpass: subpass_num,
                        view_count,
                        max: properties.max_multiview_view_count.unwrap_or(0),
                    },
                );
            }

            // VUID-VkSubpassDescription2-colorAttachmentCount-03063
            if color_attachments.len() as u32 > properties.max_color_attachments {
                return Err(
                    RenderPassCreationError::SubpassMaxColorAttachmentsExceeded {
                        subpass: subpass_num,
                        color_attachment_count: color_attachments.len() as u32,
                        max: properties.max_color_attachments,
                    },
                );
            }

            // Track the layout of each attachment used in this subpass
            let mut layouts = vec![None; attachments.len()];

            // Common checks for all attachment types
            let mut check_attachment = |atch_ref: &AttachmentReference| {
                // VUID-VkAttachmentReference2-layout-parameter
                atch_ref.layout.validate_device(device)?;

                // VUID?
                atch_ref.aspects.validate_device(device)?;

                // VUID-VkRenderPassCreateInfo2-attachment-03051
                let atch = attachments.get(atch_ref.attachment as usize).ok_or(
                    RenderPassCreationError::SubpassAttachmentOutOfRange {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                    },
                )?;

                // VUID-VkSubpassDescription2-layout-02528
                match &mut layouts[atch_ref.attachment as usize] {
                    Some(layout) if *layout == atch_ref.layout => (),
                    Some(_) => {
                        return Err(RenderPassCreationError::SubpassAttachmentLayoutMismatch {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                        })
                    }
                    layout @ None => *layout = Some(atch_ref.layout),
                }

                let first_use =
                    !std::mem::replace(&mut attachment_used[atch_ref.attachment as usize], true);

                if first_use {
                    // VUID-VkRenderPassCreateInfo2-pAttachments-02522
                    if atch.load_op == LoadOp::Clear
                        && matches!(
                            atch_ref.layout,
                            ImageLayout::ShaderReadOnlyOptimal
                                | ImageLayout::DepthStencilReadOnlyOptimal
                                | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
                        )
                    {
                        return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid {
                            attachment: atch_ref.attachment,
                            first_use_subpass: subpass_num,
                        });
                    }

                    // VUID-VkRenderPassCreateInfo2-pAttachments-02523
                    if atch.stencil_load_op == LoadOp::Clear
                        && matches!(
                            atch_ref.layout,
                            ImageLayout::ShaderReadOnlyOptimal
                                | ImageLayout::DepthStencilReadOnlyOptimal
                                | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
                        )
                    {
                        return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid {
                            attachment: atch_ref.attachment,
                            first_use_subpass: subpass_num,
                        });
                    }
                }

                let potential_format_features =
                    &attachment_potential_format_features[atch_ref.attachment as usize];

                Ok((atch, potential_format_features, first_use))
            };

            /*
                Check color attachments
            */

            let mut color_samples = None;

            for atch_ref in color_attachments.iter().flatten() {
                let (atch, features, _first_use) = check_attachment(atch_ref)?;

                // VUID-VkSubpassDescription2-pColorAttachments-02898
                if !features.intersects(FormatFeatures::COLOR_ATTACHMENT) {
                    return Err(
                        RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                            usage: "color",
                        },
                    );
                }

                // VUID-VkAttachmentReference2-layout-03077
                // VUID-VkSubpassDescription2-attachment-06913
                // VUID-VkSubpassDescription2-attachment-06916
                if matches!(
                    atch_ref.layout,
                    ImageLayout::Undefined
                        | ImageLayout::Preinitialized
                        | ImageLayout::PresentSrc
                        | ImageLayout::DepthStencilAttachmentOptimal
                        | ImageLayout::ShaderReadOnlyOptimal
                        | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
                        | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
                ) {
                    return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                        usage: "color",
                    });
                }

                // Not required by spec, but enforced by Vulkano for sanity.
                if !atch_ref.aspects.is_empty() {
                    return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                    });
                }

                // VUID-VkSubpassDescription2-pColorAttachments-03069
                match &mut color_samples {
                    Some(samples) if *samples == atch.samples => (),
                    Some(samples) => {
                        return Err(
                            RenderPassCreationError::SubpassColorDepthStencilAttachmentSamplesMismatch {
                                subpass: subpass_num,
                                attachment: atch_ref.attachment,
                                samples: atch.samples,
                                first_samples: *samples,
                            },
                        )
                    }
                    samples @ None => *samples = Some(atch.samples),
                }
            }

            /*
                Check depth/stencil attachment
            */

            if let Some(atch_ref) = depth_stencil_attachment.as_ref() {
                let (atch, features, _first_use) = check_attachment(atch_ref)?;

                // VUID-VkSubpassDescription2-pDepthStencilAttachment-02900
                if !features.intersects(FormatFeatures::DEPTH_STENCIL_ATTACHMENT) {
                    return Err(
                        RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                            usage: "depth/stencil",
                        },
                    );
                }

                // VUID-VkAttachmentReference2-layout-03077
                // VUID-VkSubpassDescription2-attachment-06915
                if matches!(
                    atch_ref.layout,
                    ImageLayout::Undefined
                        | ImageLayout::Preinitialized
                        | ImageLayout::PresentSrc
                        | ImageLayout::ColorAttachmentOptimal
                        | ImageLayout::ShaderReadOnlyOptimal
                ) {
                    return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                        usage: "depth/stencil",
                    });
                }

                // Not required by spec, but enforced by Vulkano for sanity.
                if !atch_ref.aspects.is_empty() {
                    return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                    });
                }

                // VUID-VkSubpassDescription2-pDepthStencilAttachment-04440
                if color_attachments
                    .iter()
                    .flatten()
                    .any(|color_atch_ref| color_atch_ref.attachment == atch_ref.attachment)
                {
                    return Err(
                        RenderPassCreationError::SubpassAttachmentUsageColorDepthStencil {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                        },
                    );
                }

                // VUID-VkSubpassDescription2-pDepthStencilAttachment-03071
                if let Some(samples) = color_samples.filter(|samples| *samples != atch.samples) {
                    return Err(
                        RenderPassCreationError::SubpassColorDepthStencilAttachmentSamplesMismatch {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                            samples: atch.samples,
                            first_samples: samples,
                        },
                    );
                }
            }

            /*
                Check input attachments
                This must be placed after color and depth/stencil checks so that `first_use`
                will be true for VUID-VkSubpassDescription2-loadOp-03064.
            */

            for atch_ref in input_attachments.iter_mut().flatten() {
                let (atch, features, first_use) = check_attachment(atch_ref)?;

                // VUID-VkSubpassDescription2-pInputAttachments-02897
                if !features.intersects(
                    FormatFeatures::COLOR_ATTACHMENT | FormatFeatures::DEPTH_STENCIL_ATTACHMENT,
                ) {
                    return Err(
                        RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                            usage: "input",
                        },
                    );
                }

                // VUID-VkAttachmentReference2-layout-03077
                // VUID-VkSubpassDescription2-attachment-06912
                if matches!(
                    atch_ref.layout,
                    ImageLayout::Undefined
                        | ImageLayout::Preinitialized
                        | ImageLayout::PresentSrc
                        | ImageLayout::ColorAttachmentOptimal
                        | ImageLayout::DepthStencilAttachmentOptimal
                ) {
                    return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                        usage: "input",
                    });
                }

                let atch_aspects = atch.format.unwrap().aspects();

                if atch_ref.aspects.is_empty() {
                    // VUID-VkSubpassDescription2-attachment-02800
                    atch_ref.aspects = atch_aspects;
                } else if atch_ref.aspects != atch_aspects {
                    if !(device.api_version() >= Version::V1_1
                        || device.enabled_extensions().khr_create_renderpass2
                        || device.enabled_extensions().khr_maintenance2)
                    {
                        return Err(RenderPassCreationError::RequirementNotMet {
                            required_for: "`create_info.subpasses` has an element, where \
                                `input_attachments` has an element that is `Some(atch_ref)`, \
                                where `atch_ref.aspects` does not match the aspects of the \
                                attachment itself",
                            requires_one_of: RequiresOneOf {
                                api_version: Some(Version::V1_1),
                                device_extensions: &["khr_create_renderpass2", "khr_maintenance2"],
                                ..Default::default()
                            },
                        });
                    }

                    // VUID-VkSubpassDescription2-attachment-02801
                    // VUID-VkSubpassDescription2-attachment-04563
                    // VUID-VkRenderPassCreateInfo2-attachment-02525
                    if !atch_aspects.contains(atch_ref.aspects) {
                        return Err(
                            RenderPassCreationError::SubpassInputAttachmentAspectsNotCompatible {
                                subpass: subpass_num,
                                attachment: atch_ref.attachment,
                            },
                        );
                    }
                }

                // VUID-VkSubpassDescription2-loadOp-03064
                if first_use && atch.load_op == LoadOp::Clear {
                    return Err(RenderPassCreationError::AttachmentFirstUseLoadOpInvalid {
                        attachment: atch_ref.attachment,
                        first_use_subpass: subpass_num,
                    });
                }
            }

            /*
                Check resolve attachments
            */

            // VUID-VkSubpassDescription2-pResolveAttachments-parameter
            if !(resolve_attachments.is_empty()
                || resolve_attachments.len() == color_attachments.len())
            {
                return Err(
                    RenderPassCreationError::SubpassResolveAttachmentsColorAttachmentsLenMismatch {
                        subpass: subpass_num,
                    },
                );
            }

            for (atch_ref, color_atch_ref) in resolve_attachments
                .iter()
                .zip(subpass.color_attachments.iter())
                .filter_map(|(r, c)| r.as_ref().map(|r| (r, c.as_ref())))
            {
                let (atch, features, _first_use) = check_attachment(atch_ref)?;

                // VUID-VkSubpassDescription2-pResolveAttachments-02899
                if !features.intersects(FormatFeatures::COLOR_ATTACHMENT) {
                    return Err(
                        RenderPassCreationError::SubpassAttachmentFormatUsageNotSupported {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                            usage: "resolve",
                        },
                    );
                }

                // VUID-VkAttachmentReference2-layout-03077
                // VUID-VkSubpassDescription2-attachment-06914
                // VUID-VkSubpassDescription2-attachment-06917
                if matches!(
                    atch_ref.layout,
                    ImageLayout::Undefined
                        | ImageLayout::Preinitialized
                        | ImageLayout::PresentSrc
                        | ImageLayout::DepthStencilAttachmentOptimal
                        | ImageLayout::ShaderReadOnlyOptimal
                        | ImageLayout::DepthAttachmentStencilReadOnlyOptimal
                        | ImageLayout::DepthReadOnlyStencilAttachmentOptimal
                ) {
                    return Err(RenderPassCreationError::SubpassAttachmentLayoutInvalid {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                        usage: "resolve",
                    });
                }

                // Not required by spec, but enforced by Vulkano for sanity.
                if !atch_ref.aspects.is_empty() {
                    return Err(RenderPassCreationError::SubpassAttachmentAspectsNotEmpty {
                        subpass: subpass_num,
                        attachment: atch_ref.attachment,
                    });
                }

                // VUID-VkSubpassDescription2-pResolveAttachments-03065
                let color_atch_ref = color_atch_ref.ok_or({
                    RenderPassCreationError::SubpassResolveAttachmentWithoutColorAttachment {
                        subpass: subpass_num,
                    }
                })?;
                let color_atch = &attachments[color_atch_ref.attachment as usize];

                // VUID-VkSubpassDescription2-pResolveAttachments-03067
                if atch.samples != SampleCount::Sample1 {
                    return Err(
                        RenderPassCreationError::SubpassResolveAttachmentMultisampled {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                        },
                    );
                }

                // VUID-VkSubpassDescription2-pResolveAttachments-03066
                if color_atch.samples == SampleCount::Sample1 {
                    return Err(
                        RenderPassCreationError::SubpassColorAttachmentWithResolveNotMultisampled {
                            subpass: subpass_num,
                            attachment: atch_ref.attachment,
                        },
                    );
                }

                // VUID-VkSubpassDescription2-pResolveAttachments-03068
                if atch.format != color_atch.format {
                    return Err(
                        RenderPassCreationError::SubpassResolveAttachmentFormatMismatch {
                            subpass: subpass_num,
                            resolve_attachment: atch_ref.attachment,
                            color_attachment: color_atch_ref.attachment,
                        },
                    );
                }
            }

            /*
                Check preserve attachments
            */

            for &atch in preserve_attachments {
                // VUID-VkRenderPassCreateInfo2-attachment-03051
                if atch as usize >= attachments.len() {
                    return Err(RenderPassCreationError::SubpassAttachmentOutOfRange {
                        subpass: subpass_num,
                        attachment: atch,
                    });
                }

                // VUID-VkSubpassDescription2-pPreserveAttachments-03074
                if layouts[atch as usize].is_some() {
                    return Err(
                        RenderPassCreationError::SubpassPreserveAttachmentUsedElsewhere {
                            subpass: subpass_num,
                            attachment: atch,
                        },
                    );
                }
            }
        }

        /*
            Dependencies
        */

        for (dependency_num, dependency) in dependencies.iter().enumerate() {
            let &SubpassDependency {
                src_subpass,
                dst_subpass,
                src_stages,
                dst_stages,
                src_access,
                dst_access,
                dependency_flags,
                view_offset,
                _ne: _,
            } = dependency;
            let dependency_num = dependency_num as u32;

            for (stages, access) in [(src_stages, src_access), (dst_stages, dst_access)] {
                if !device.enabled_features().synchronization2 {
                    if stages.is_2() {
                        return Err(RenderPassCreationError::RequirementNotMet {
                            required_for: "`create_info.dependencies` has an element where \
                                `src_stages` or `dst_stages` contains flags from \
                                `VkPipelineStageFlagBits2`",
                            requires_one_of: RequiresOneOf {
                                features: &["synchronization2"],
                                ..Default::default()
                            },
                        });
                    }

                    if access.is_2() {
                        return Err(RenderPassCreationError::RequirementNotMet {
                            required_for: "`create_info.dependencies` has an element where \
                                `src_access` or `dst_access` contains flags from \
                                `VkAccessFlagBits2`",
                            requires_one_of: RequiresOneOf {
                                features: &["synchronization2"],
                                ..Default::default()
                            },
                        });
                    }
                } else if !(device.api_version() >= Version::V1_2
                    || device.enabled_extensions().khr_create_renderpass2)
                {
                    // If synchronization2 is enabled but we don't have create_renderpass2,
                    // we are unable to use extension structs, so we can't use the
                    // extra flag bits.

                    if stages.is_2() {
                        return Err(RenderPassCreationError::RequirementNotMet {
                            required_for: "`create_info.dependencies` has an element where \
                                `src_stages` or `dst_stages` contains flags from \
                                `VkPipelineStageFlagBits2`",
                            requires_one_of: RequiresOneOf {
                                api_version: Some(Version::V1_2),
                                device_extensions: &["khr_create_renderpass2"],
                                ..Default::default()
                            },
                        });
                    }

                    if access.is_2() {
                        return Err(RenderPassCreationError::RequirementNotMet {
                            required_for: "`create_info.dependencies` has an element where \
                                `src_access` or `dst_access` contains flags from \
                                `VkAccessFlagBits2`",
                            requires_one_of: RequiresOneOf {
                                api_version: Some(Version::V1_2),
                                device_extensions: &["khr_create_renderpass2"],
                                ..Default::default()
                            },
                        });
                    }
                }

                // VUID-VkMemoryBarrier2-srcStageMask-parameter
                // VUID-VkMemoryBarrier2-dstStageMask-parameter
                stages.validate_device(device)?;

                // VUID-VkMemoryBarrier2-srcAccessMask-parameter
                // VUID-VkMemoryBarrier2-dstAccessMask-parameter
                access.validate_device(device)?;

                // VUID-VkMemoryBarrier2-srcStageMask-03929
                // VUID-VkMemoryBarrier2-dstStageMask-03929
                if stages.intersects(PipelineStages::GEOMETRY_SHADER)
                    && !device.enabled_features().geometry_shader
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::GEOMETRY_SHADER`",
                        requires_one_of: RequiresOneOf {
                            features: &["geometry_shader"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-03930
                // VUID-VkMemoryBarrier2-dstStageMask-03930
                if stages.intersects(
                    PipelineStages::TESSELLATION_CONTROL_SHADER
                        | PipelineStages::TESSELLATION_EVALUATION_SHADER,
                ) && !device.enabled_features().tessellation_shader
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::TESSELLATION_CONTROL_SHADER` or \
                            `PipelineStages::TESSELLATION_EVALUATION_SHADER`",
                        requires_one_of: RequiresOneOf {
                            features: &["tessellation_shader"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-03931
                // VUID-VkMemoryBarrier2-dstStageMask-03931
                if stages.intersects(PipelineStages::CONDITIONAL_RENDERING)
                    && !device.enabled_features().conditional_rendering
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::CONDITIONAL_RENDERING`",
                        requires_one_of: RequiresOneOf {
                            features: &["conditional_rendering"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-03932
                // VUID-VkMemoryBarrier2-dstStageMask-03932
                if stages.intersects(PipelineStages::FRAGMENT_DENSITY_PROCESS)
                    && !device.enabled_features().fragment_density_map
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::FRAGMENT_DENSITY_PROCESS`",
                        requires_one_of: RequiresOneOf {
                            features: &["fragment_density_map"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-03933
                // VUID-VkMemoryBarrier2-dstStageMask-03933
                if stages.intersects(PipelineStages::TRANSFORM_FEEDBACK)
                    && !device.enabled_features().transform_feedback
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::TRANSFORM_FEEDBACK`",
                        requires_one_of: RequiresOneOf {
                            features: &["transform_feedback"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-03934
                // VUID-VkMemoryBarrier2-dstStageMask-03934
                if stages.intersects(PipelineStages::MESH_SHADER)
                    && !device.enabled_features().mesh_shader
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::MESH_SHADER`",
                        requires_one_of: RequiresOneOf {
                            features: &["mesh_shader"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-03935
                // VUID-VkMemoryBarrier2-dstStageMask-03935
                if stages.intersects(PipelineStages::TASK_SHADER)
                    && !device.enabled_features().task_shader
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::TASK_SHADER`",
                        requires_one_of: RequiresOneOf {
                            features: &["task_shader"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-shadingRateImage-07316
                // VUID-VkMemoryBarrier2-shadingRateImage-07316
                if stages.intersects(PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT)
                    && !(device.enabled_features().attachment_fragment_shading_rate
                        || device.enabled_features().shading_rate_image)
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT`",
                        requires_one_of: RequiresOneOf {
                            features: &["attachment_fragment_shading_rate", "shading_rate_image"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-04957
                // VUID-VkMemoryBarrier2-dstStageMask-04957
                if stages.intersects(PipelineStages::SUBPASS_SHADING)
                    && !device.enabled_features().subpass_shading
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::SUBPASS_SHADING`",
                        requires_one_of: RequiresOneOf {
                            features: &["subpass_shading"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkMemoryBarrier2-srcStageMask-04995
                // VUID-VkMemoryBarrier2-dstStageMask-04995
                if stages.intersects(PipelineStages::INVOCATION_MASK)
                    && !device.enabled_features().invocation_mask
                {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            contains `PipelineStages::INVOCATION_MASK`",
                        requires_one_of: RequiresOneOf {
                            features: &["invocation_mask"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkSubpassDependency2-srcStageMask-03937
                // VUID-VkSubpassDependency2-dstStageMask-03937
                if stages.is_empty() && !device.enabled_features().synchronization2 {
                    return Err(RenderPassCreationError::RequirementNotMet {
                        required_for: "`create_info.dependencies` has an element where `stages` \
                            is empty",
                        requires_one_of: RequiresOneOf {
                            features: &["synchronization2"],
                            ..Default::default()
                        },
                    });
                }

                // VUID-VkSubpassDependency2-srcAccessMask-03088
                // VUID-VkSubpassDependency2-dstAccessMask-03089
                if !AccessFlags::from(stages).contains(access) {
                    return Err(
                        RenderPassCreationError::DependencyAccessNotSupportedByStages {
                            dependency: dependency_num,
                        },
                    );
                }
            }

            if dependency_flags.intersects(DependencyFlags::VIEW_LOCAL) {
                // VUID-VkRenderPassCreateInfo2-viewMask-03059
                if !is_multiview {
                    return Err(
                        RenderPassCreationError::DependencyViewLocalMultiviewNotEnabled {
                            dependency: dependency_num,
                        },
                    );
                }
            } else {
                // VUID-VkSubpassDependency2-dependencyFlags-03092
                if view_offset != 0 {
                    return Err(
                        RenderPassCreationError::DependencyViewOffzetNonzeroWithoutViewLocal {
                            dependency: dependency_num,
                        },
                    );
                }
            }

            // VUID-VkSubpassDependency2-srcSubpass-03085
            if src_subpass.is_none() && dst_subpass.is_none() {
                return Err(RenderPassCreationError::DependencyBothSubpassesExternal {
                    dependency: dependency_num,
                });
            }

            for (subpass, stages) in [(src_subpass, src_stages), (dst_subpass, dst_stages)] {
                if let Some(subpass) = subpass {
                    // VUID-VkRenderPassCreateInfo2-srcSubpass-02526
                    // VUID-VkRenderPassCreateInfo2-dstSubpass-02527
                    if subpass as usize >= subpasses.len() {
                        return Err(RenderPassCreationError::DependencySubpassOutOfRange {
                            dependency: dependency_num,
                            subpass,
                        });
                    }

                    let remaining_stages = stages
                        - (PipelineStages::DRAW_INDIRECT
                            | PipelineStages::INDEX_INPUT
                            | PipelineStages::VERTEX_ATTRIBUTE_INPUT
                            | PipelineStages::VERTEX_SHADER
                            | PipelineStages::TESSELLATION_CONTROL_SHADER
                            | PipelineStages::TESSELLATION_EVALUATION_SHADER
                            | PipelineStages::GEOMETRY_SHADER
                            | PipelineStages::TRANSFORM_FEEDBACK
                            | PipelineStages::FRAGMENT_SHADING_RATE_ATTACHMENT
                            | PipelineStages::EARLY_FRAGMENT_TESTS
                            | PipelineStages::FRAGMENT_SHADER
                            | PipelineStages::LATE_FRAGMENT_TESTS
                            | PipelineStages::COLOR_ATTACHMENT_OUTPUT
                            | PipelineStages::ALL_GRAPHICS);

                    // VUID-VkRenderPassCreateInfo2-pDependencies-03054
                    // VUID-VkRenderPassCreateInfo2-pDependencies-03055
                    if !remaining_stages.is_empty() {
                        return Err(RenderPassCreationError::DependencyStageNotSupported {
                            dependency: dependency_num,
                        });
                    }
                } else {
                    // VUID-VkSubpassDependency2-dependencyFlags-03090
                    // VUID-VkSubpassDependency2-dependencyFlags-03091
                    if dependency_flags.intersects(DependencyFlags::VIEW_LOCAL) {
                        return Err(
                            RenderPassCreationError::DependencyViewLocalExternalDependency {
                                dependency: dependency_num,
                            },
                        );
                    }
                }
            }

            if let (Some(src_subpass), Some(dst_subpass)) = (src_subpass, dst_subpass) {
                // VUID-VkSubpassDependency2-srcSubpass-03084
                if src_subpass > dst_subpass {
                    return Err(
                        RenderPassCreationError::DependencySourceSubpassAfterDestinationSubpass {
                            dependency: dependency_num,
                        },
                    );
                }

                if src_subpass == dst_subpass {
                    let framebuffer_stages = PipelineStages::EARLY_FRAGMENT_TESTS
                        | PipelineStages::FRAGMENT_SHADER
                        | PipelineStages::LATE_FRAGMENT_TESTS
                        | PipelineStages::COLOR_ATTACHMENT_OUTPUT;

                    // VUID-VkSubpassDependency2-srcSubpass-06810
                    if src_stages.intersects(framebuffer_stages)
                        && !(dst_stages - framebuffer_stages).is_empty()
                    {
                        return Err(
                            RenderPassCreationError::DependencySelfDependencySourceStageAfterDestinationStage {
                                dependency: dependency_num,
                            },
                        );
                    }

                    // VUID-VkSubpassDependency2-srcSubpass-02245
                    if src_stages.intersects(framebuffer_stages)
                        && dst_stages.intersects(framebuffer_stages)
                        && !dependency_flags.intersects(DependencyFlags::BY_REGION)
                    {
                        return Err(
                            RenderPassCreationError::DependencySelfDependencyFramebufferStagesWithoutByRegion {
                                dependency: dependency_num,
                            },
                        );
                    }

                    if dependency_flags.intersects(DependencyFlags::VIEW_LOCAL) {
                        // VUID-VkSubpassDependency2-viewOffset-02530
                        if view_offset != 0 {
                            return Err(
                                RenderPassCreationError::DependencySelfDependencyViewLocalNonzeroViewOffset {
                                    dependency: dependency_num,
                                },
                            );
                        }
                    } else {
                        // VUID-VkRenderPassCreateInfo2-pDependencies-03060
                        if subpasses[src_subpass as usize].view_mask.count_ones() > 1 {
                            return Err(
                                RenderPassCreationError::DependencySelfDependencyViewMaskMultiple {
                                    dependency: dependency_num,
                                    subpass: src_subpass,
                                },
                            );
                        }
                    }
                }
            }
        }

        /*
            Correlated view masks
        */

        // VUID-VkRenderPassCreateInfo2-viewMask-03057
        if !correlated_view_masks.is_empty() {
            if !is_multiview {
                return Err(RenderPassCreationError::CorrelatedViewMasksMultiviewNotEnabled);
            }

            // VUID-VkRenderPassCreateInfo2-pCorrelatedViewMasks-03056
            correlated_view_masks.iter().try_fold(0, |total, &mask| {
                if total & mask != 0 {
                    Err(RenderPassCreationError::CorrelatedViewMasksOverlapping)
                } else {
                    Ok(total | mask)
                }
            })?;
        }

        Ok(())
    }

    pub(super) unsafe fn create_v2(
        device: &Device,
        create_info: &RenderPassCreateInfo,
    ) -> Result<ash::vk::RenderPass, RenderPassCreationError> {
        let RenderPassCreateInfo {
            attachments,
            subpasses,
            dependencies,
            correlated_view_masks,
            _ne: _,
        } = create_info;

        let attachments_vk = attachments
            .iter()
            .map(|attachment| ash::vk::AttachmentDescription2 {
                flags: ash::vk::AttachmentDescriptionFlags::empty(),
                format: attachment
                    .format
                    .map_or(ash::vk::Format::UNDEFINED, |f| f.into()),
                samples: attachment.samples.into(),
                load_op: attachment.load_op.into(),
                store_op: attachment.store_op.into(),
                stencil_load_op: attachment.stencil_load_op.into(),
                stencil_store_op: attachment.stencil_store_op.into(),
                initial_layout: attachment.initial_layout.into(),
                final_layout: attachment.final_layout.into(),
                ..Default::default()
            })
            .collect::<SmallVec<[_; 4]>>();

        let attachment_references_vk = subpasses
            .iter()
            .flat_map(|subpass| {
                (subpass.input_attachments.iter())
                    .chain(subpass.color_attachments.iter())
                    .chain(subpass.resolve_attachments.iter())
                    .map(Option::as_ref)
                    .chain(subpass.depth_stencil_attachment.iter().map(Some))
                    .map(|atch_ref| {
                        if let Some(atch_ref) = atch_ref {
                            ash::vk::AttachmentReference2 {
                                attachment: atch_ref.attachment,
                                layout: atch_ref.layout.into(),
                                aspect_mask: atch_ref.aspects.into(),
                                ..Default::default()
                            }
                        } else {
                            ash::vk::AttachmentReference2 {
                                attachment: ash::vk::ATTACHMENT_UNUSED,
                                ..Default::default()
                            }
                        }
                    })
            })
            .collect::<SmallVec<[_; 8]>>();

        let subpasses_vk = {
            // `ref_index` is increased during the loop and points to the next element to use
            // in `attachment_references_vk`.
            let mut ref_index = 0usize;
            let out: SmallVec<[_; 4]> = subpasses
                .iter()
                .map(|subpass| {
                    let input_attachments = attachment_references_vk.as_ptr().add(ref_index);
                    ref_index += subpass.input_attachments.len();
                    let color_attachments = attachment_references_vk.as_ptr().add(ref_index);
                    ref_index += subpass.color_attachments.len();
                    let resolve_attachments = attachment_references_vk.as_ptr().add(ref_index);
                    ref_index += subpass.resolve_attachments.len();
                    let depth_stencil = if subpass.depth_stencil_attachment.is_some() {
                        let a = attachment_references_vk.as_ptr().add(ref_index);
                        ref_index += 1;
                        a
                    } else {
                        ptr::null()
                    };

                    ash::vk::SubpassDescription2 {
                        flags: ash::vk::SubpassDescriptionFlags::empty(),
                        pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS, // TODO: any need to make this user-specifiable?
                        view_mask: subpass.view_mask,
                        input_attachment_count: subpass.input_attachments.len() as u32,
                        p_input_attachments: if subpass.input_attachments.is_empty() {
                            ptr::null()
                        } else {
                            input_attachments
                        },
                        color_attachment_count: subpass.color_attachments.len() as u32,
                        p_color_attachments: if subpass.color_attachments.is_empty() {
                            ptr::null()
                        } else {
                            color_attachments
                        },
                        p_resolve_attachments: if subpass.resolve_attachments.is_empty() {
                            ptr::null()
                        } else {
                            resolve_attachments
                        },
                        p_depth_stencil_attachment: depth_stencil,
                        preserve_attachment_count: subpass.preserve_attachments.len() as u32,
                        p_preserve_attachments: if subpass.preserve_attachments.is_empty() {
                            ptr::null()
                        } else {
                            subpass.preserve_attachments.as_ptr()
                        },
                        ..Default::default()
                    }
                })
                .collect();

            // If this assertion fails, there's a serious bug in the code above ^.
            debug_assert!(ref_index == attachment_references_vk.len());

            out
        };

        let memory_barriers_vk: SmallVec<[_; 4]> = if device.enabled_features().synchronization2 {
            debug_assert!(
                device.api_version() >= Version::V1_3
                    || device.enabled_extensions().khr_synchronization2
            );
            dependencies
                .iter()
                .map(|dependency| ash::vk::MemoryBarrier2 {
                    src_stage_mask: dependency.src_stages.into(),
                    src_access_mask: dependency.src_access.into(),
                    dst_stage_mask: dependency.dst_stages.into(),
                    dst_access_mask: dependency.dst_access.into(),
                    ..Default::default()
                })
                .collect()
        } else {
            SmallVec::new()
        };

        let dependencies_vk = dependencies
            .iter()
            .enumerate()
            .map(|(index, dependency)| {
                ash::vk::SubpassDependency2 {
                    p_next: memory_barriers_vk
                        .get(index)
                        .map_or(ptr::null(), |mb| mb as *const _ as *const _),
                    src_subpass: dependency.src_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
                    dst_subpass: dependency.dst_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
                    src_stage_mask: dependency.src_stages.into(),
                    dst_stage_mask: dependency.dst_stages.into(),
                    src_access_mask: dependency.src_access.into(),
                    dst_access_mask: dependency.dst_access.into(),
                    dependency_flags: dependency.dependency_flags.into(),
                    // VUID-VkSubpassDependency2-dependencyFlags-03092
                    view_offset: dependency.view_offset,
                    ..Default::default()
                }
            })
            .collect::<SmallVec<[_; 4]>>();

        let create_info = ash::vk::RenderPassCreateInfo2 {
            flags: ash::vk::RenderPassCreateFlags::empty(),
            attachment_count: attachments_vk.len() as u32,
            p_attachments: if attachments_vk.is_empty() {
                ptr::null()
            } else {
                attachments_vk.as_ptr()
            },
            subpass_count: subpasses_vk.len() as u32,
            p_subpasses: if subpasses_vk.is_empty() {
                ptr::null()
            } else {
                subpasses_vk.as_ptr()
            },
            dependency_count: dependencies_vk.len() as u32,
            p_dependencies: if dependencies_vk.is_empty() {
                ptr::null()
            } else {
                dependencies_vk.as_ptr()
            },
            correlated_view_mask_count: correlated_view_masks.len() as u32,
            p_correlated_view_masks: correlated_view_masks.as_ptr(),
            ..Default::default()
        };

        Ok({
            let fns = device.fns();
            let mut output = MaybeUninit::uninit();

            if device.api_version() >= Version::V1_2 {
                (fns.v1_2.create_render_pass2)(
                    device.handle(),
                    &create_info,
                    ptr::null(),
                    output.as_mut_ptr(),
                )
            } else {
                (fns.khr_create_renderpass2.create_render_pass2_khr)(
                    device.handle(),
                    &create_info,
                    ptr::null(),
                    output.as_mut_ptr(),
                )
            }
            .result()
            .map_err(VulkanError::from)?;

            output.assume_init()
        })
    }

    pub(super) unsafe fn create_v1(
        device: &Device,
        create_info: &RenderPassCreateInfo,
    ) -> Result<ash::vk::RenderPass, RenderPassCreationError> {
        let RenderPassCreateInfo {
            attachments,
            subpasses,
            dependencies,
            correlated_view_masks,
            _ne: _,
        } = create_info;

        let attachments_vk = attachments
            .iter()
            .map(|attachment| ash::vk::AttachmentDescription {
                flags: ash::vk::AttachmentDescriptionFlags::empty(),
                format: attachment
                    .format
                    .map_or(ash::vk::Format::UNDEFINED, |f| f.into()),
                samples: attachment.samples.into(),
                load_op: attachment.load_op.into(),
                store_op: attachment.store_op.into(),
                stencil_load_op: attachment.stencil_load_op.into(),
                stencil_store_op: attachment.stencil_store_op.into(),
                initial_layout: attachment.initial_layout.into(),
                final_layout: attachment.final_layout.into(),
            })
            .collect::<SmallVec<[_; 4]>>();

        let attachment_references_vk = subpasses
            .iter()
            .flat_map(|subpass| {
                (subpass.input_attachments.iter())
                    .chain(subpass.color_attachments.iter())
                    .chain(subpass.resolve_attachments.iter())
                    .map(Option::as_ref)
                    .chain(subpass.depth_stencil_attachment.iter().map(Some))
                    .map(|atch_ref| {
                        if let Some(atch_ref) = atch_ref {
                            ash::vk::AttachmentReference {
                                attachment: atch_ref.attachment,
                                layout: atch_ref.layout.into(),
                            }
                        } else {
                            ash::vk::AttachmentReference {
                                attachment: ash::vk::ATTACHMENT_UNUSED,
                                layout: Default::default(),
                            }
                        }
                    })
            })
            .collect::<SmallVec<[_; 8]>>();

        let subpasses_vk = {
            // `ref_index` is increased during the loop and points to the next element to use
            // in `attachment_references_vk`.
            let mut ref_index = 0usize;
            let out: SmallVec<[_; 4]> = subpasses
                .iter()
                .map(|subpass| {
                    let input_attachments = attachment_references_vk.as_ptr().add(ref_index);
                    ref_index += subpass.input_attachments.len();
                    let color_attachments = attachment_references_vk.as_ptr().add(ref_index);
                    ref_index += subpass.color_attachments.len();
                    let resolve_attachments = attachment_references_vk.as_ptr().add(ref_index);
                    ref_index += subpass.resolve_attachments.len();
                    let depth_stencil = if subpass.depth_stencil_attachment.is_some() {
                        let a = attachment_references_vk.as_ptr().add(ref_index);
                        ref_index += 1;
                        a
                    } else {
                        ptr::null()
                    };

                    ash::vk::SubpassDescription {
                        flags: ash::vk::SubpassDescriptionFlags::empty(),
                        pipeline_bind_point: ash::vk::PipelineBindPoint::GRAPHICS,
                        input_attachment_count: subpass.input_attachments.len() as u32,
                        p_input_attachments: if subpass.input_attachments.is_empty() {
                            ptr::null()
                        } else {
                            input_attachments
                        },
                        color_attachment_count: subpass.color_attachments.len() as u32,
                        p_color_attachments: if subpass.color_attachments.is_empty() {
                            ptr::null()
                        } else {
                            color_attachments
                        },
                        p_resolve_attachments: if subpass.resolve_attachments.is_empty() {
                            ptr::null()
                        } else {
                            resolve_attachments
                        },
                        p_depth_stencil_attachment: depth_stencil,
                        preserve_attachment_count: subpass.preserve_attachments.len() as u32,
                        p_preserve_attachments: if subpass.preserve_attachments.is_empty() {
                            ptr::null()
                        } else {
                            subpass.preserve_attachments.as_ptr()
                        },
                    }
                })
                .collect();

            // If this assertion fails, there's a serious bug in the code above ^.
            debug_assert!(ref_index == attachment_references_vk.len());

            out
        };

        let dependencies_vk = dependencies
            .iter()
            .map(|dependency| ash::vk::SubpassDependency {
                src_subpass: dependency.src_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
                dst_subpass: dependency.dst_subpass.unwrap_or(ash::vk::SUBPASS_EXTERNAL),
                src_stage_mask: dependency.src_stages.into(),
                dst_stage_mask: dependency.dst_stages.into(),
                src_access_mask: dependency.src_access.into(),
                dst_access_mask: dependency.dst_access.into(),
                dependency_flags: dependency.dependency_flags.into(),
            })
            .collect::<SmallVec<[_; 4]>>();

        /* Input attachment aspect */

        let input_attachment_aspect_references: SmallVec<[_; 8]> = if device.api_version()
            >= Version::V1_1
            || device.enabled_extensions().khr_maintenance2
        {
            subpasses
                .iter()
                .enumerate()
                .flat_map(|(subpass_num, subpass)| {
                    subpass.input_attachments.iter().enumerate().flat_map(
                        move |(atch_num, atch_ref)| {
                            atch_ref.as_ref().map(|atch_ref| {
                                ash::vk::InputAttachmentAspectReference {
                                    subpass: subpass_num as u32,
                                    input_attachment_index: atch_num as u32,
                                    aspect_mask: atch_ref.aspects.into(),
                                }
                            })
                        },
                    )
                })
                .collect()
        } else {
            SmallVec::new()
        };

        let mut input_attachment_aspect_create_info =
            if !input_attachment_aspect_references.is_empty() {
                Some(ash::vk::RenderPassInputAttachmentAspectCreateInfo {
                    aspect_reference_count: input_attachment_aspect_references.len() as u32,
                    p_aspect_references: input_attachment_aspect_references.as_ptr(),
                    ..Default::default()
                })
            } else {
                None
            };

        /* Multiview */

        let is_multiview = subpasses[0].view_mask != 0;

        let (multiview_view_masks, multiview_view_offsets): (SmallVec<[_; 4]>, SmallVec<[_; 4]>) =
            if is_multiview {
                (
                    subpasses.iter().map(|subpass| subpass.view_mask).collect(),
                    dependencies
                        .iter()
                        .map(|dependency| dependency.view_offset)
                        .collect(),
                )
            } else {
                (SmallVec::new(), SmallVec::new())
            };

        let mut multiview_create_info = if is_multiview {
            debug_assert!(multiview_view_masks.len() == subpasses.len());
            debug_assert!(multiview_view_offsets.len() == dependencies.len());

            Some(ash::vk::RenderPassMultiviewCreateInfo {
                subpass_count: multiview_view_masks.len() as u32,
                p_view_masks: multiview_view_masks.as_ptr(),
                dependency_count: multiview_view_offsets.len() as u32,
                p_view_offsets: multiview_view_offsets.as_ptr(),
                correlation_mask_count: correlated_view_masks.len() as u32,
                p_correlation_masks: correlated_view_masks.as_ptr(),
                ..Default::default()
            })
        } else {
            None
        };

        /* Create */

        let mut create_info = ash::vk::RenderPassCreateInfo {
            flags: ash::vk::RenderPassCreateFlags::empty(),
            attachment_count: attachments_vk.len() as u32,
            p_attachments: if attachments_vk.is_empty() {
                ptr::null()
            } else {
                attachments_vk.as_ptr()
            },
            subpass_count: subpasses_vk.len() as u32,
            p_subpasses: if subpasses_vk.is_empty() {
                ptr::null()
            } else {
                subpasses_vk.as_ptr()
            },
            dependency_count: dependencies_vk.len() as u32,
            p_dependencies: if dependencies_vk.is_empty() {
                ptr::null()
            } else {
                dependencies_vk.as_ptr()
            },
            ..Default::default()
        };

        if let Some(input_attachment_aspect_create_info) =
            input_attachment_aspect_create_info.as_mut()
        {
            input_attachment_aspect_create_info.p_next = create_info.p_next;
            create_info.p_next = input_attachment_aspect_create_info as *const _ as *const _;
        }

        if let Some(multiview_create_info) = multiview_create_info.as_mut() {
            multiview_create_info.p_next = create_info.p_next;
            create_info.p_next = multiview_create_info as *const _ as *const _;
        }

        Ok({
            let fns = device.fns();
            let mut output = MaybeUninit::uninit();
            (fns.v1_0.create_render_pass)(
                device.handle(),
                &create_info,
                ptr::null(),
                output.as_mut_ptr(),
            )
            .result()
            .map_err(VulkanError::from)?;
            output.assume_init()
        })
    }
}

/// Error that can happen when creating a `RenderPass`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RenderPassCreationError {
    /// Not enough memory.
    OomError(OomError),

    RequirementNotMet {
        required_for: &'static str,
        requires_one_of: RequiresOneOf,
    },

    /// An attachment is first used in the render pass with a read-only layout or as an input
    /// attachment, but its `load_op` or `stencil_load_op` is [`LoadOp::Clear`].
    AttachmentFirstUseLoadOpInvalid {
        attachment: u32,
        first_use_subpass: u32,
    },

    /// An attachment has an `initial_layout` or `final_layout` value that is invalid for the
    /// provided `format`.
    AttachmentLayoutInvalid { attachment: u32 },

    /// Correlated view masks were included, but multiview is not enabled on the render pass.
    CorrelatedViewMasksMultiviewNotEnabled,

    /// The provided correlated view masks contain a bit that is set in more than one element.
    CorrelatedViewMasksOverlapping,

    /// A subpass dependency specified an access type that was not supported by the given stages.
    DependencyAccessNotSupportedByStages { dependency: u32 },

    /// A subpass dependency has both `src_subpass` and `dst_subpass` set to `None`.
    DependencyBothSubpassesExternal { dependency: u32 },

    /// A subpass dependency specifies a subpass self-dependency and includes framebuffer stages in
    /// both `src_stages` and `dst_stages`, but the `by_region` dependency was not enabled.
    DependencySelfDependencyFramebufferStagesWithoutByRegion { dependency: u32 },

    /// A subpass dependency specifies a subpass self-dependency and includes
    /// non-framebuffer stages, but the latest stage in `src_stages` is after the earliest stage
    /// in `dst_stages`.
    DependencySelfDependencySourceStageAfterDestinationStage { dependency: u32 },

    /// A subpass dependency specifies a subpass self-dependency and has the `view_local` dependency
    /// enabled, but the inner offset value was not 0.
    DependencySelfDependencyViewLocalNonzeroViewOffset { dependency: u32 },

    /// A subpass dependency specifies a subpass self-dependency without the `view_local`
    /// dependency, but the referenced subpass has more than one bit set in its `view_mask`.
    DependencySelfDependencyViewMaskMultiple { dependency: u32, subpass: u32 },

    /// A subpass dependency has a `src_subpass` that is later than the `dst_subpass`.
    DependencySourceSubpassAfterDestinationSubpass { dependency: u32 },

    /// A subpass dependency has a bit set in the `src_stages` or `dst_stages` that is
    /// not supported for graphics pipelines.
    DependencyStageNotSupported { dependency: u32 },

    /// A subpass index in a subpass dependency is not less than the number of subpasses in the
    /// render pass.
    DependencySubpassOutOfRange { dependency: u32, subpass: u32 },

    /// In a subpass dependency, `dependency_flags` contains [`VIEW_LOCAL`], but `src_subpass` or
    /// `dst_subpass` were set to `None`.
    ///
    /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
    DependencyViewLocalExternalDependency { dependency: u32 },

    /// In a subpass dependency, `dependency_flags` contains [`VIEW_LOCAL`], but multiview is not
    /// enabled on the render pass.
    ///
    /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
    DependencyViewLocalMultiviewNotEnabled { dependency: u32 },

    /// In a subpass dependency, `view_offset` is not zero, but `dependency_flags` does not contain
    /// [`VIEW_LOCAL`].
    ///
    /// [`VIEW_LOCAL`]: crate::sync::DependencyFlags::VIEW_LOCAL
    DependencyViewOffzetNonzeroWithoutViewLocal { dependency: u32 },

    /// A reference to an attachment used other than as an input attachment in a subpass has
    /// one or more aspects selected.
    SubpassAttachmentAspectsNotEmpty { subpass: u32, attachment: u32 },

    /// An attachment used as an attachment in a subpass has a layout that is not supported for
    /// that usage.
    SubpassAttachmentLayoutInvalid {
        subpass: u32,
        attachment: u32,
        usage: &'static str,
    },

    /// The layouts of all uses of an attachment in a subpass do not match.
    SubpassAttachmentLayoutMismatch { subpass: u32, attachment: u32 },

    /// An attachment index in a subpass is not less than the number of attachments in the render
    /// pass.
    SubpassAttachmentOutOfRange { subpass: u32, attachment: u32 },

    /// An attachment is used as both a color attachment and a depth/stencil attachment in a
    /// subpass.
    SubpassAttachmentUsageColorDepthStencil { subpass: u32, attachment: u32 },

    /// An attachment used as an attachment in a subpass has a format that does not support that
    /// usage.
    SubpassAttachmentFormatUsageNotSupported {
        subpass: u32,
        attachment: u32,
        usage: &'static str,
    },

    /// An attachment used as a color attachment in a subpass with resolve attachments has a
    /// `samples` value of [`SampleCount::Sample1`].
    SubpassColorAttachmentWithResolveNotMultisampled { subpass: u32, attachment: u32 },

    /// An attachment used as a color or depth/stencil attachment in a subpass has a `samples` value
    /// that is different from the first color attachment.
    SubpassColorDepthStencilAttachmentSamplesMismatch {
        subpass: u32,
        attachment: u32,
        samples: SampleCount,
        first_samples: SampleCount,
    },

    /// A reference to an attachment used as an input attachment in a subpass selects aspects that
    /// are not present in the format of the attachment.
    SubpassInputAttachmentAspectsNotCompatible { subpass: u32, attachment: u32 },

    /// The `max_color_attachments` limit has been exceeded for a subpass.
    SubpassMaxColorAttachmentsExceeded {
        subpass: u32,
        color_attachment_count: u32,
        max: u32,
    },

    /// The `max_multiview_view_count` limit has been exceeded for a subpass.
    SubpassMaxMultiviewViewCountExceeded {
        subpass: u32,
        view_count: u32,
        max: u32,
    },

    /// The multiview state (whether `view_mask` is nonzero) of a subpass is different from the
    /// first subpass.
    SubpassMultiviewMismatch {
        subpass: u32,
        multiview: bool,
        first_subpass_multiview: bool,
    },

    /// An attachment marked as a preserve attachment in a subpass is also used as an attachment
    /// in that subpass.
    SubpassPreserveAttachmentUsedElsewhere { subpass: u32, attachment: u32 },

    /// The `resolve_attachments` field of a subpass was not empty, but its length did not match
    /// the length of `color_attachments`.
    SubpassResolveAttachmentsColorAttachmentsLenMismatch { subpass: u32 },

    /// An attachment used as a resolve attachment in a subpass has a `format` value different from
    /// the corresponding color attachment.
    SubpassResolveAttachmentFormatMismatch {
        subpass: u32,
        resolve_attachment: u32,
        color_attachment: u32,
    },

    /// An attachment used as a resolve attachment in a subpass has a `samples` value other than
    /// [`SampleCount::Sample1`].
    SubpassResolveAttachmentMultisampled { subpass: u32, attachment: u32 },

    /// A resolve attachment in a subpass is `Some`, but the corresponding color attachment is
    /// `None`.
    SubpassResolveAttachmentWithoutColorAttachment { subpass: u32 },
}

impl Error for RenderPassCreationError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match self {
            RenderPassCreationError::OomError(err) => Some(err),
            _ => None,
        }
    }
}

impl Display for RenderPassCreationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
        match self {
            Self::OomError(_) => write!(f, "not enough memory available"),
            Self::RequirementNotMet {
                required_for,
                requires_one_of,
            } => write!(
                f,
                "a requirement was not met for: {}; requires one of: {}",
                required_for, requires_one_of,
            ),
            Self::AttachmentFirstUseLoadOpInvalid {
                attachment,
                first_use_subpass,
            } => write!(
                f,
                "attachment {} is first used in the render pass in subpass {} with a read-only \
                layout or as an input attachment, but its `load_op` or `stencil_load_op` is \
                `LoadOp::Clear`",
                attachment, first_use_subpass,
            ),
            Self::AttachmentLayoutInvalid { attachment } => write!(
                f,
                "attachment {} has an `initial_layout` or `final_layout` value that is invalid for \
                the provided `format`",
                attachment,
            ),
            Self::CorrelatedViewMasksMultiviewNotEnabled => write!(
                f,
                "correlated view masks were included, but multiview is not enabled on the render \
                pass",
            ),
            Self::CorrelatedViewMasksOverlapping => write!(
                f,
                "the provided correlated view masks contain a bit that is set in more than one \
                element",
            ),
            Self::DependencyAccessNotSupportedByStages { dependency } => write!(
                f,
                "subpass dependency {} specified an access type that was not supported by the \
                given stages",
                dependency,
            ),
            Self::DependencySelfDependencyFramebufferStagesWithoutByRegion { dependency } => {
                write!(
                    f,
                    "subpass dependency {} specifies a subpass self-dependency and includes \
                    framebuffer stages in both `src_stages` and `dst_stages`, but the \
                    `by_region` dependency was not enabled",
                    dependency,
                )
            }
            Self::DependencySelfDependencySourceStageAfterDestinationStage { dependency } => {
                write!(
                    f,
                    "subpass dependency {} specifies a subpass self-dependency and includes \
                    non-framebuffer stages, but the latest stage in `src_stages` is after the \
                    earliest stage in `dst_stages`",
                    dependency,
                )
            }
            Self::DependencySelfDependencyViewLocalNonzeroViewOffset { dependency } => write!(
                f,
                "subpass dependency {} specifies a subpass self-dependency and has the \
                `view_local` dependency enabled, but the inner offset value was not 0",
                dependency,
            ),
            Self::DependencySelfDependencyViewMaskMultiple {
                dependency,
                subpass,
            } => write!(
                f,
                "subpass dependency {} specifies a subpass self-dependency without the \
                `view_local` dependency, but the referenced subpass {} has more than one bit set \
                in its `view_mask`",
                dependency, subpass,
            ),
            Self::DependencySourceSubpassAfterDestinationSubpass { dependency } => write!(
                f,
                "subpass dependency {} has a `src_subpass` that is later than the \
                `dst_subpass`",
                dependency,
            ),
            Self::DependencyStageNotSupported { dependency } => write!(
                f,
                "subpass dependency {} has a bit set in the `src_stages` or \
                `dst_stages` that is not supported for graphics pipelines",
                dependency,
            ),
            Self::DependencyBothSubpassesExternal { dependency } => write!(
                f,
                "subpass dependency {} has both `src_subpass` and `dst_subpass` set to \
                `None`",
                dependency,
            ),
            Self::DependencySubpassOutOfRange {
                dependency,
                subpass,
            } => write!(
                f,
                "the subpass index {} in subpass dependency {} is not less than the number of \
                subpasses in the render pass",
                subpass, dependency,
            ),
            Self::DependencyViewLocalExternalDependency { dependency } => write!(
                f,
                "in subpass dependency {}, `dependency_flags` contains `VIEW_LOCAL`, but \
                `src_subpass` or `dst_subpass` were set to `None`",
                dependency,
            ),
            Self::DependencyViewLocalMultiviewNotEnabled { dependency } => write!(
                f,
                "in subpass dependency {}, `dependency_flags` contains `VIEW_LOCAL`, but \
                multiview is not enabled on the render pass",
                dependency,
            ),
            Self::DependencyViewOffzetNonzeroWithoutViewLocal { dependency } => write!(
                f,
                "in subpass dependency {}, `view_offset` is not zero, but `dependency_flags` does \
                not contain `VIEW_LOCAL`",
                dependency,
            ),
            Self::SubpassAttachmentAspectsNotEmpty {
                subpass,
                attachment,
            } => write!(
                f,
                "a reference to attachment {} used other than as an input attachment in subpass {} \
                has one or more aspects selected",
                attachment, subpass,
            ),
            Self::SubpassAttachmentLayoutMismatch {
                subpass,
                attachment,
            } => write!(
                f,
                "the layouts of all uses of attachment {} in subpass {} do not match.",
                attachment, subpass,
            ),
            Self::SubpassAttachmentLayoutInvalid {
                subpass,
                attachment,
                usage,
            } => write!(
                f,
                "attachment {} used as {} attachment in subpass {} has a layout that is not \
                supported for that usage",
                attachment, usage, subpass,
            ),
            Self::SubpassAttachmentOutOfRange {
                subpass,
                attachment,
            } => write!(
                f,
                "the attachment index {} in subpass {} is not less than the number of attachments \
                in the render pass",
                attachment, subpass,
            ),
            Self::SubpassAttachmentUsageColorDepthStencil {
                subpass,
                attachment,
            } => write!(
                f,
                "attachment {} is used as both a color attachment and a depth/stencil attachment \
                in subpass {}",
                attachment, subpass,
            ),
            Self::SubpassAttachmentFormatUsageNotSupported {
                subpass,
                attachment,
                usage,
            } => write!(
                f,
                "attachment {} used as {} attachment in subpass {} has a format that does not \
                support that usage",
                attachment, usage, subpass,
            ),
            Self::SubpassColorAttachmentWithResolveNotMultisampled {
                subpass,
                attachment,
            } => write!(
                f,
                "attachment {} used as a color attachment in subpass {} with resolve attachments \
                has a `samples` value of `SampleCount::Sample1`",
                attachment, subpass,
            ),
            Self::SubpassColorDepthStencilAttachmentSamplesMismatch {
                subpass,
                attachment,
                samples,
                first_samples,
            } => write!(
                f,
                "attachment {} used as a color or depth/stencil attachment in subpass {} has a \
                `samples` value {:?} that is different from the first color attachment ({:?})",
                attachment, subpass, samples, first_samples,
            ),
            Self::SubpassInputAttachmentAspectsNotCompatible {
                subpass,
                attachment,
            } => write!(
                f,
                "a reference to attachment {} used as an input attachment in subpass {} selects \
                aspects that are not present in the format of the attachment",
                attachment, subpass,
            ),
            Self::SubpassMaxColorAttachmentsExceeded { .. } => {
                write!(f, "the `max_color_attachments` limit has been exceeded")
            }
            Self::SubpassMaxMultiviewViewCountExceeded { .. } => write!(
                f,
                "the `max_multiview_view_count` limit has been exceeded for a subpass",
            ),
            Self::SubpassMultiviewMismatch {
                subpass,
                multiview,
                first_subpass_multiview,
            } => write!(
                f,
                "the multiview state (whether `view_mask` is nonzero) of subpass {} is {}, which \
                is different from the first subpass ({})",
                subpass, multiview, first_subpass_multiview,
            ),
            Self::SubpassPreserveAttachmentUsedElsewhere {
                subpass,
                attachment,
            } => write!(
                f,
                "attachment {} marked as a preserve attachment in subpass {} is also used as an \
                attachment in that subpass",
                attachment, subpass,
            ),
            Self::SubpassResolveAttachmentsColorAttachmentsLenMismatch { subpass } => write!(
                f,
                "the `resolve_attachments` field of subpass {} was not empty, but its length did \
                not match the length of `color_attachments`",
                subpass,
            ),
            Self::SubpassResolveAttachmentFormatMismatch {
                subpass,
                resolve_attachment,
                color_attachment,
            } => write!(
                f,
                "attachment {} used as a resolve attachment in subpass {} has a `format` value \
                different from the corresponding color attachment {}",
                subpass, resolve_attachment, color_attachment,
            ),
            Self::SubpassResolveAttachmentMultisampled {
                subpass,
                attachment,
            } => write!(
                f,
                "attachment {} used as a resolve attachment in subpass {} has a `samples` value \
                other than `SampleCount::Sample1`",
                attachment, subpass,
            ),
            Self::SubpassResolveAttachmentWithoutColorAttachment { subpass } => write!(
                f,
                "a resolve attachment in subpass {} is `Some`, but the corresponding color \
                attachment is `None`",
                subpass,
            ),
        }
    }
}

impl From<OomError> for RenderPassCreationError {
    fn from(err: OomError) -> RenderPassCreationError {
        RenderPassCreationError::OomError(err)
    }
}

impl From<VulkanError> for RenderPassCreationError {
    fn from(err: VulkanError) -> RenderPassCreationError {
        match err {
            err @ VulkanError::OutOfHostMemory => {
                RenderPassCreationError::OomError(OomError::from(err))
            }
            err @ VulkanError::OutOfDeviceMemory => {
                RenderPassCreationError::OomError(OomError::from(err))
            }
            _ => panic!("unexpected error: {:?}", err),
        }
    }
}

impl From<RequirementNotMet> for RenderPassCreationError {
    fn from(err: RequirementNotMet) -> Self {
        Self::RequirementNotMet {
            required_for: err.required_for,
            requires_one_of: err.requires_one_of,
        }
    }
}
