/* Copyright (c) 2015-2026 The Khronos Group Inc.
 * Copyright (c) 2015-2026 Valve Corporation
 * Copyright (c) 2015-2026 LunarG, Inc.
 * Copyright (C) 2015-2026 Google Inc.
 * Modifications Copyright (C) 2020-2026 Advanced Micro Devices, Inc. All rights reserved.
 * Modifications Copyright (C) 2022-2024 RasterGrid Kft.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <string>
#include <vulkan/utility/vk_format_utils.h>
#include <vulkan/vk_enum_string_helper.h>
#include <vulkan/vulkan_core.h>

#include "core_validation.h"
#include "cc_sync_vuid_maps.h"
#include "cc_vuid_maps.h"
#include "core_checks/cc_state_tracker.h"
#include "error_message/error_location.h"
#include "generated/pnext_chain_extraction.h"
#include "generated/dispatch_functions.h"
#include "generated/extended_flags_helper_generator.h"
#include "error_message/error_strings.h"
#include "state_tracker/image_state.h"
#include "state_tracker/sampler_state.h"
#include "state_tracker/render_pass_state.h"
#include "state_tracker/cmd_buffer_state.h"
#include "state_tracker/wsi_state.h"
#include "utils/math_utils.h"
#include "utils/image_utils.h"
#include "containers/container_utils.h"

bool CoreChecks::IsMixSamplingSupported() const {
    return IsExtEnabled(extensions.vk_amd_mixed_attachment_samples) || IsExtEnabled(extensions.vk_nv_framebuffer_mixed_samples) ||
           enabled_features.multisampledRenderToSingleSampled;
}

bool CoreChecks::ValidateImageFormatFeatures(const VkImageCreateInfo& create_info, const Location& loc,
                                             const VkImageCreateFlags2KHR create_flags, const VkImageUsageFlags2KHR usage) const {
    bool skip = false;

    // validates based on imageCreateFormatFeatures from vkspec.html#resources-image-creation-limits
    VkFormatFeatureFlags2 tiling_features = 0;
    const VkImageTiling image_tiling = create_info.tiling;
    const VkFormat image_format = create_info.format;

    if (image_format == VK_FORMAT_UNDEFINED) {
        // VU 01975 states format can't be undefined unless an android externalFormat
        if (image_tiling == VK_IMAGE_TILING_OPTIMAL) {
            tiling_features = device_state->GetExternalFormatFeaturesANDROID(create_info.pNext);
        }
    } else if (image_tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
        vvl::unordered_set<uint64_t> drm_format_modifiers;
        const VkImageDrmFormatModifierExplicitCreateInfoEXT* drm_explicit =
            vku::FindStructInPNextChain<VkImageDrmFormatModifierExplicitCreateInfoEXT>(create_info.pNext);
        const VkImageDrmFormatModifierListCreateInfoEXT* drm_implicit =
            vku::FindStructInPNextChain<VkImageDrmFormatModifierListCreateInfoEXT>(create_info.pNext);

        if (drm_explicit != nullptr) {
            drm_format_modifiers.insert(drm_explicit->drmFormatModifier);
        } else {
            // VUID 02261 makes sure its only explict or implict in parameter checking
            ASSERT_AND_RETURN_SKIP(drm_implicit);
            for (uint32_t i = 0; i < drm_implicit->drmFormatModifierCount; i++) {
                drm_format_modifiers.insert(drm_implicit->pDrmFormatModifiers[i]);
            }
        }

        if (device_state->special_supported.vk_khr_format_feature_flags2) {
            VkDrmFormatModifierPropertiesList2EXT fmt_drm_props = vku::InitStructHelper();
            VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, image_format, &fmt_props_2);
            std::vector<VkDrmFormatModifierProperties2EXT> drm_properties;
            drm_properties.resize(fmt_drm_props.drmFormatModifierCount);
            fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, image_format, &fmt_props_2);

            for (uint32_t i = 0; i < fmt_drm_props.drmFormatModifierCount; i++) {
                if (drm_format_modifiers.find(fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifier) !=
                    drm_format_modifiers.end()) {
                    tiling_features |= fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifierTilingFeatures;
                }
            }
        } else {
            VkDrmFormatModifierPropertiesListEXT fmt_drm_props = vku::InitStructHelper();
            VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, image_format, &fmt_props_2);
            std::vector<VkDrmFormatModifierPropertiesEXT> drm_properties;
            drm_properties.resize(fmt_drm_props.drmFormatModifierCount);
            fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, image_format, &fmt_props_2);

            for (uint32_t i = 0; i < fmt_drm_props.drmFormatModifierCount; i++) {
                if (drm_format_modifiers.find(fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifier) !=
                    drm_format_modifiers.end()) {
                    tiling_features |= fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifierTilingFeatures;
                }
            }
        }
    } else {
        VkFormatProperties3 format_properties = GetPDFormatProperties(image_format);
        tiling_features = (image_tiling == VK_IMAGE_TILING_LINEAR) ? format_properties.linearTilingFeatures
                                                                   : format_properties.optimalTilingFeatures;
    }

    // Lack of disjoint format feature support while using the flag
    if (vkuFormatIsMultiplane(image_format) && ((create_flags & VK_IMAGE_CREATE_DISJOINT_BIT) != 0) &&
        ((tiling_features & VK_FORMAT_FEATURE_2_DISJOINT_BIT) == 0)) {
        skip |= LogError("VUID-VkImageCreateInfo-imageCreateFormatFeatures-02260", device, loc.dot(Field::usage),
                         "includes VK_IMAGE_CREATE_DISJOINT_BIT, but %s doesn't support "
                         "VK_FORMAT_FEATURE_DISJOINT_BIT.\n"
                         "(supported features: %s)",
                         string_VkFormat(create_info.format), string_VkFormatFeatureFlags2(tiling_features).c_str());
    }

    if (((tiling_features & VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT) == 0) && (usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT)) {
        skip |= LogError("VUID-VkImageCreateInfo-imageCreateFormatFeatures-09048", device, loc.dot(Field::usage),
                         "includes VK_IMAGE_USAGE_HOST_TRANSFER_BIT, but %s doesn't support "
                         "VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT.\n"
                         "(supported features: %s)",
                         string_VkFormat(create_info.format), string_VkFormatFeatureFlags2(tiling_features).c_str());
    }

    return skip;
}

bool CoreChecks::ValidateImageAlignmentControlCreateInfo(const VkImageCreateInfo& create_info,
                                                         const Location& create_info_loc) const {
    bool skip = false;
    const auto alignment_control_create_info =
        vku::FindStructInPNextChain<VkImageAlignmentControlCreateInfoMESA>(create_info.pNext);
    if (!alignment_control_create_info) {
        return skip;
    }

    if (!enabled_features.imageAlignmentControl) {
        skip |= LogError(
            "VUID-VkImageAlignmentControlCreateInfoMESA-imageAlignmentControl-09657", device, create_info_loc.dot(Field::pNext),
            "contains a VkImageAlignmentControlCreateInfoMESA struct but the imageAlignmentControl feature was not enabled.\n%s",
            PrintPNextChain(Struct::VkImageCreateInfo, create_info.pNext).c_str());
    }

    if (vku::FindStructInPNextChain<VkExternalMemoryImageCreateInfo>(create_info.pNext)) {
        skip |= LogError("VUID-VkImageCreateInfo-pNext-09654", device, create_info_loc.dot(Field::pNext),
                         "contains both a VkImageAlignmentControlCreateInfoMESA and VkExternalMemoryImageCreateInfo struct.\n%s",
                         PrintPNextChain(Struct::VkImageCreateInfo, create_info.pNext).c_str());
    }

    if (create_info.tiling != VK_IMAGE_TILING_OPTIMAL) {
        skip |= LogError("VUID-VkImageCreateInfo-pNext-09653", device, create_info_loc.dot(Field::tiling),
                         "is %s but needs to be VK_IMAGE_TILING_OPTIMAL", string_VkImageTiling(create_info.tiling));
    }

    if (alignment_control_create_info->maximumRequestedAlignment != 0) {
        if (!IsPowerOfTwo(alignment_control_create_info->maximumRequestedAlignment)) {
            skip |= LogError("VUID-VkImageAlignmentControlCreateInfoMESA-maximumRequestedAlignment-09655", device,
                             create_info_loc.pNext(Struct::VkImageAlignmentControlCreateInfoMESA, Field::maximumRequestedAlignment),
                             "(%" PRIu32 ") must be a power of two.", alignment_control_create_info->maximumRequestedAlignment);
        } else if ((alignment_control_create_info->maximumRequestedAlignment &
                    phys_dev_ext_props.image_alignment_control_props.supportedImageAlignmentMask) == 0) {
            skip |= LogError("VUID-VkImageAlignmentControlCreateInfoMESA-maximumRequestedAlignment-09656", device,
                             create_info_loc.pNext(Struct::VkImageAlignmentControlCreateInfoMESA, Field::maximumRequestedAlignment),
                             "(0x%" PRIx32 ") and supportedImageAlignmentMask (0x%" PRIx32 ") don't share any bits.",
                             alignment_control_create_info->maximumRequestedAlignment,
                             phys_dev_ext_props.image_alignment_control_props.supportedImageAlignmentMask);
        }
    }
    return skip;
}

bool CoreChecks::ValidateImageVideo(const VkImageCreateInfo& create_info, const Location& create_info_loc,
                                    const VkImageCreateFlags2KHR create_flags, const Location& flags_loc,
                                    const VkImageUsageFlags2KHR usage, const Location& usage_loc,
                                    const ErrorObject& error_obj) const {
    bool skip = false;

    const bool has_decode_usage = usage & (VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR |
                                           VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR);
    const bool has_encode_usage = usage & (VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR |
                                           VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR);
    const bool has_quantization_map_usage =
        usage & (VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR);
    const bool video_profile_independent = create_flags & VK_IMAGE_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR;

    if (video_profile_independent && !enabled_features.videoMaintenance1) {
        skip |= LogError("VUID-VkImageCreateInfo-flags-08328", device, flags_loc,
                         "has VK_IMAGE_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR is set but the videoMaintenance1 "
                         "device feature is not enabled.");
    }

    if (video_profile_independent && (usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR) &&
        (usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) == 0) {
        skip |= LogError("VUID-VkImageCreateInfo-flags-08329", device, flags_loc,
                         "has VK_IMAGE_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR set but usage (%s) contains "
                         "VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR without also containing "
                         "VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR.",
                         string_VkImageUsageFlags2KHR(usage).c_str());
    }

    if (video_profile_independent && (usage & VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR)) {
        skip |= LogError("VUID-VkImageCreateInfo-flags-08331", device, flags_loc,
                         "has VK_IMAGE_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR set but usage (%s) contains "
                         "VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR.",
                         string_VkImageUsageFlags2KHR(usage).c_str());
    }

    // There are stronger VUs for the format properties of quantization maps (i.e. no INDEPENDENT, must be 2D, etc.).
    // If any of these are triggered, then the video profile compatibility check can be skipped.
    bool valid_quantization_map_format = has_quantization_map_usage;
    if (has_quantization_map_usage) {
        if (!enabled_features.videoEncodeQuantizationMap) {
            skip |= LogError("VUID-VkImageCreateInfo-usage-10251", device, usage_loc,
                             "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                             "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but the "
                             "videoEncodeQuantizationMap device feature is not enabled.");
            valid_quantization_map_format = false;
        }

        if (video_profile_independent) {
            skip |= LogError("VUID-VkImageCreateInfo-flags-08331", device, flags_loc,
                             "has VK_IMAGE_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR set but usage (%s) contains "
                             "VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                             "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR.",
                             string_VkImageUsageFlags2KHR(usage).c_str());
            valid_quantization_map_format = false;
        }

        if (create_info.imageType != VK_IMAGE_TYPE_2D) {
            skip |= LogError("VUID-VkImageCreateInfo-usage-10252", device, usage_loc,
                             "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                             "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but imageType (%s) "
                             "is not VK_IMAGE_TYPE_2D.",
                             string_VkImageType(create_info.imageType));
            valid_quantization_map_format = false;
        }

        if (create_info.samples != VK_SAMPLE_COUNT_1_BIT) {
            skip |= LogError("VUID-VkImageCreateInfo-usage-10253", device, usage_loc,
                             "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                             "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but samples (%s) "
                             "is not VK_SAMPLE_COUNT_1_BIT_KHR.",
                             string_VkSampleCountFlagBits(create_info.samples));
            valid_quantization_map_format = false;
        }

        const auto* video_profiles = vku::FindStructInPNextChain<VkVideoProfileListInfoKHR>(create_info.pNext);
        if (video_profiles == nullptr) {
            skip |= LogError("VUID-VkImageCreateInfo-usage-10254", device, usage_loc,
                             "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                             "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but the pNext chain does not "
                             "contain a VkVideoProfileListInfoKHR structure.");
            valid_quantization_map_format = false;
        } else if (video_profiles->profileCount != 1) {
            skip |= LogError("VUID-VkImageCreateInfo-usage-10254", device, usage_loc,
                             "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                             "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but %s (%" PRIu32 ") does not equal 1.",
                             create_info_loc.pNext(Struct::VkVideoProfileListInfoKHR, Field::profileCount).Fields().c_str(),
                             video_profiles->profileCount);
            valid_quantization_map_format = false;
        } else {
            const Location profile_info_loc = create_info_loc.pNext(Struct::VkVideoProfileListInfoKHR, Field::pProfiles, 0);
            skip |= core::ValidateVideoProfileInfo(*this, &video_profiles->pProfiles[0], error_obj,
                                                   create_info_loc.dot(Field::pProfiles, 0));

            vvl::VideoProfileDesc profile_desc(physical_device, &video_profiles->pProfiles[0]);
            const auto& profile_caps = profile_desc.GetCapabilities();

            if (profile_desc.IsEncode()) {
                if ((usage & VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR) != 0 &&
                    (profile_caps.encode.flags & VK_VIDEO_ENCODE_CAPABILITY_QUANTIZATION_DELTA_MAP_BIT_KHR) == 0) {
                    skip |= LogError("VUID-VkImageCreateInfo-usage-10255", device, usage_loc,
                                     "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR set but "
                                     "the encode profile specified in %s does not support "
                                     "VK_VIDEO_ENCODE_CAPABILITY_QUANTIZATION_DELTA_MAP_BIT_KHR.",
                                     profile_info_loc.Fields().c_str());
                    valid_quantization_map_format = false;
                }

                if ((usage & VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR) != 0 &&
                    (profile_caps.encode.flags & VK_VIDEO_ENCODE_CAPABILITY_EMPHASIS_MAP_BIT_KHR) == 0) {
                    skip |= LogError("VUID-VkImageCreateInfo-usage-10256", device, usage_loc,
                                     "has VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but "
                                     "the encode profile specified in %s does not support "
                                     "VK_VIDEO_ENCODE_CAPABILITY_EMPHASIS_MAP_BIT_KHR.",
                                     profile_info_loc.Fields().c_str());
                    valid_quantization_map_format = false;
                }

                if (valid_quantization_map_format &&
                    create_info.extent.width > profile_caps.encode_ext.quantization_map.maxQuantizationMapExtent.width) {
                    skip |=
                        LogError("VUID-VkImageCreateInfo-usage-10257", device, usage_loc,
                                 "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                                 "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but has extent.width (%" PRIu32
                                 ") "
                                 "larger than the maxQuantizationMapExtent.width (%" PRIu32
                                 ") supported by the "
                                 "encode profile specified in %s.",
                                 create_info.extent.width, profile_caps.encode_ext.quantization_map.maxQuantizationMapExtent.width,
                                 profile_info_loc.Fields().c_str());
                }

                if (valid_quantization_map_format &&
                    create_info.extent.height > profile_caps.encode_ext.quantization_map.maxQuantizationMapExtent.height) {
                    skip |= LogError("VUID-VkImageCreateInfo-usage-10258", device, usage_loc,
                                     "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                                     "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but has extent.height (%" PRIu32
                                     ") "
                                     "larger than the maxQuantizationMapExtent.height (%" PRIu32
                                     ") supported by the "
                                     "encode profile specified in %s.",
                                     create_info.extent.height,
                                     profile_caps.encode_ext.quantization_map.maxQuantizationMapExtent.height,
                                     profile_info_loc.Fields().c_str());
                }
            } else {
                skip |= LogError("VUID-VkImageCreateInfo-usage-10254", device, usage_loc,
                                 "has VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR or "
                                 "VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR set but %s does not specify a "
                                 "video encode operation (videoCodecOperation is %s).",
                                 profile_info_loc.Fields().c_str(),
                                 string_VkVideoCodecOperationFlagBitsKHR(video_profiles->pProfiles[0].videoCodecOperation));
                valid_quantization_map_format = false;
            }
        }
    }

    if (has_decode_usage || has_encode_usage || valid_quantization_map_format) {
        const bool expect_decode_profile = has_decode_usage && !video_profile_independent;
        const bool expect_encode_profile = has_encode_usage && !video_profile_independent;

        const auto* video_profiles = vku::FindStructInPNextChain<VkVideoProfileListInfoKHR>(create_info.pNext);
        // Quantization map video profile list info validation happens in the previous block
        if (!has_quantization_map_usage) {
            skip |= core::ValidateVideoProfileListInfo(
                *this, video_profiles, error_obj, create_info_loc.pNext(Struct::VkVideoProfileListInfoKHR), expect_decode_profile,
                "VUID-VkImageCreateInfo-usage-04815", expect_encode_profile, "VUID-VkImageCreateInfo-usage-04816");
        }

        if (video_profiles && video_profiles->profileCount > 0) {
            if (!IsSupportedVideoFormat(create_flags, usage, create_info.imageType, create_info.format, create_info.tiling,
                                        video_profiles)) {
                skip |= LogError("VUID-VkImageCreateInfo-pNext-06811", device, create_info_loc,
                                 "specifies flags (%s), format (%s), imageType (%s), and tiling (%s) which are not "
                                 "supported by any of the supported video format properties for the video profiles "
                                 "specified in the VkVideoProfileListInfoKHR structure included in the "
                                 "pCreateInfo->pNext chain, as reported by "
                                 "vkGetPhysicalDeviceVideoFormatPropertiesKHR for the same video profiles "
                                 "and the image usage flags specified in pCreateInfo->usage (%s).",
                                 string_VkImageCreateFlags2KHR(create_flags).c_str(), string_VkFormat(create_info.format),
                                 string_VkImageType(create_info.imageType), string_VkImageTiling(create_info.tiling),
                                 string_VkImageUsageFlags2KHR(usage).c_str());
            }
        }
    }

    return skip;
}

bool CoreChecks::ValidateImageSwapchain(const VkImageCreateInfo& create_info, const Location& create_info_loc,
                                        const VkImageCreateFlags2KHR create_flags, const VkImageUsageFlags2KHR usage) const {
    bool skip = false;

    const auto swapchain_create_info = vku::FindStructInPNextChain<VkImageSwapchainCreateInfoKHR>(create_info.pNext);
    if (!swapchain_create_info || swapchain_create_info->swapchain == VK_NULL_HANDLE) {
        return skip;
    }
    auto swapchain_state = Get<vvl::Swapchain>(swapchain_create_info->swapchain);
    if (!swapchain_state) {
        return skip;
    }

    const VkSwapchainCreateFlagsKHR swapchain_flags = swapchain_state->create_info.flags;

    // Validate rest of Swapchain Image create check that require swapchain state
    const char* vuid = "VUID-VkImageSwapchainCreateInfoKHR-swapchain-00995";
    if (((swapchain_flags & VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR) != 0) &&
        ((create_flags & VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT) == 0)) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR flag so "
                         "all swapchain images must have the VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT flag set.");
    }
    if (((swapchain_flags & VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR) != 0) &&
        ((create_flags & VK_IMAGE_CREATE_PROTECTED_BIT) == 0)) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR flag so all "
                         "swapchain images must have the VK_IMAGE_CREATE_PROTECTED_BIT flag set.");
    }
    const VkImageCreateFlags mutable_flags = (VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT);
    if (((swapchain_flags & VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR) != 0) &&
        ((create_flags & mutable_flags) != mutable_flags)) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR flag so "
                         "all swapchain images must have the VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT and "
                         "VK_IMAGE_CREATE_EXTENDED_USAGE_BIT flags both set.");
    }
    if (create_info.format != swapchain_state->create_info.imageFormat) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with format %s which doesn't match pCreateInfo->format %s.",
                         string_VkFormat(swapchain_state->create_info.imageFormat), string_VkFormat(create_info.format));
    }
    if (create_info.extent.width != swapchain_state->create_info.imageExtent.width ||
        create_info.extent.height != swapchain_state->create_info.imageExtent.height || create_info.extent.depth != 1u) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with extent (%" PRIu32 ", %" PRIu32 ", 1) which doesn't match pCreateInfo->extent (%" PRIu32
                         ", %" PRIu32 ", %" PRIu32 ").",
                         swapchain_state->create_info.imageExtent.width, swapchain_state->create_info.imageExtent.height,
                         create_info.extent.width, create_info.extent.height, create_info.extent.depth);
    }
    if (create_info.arrayLayers != swapchain_state->create_info.imageArrayLayers) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with imageArrayLayers %" PRIu32 " which doesn't match pCreateInfo->arrayLayers %" PRIu32 ".",
                         swapchain_state->create_info.imageArrayLayers, create_info.arrayLayers);
    }
    if (usage != swapchain_state->create_info.imageUsage) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with imageUsage %s which doesn't match pCreateInfo->usage %s.",
                         string_VkImageUsageFlags2KHR(swapchain_state->image_usage).c_str(),
                         string_VkImageUsageFlags2KHR(usage).c_str());
    }
    if (create_info.sharingMode != swapchain_state->create_info.imageSharingMode) {
        skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                         "was created with imageSharingMode %s which doesn't match pCreateInfo->sharingMode %s.",
                         string_VkSharingMode(swapchain_state->create_info.imageSharingMode),
                         string_VkSharingMode(create_info.sharingMode));
    }
    if (create_info.sharingMode == VK_SHARING_MODE_CONCURRENT) {
        if (create_info.queueFamilyIndexCount != swapchain_state->create_info.queueFamilyIndexCount) {
            skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                             "was created with queueFamilyIndexCount %" PRIu32
                             " which doesn't match pCreateInfo->queueFamilyIndexCount %" PRIu32 ".",
                             swapchain_state->create_info.queueFamilyIndexCount, create_info.queueFamilyIndexCount);
        } else {
            for (uint32_t i = 0; i < create_info.queueFamilyIndexCount; ++i) {
                bool found = false;
                for (uint32_t j = 0; j < create_info.queueFamilyIndexCount; ++j) {
                    if (create_info.pQueueFamilyIndices[j] == swapchain_state->create_info.pQueueFamilyIndices[i]) {
                        found = true;
                        break;
                    }
                }
                if (!found) {
                    skip |= LogError(vuid, device, create_info_loc.pNext(Struct::VkImageSwapchainCreateInfoKHR, Field::swapchain),
                                     "was created with pQueueFamilyIndices containing index %" PRIu32
                                     " which is not included in pCreateInfo->pQueueFamilyIndices.",
                                     i);
                }
            }
        }
    }

    return skip;
}

bool CoreChecks::ValidateImageExternalMemory(const VkImageCreateInfo& create_info, const Location& create_info_loc,
                                             VkPhysicalDeviceImageFormatInfo2& image_format_info) const {
    bool skip = false;

    const auto external_memory_create_info_nv = vku::FindStructInPNextChain<VkExternalMemoryImageCreateInfoNV>(create_info.pNext);
    const auto external_memory_create_info = vku::FindStructInPNextChain<VkExternalMemoryImageCreateInfo>(create_info.pNext);
    if (external_memory_create_info_nv != nullptr && external_memory_create_info != nullptr) {
        skip |= LogError("VUID-VkImageCreateInfo-pNext-00988", device, create_info_loc,
                         "has both VkExternalMemoryImageCreateInfoNV and "
                         "VkExternalMemoryImageCreateInfo chained structs.");
    }

    VkResult result = VK_SUCCESS;
    // No way to pass external format into vkGetPhysicalDeviceImageFormatProperties2 so invalid to check
    if (external_memory_create_info && external_memory_create_info->handleTypes != 0 && create_info.format != VK_FORMAT_UNDEFINED) {
        if (create_info.initialLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
            skip |= LogError("VUID-VkImageCreateInfo-pNext-01443", device,
                             create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfo, Field::handleTypes),
                             "is %s (non-zero) but the initialLayout is %s.",
                             string_VkExternalMemoryHandleTypeFlags(external_memory_create_info->handleTypes).c_str(),
                             string_VkImageLayout(create_info.initialLayout));
        }
        // Check external memory handle types compatibility
        const uint32_t any_type = 1u << MostSignificantBit(external_memory_create_info->handleTypes);
        VkPhysicalDeviceExternalImageFormatInfo external_image_info = vku::InitStructHelper();
        external_image_info.handleType = static_cast<VkExternalMemoryHandleTypeFlagBits>(any_type);
        vvl::PnextChainScopedAdd scoped_add_ext_img_info(&image_format_info, &external_image_info);

        VkExternalImageFormatProperties external_image_properties = vku::InitStructHelper();
        VkImageFormatProperties2 image_properties = vku::InitStructHelper(&external_image_properties);
        VkExternalMemoryHandleTypeFlags compatible_types = 0;
        if (create_info.tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
            result = DispatchGetPhysicalDeviceImageFormatProperties2Helper(api_version, physical_device, &image_format_info,
                                                                           &image_properties);
            compatible_types = external_image_properties.externalMemoryProperties.compatibleHandleTypes;
        } else {
            auto modifier_list = vku::FindStructInPNextChain<VkImageDrmFormatModifierListCreateInfoEXT>(create_info.pNext);
            auto explicit_modifier = vku::FindStructInPNextChain<VkImageDrmFormatModifierExplicitCreateInfoEXT>(create_info.pNext);
            VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_format_modifier = vku::InitStructHelper();
            drm_format_modifier.sharingMode = create_info.sharingMode;
            drm_format_modifier.queueFamilyIndexCount = create_info.queueFamilyIndexCount;
            drm_format_modifier.pQueueFamilyIndices = create_info.pQueueFamilyIndices;
            vvl::PnextChainScopedAdd scoped_add_drm_fmt_mod(&image_format_info, &drm_format_modifier);

            if (modifier_list) {
                for (uint32_t i = 0; i < modifier_list->drmFormatModifierCount; i++) {
                    drm_format_modifier.drmFormatModifier = modifier_list->pDrmFormatModifiers[i];
                    result = DispatchGetPhysicalDeviceImageFormatProperties2Helper(api_version, physical_device, &image_format_info,
                                                                                   &image_properties);
                    if (result == VK_SUCCESS) {
                        compatible_types = external_image_properties.externalMemoryProperties.compatibleHandleTypes;
                        if ((external_memory_create_info->handleTypes & compatible_types) ==
                            external_memory_create_info->handleTypes)
                            break;
                    }
                }
                if (compatible_types != 0) result = VK_SUCCESS;
            } else if (explicit_modifier) {
                drm_format_modifier.drmFormatModifier = explicit_modifier->drmFormatModifier;
                result = DispatchGetPhysicalDeviceImageFormatProperties2Helper(api_version, physical_device, &image_format_info,
                                                                               &image_properties);
                compatible_types = external_image_properties.externalMemoryProperties.compatibleHandleTypes;
            }
        }

        if (result != VK_SUCCESS) {
            skip |= LogError(
                "VUID-VkImageCreateInfo-pNext-00990", device, create_info_loc,
                "The handle type (%s) with VkImageCreateInfo\n%s"
                "is not supported combination of parameters and vkGetPhysicalDeviceImageFormatProperties2 returned back %s.",
                string_VkExternalMemoryHandleTypeFlagBits(external_image_info.handleType),
                string_VkPhysicalDeviceImageFormatInfo2(image_format_info).c_str(), string_VkResult(result));
        } else if ((external_memory_create_info->handleTypes & compatible_types) != external_memory_create_info->handleTypes) {
            skip |= LogError(
                "VUID-VkImageCreateInfo-pNext-00990", device,
                create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfo, Field::handleTypes),
                "(%s) is not reported as compatible by vkGetPhysicalDeviceImageFormatProperties2. Compatible types are %s.",
                string_VkExternalMemoryHandleTypeFlags(external_memory_create_info->handleTypes).c_str(),
                string_VkExternalMemoryHandleTypeFlags(compatible_types).c_str());
        }
    } else if (external_memory_create_info_nv && external_memory_create_info_nv->handleTypes != 0) {
        if (create_info.initialLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
            skip |= LogError("VUID-VkImageCreateInfo-pNext-01443", device,
                             create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfoNV, Field::handleTypes),
                             "is %s (non-zero) but pCreateInfo->initialLayout is %s.",
                             string_VkExternalMemoryHandleTypeFlagsNV(external_memory_create_info_nv->handleTypes).c_str(),
                             string_VkImageLayout(create_info.initialLayout));
        }
        // Check external memory handle types compatibility
        const uint32_t any_type = 1u << MostSignificantBit(external_memory_create_info_nv->handleTypes);
        auto handle_type = static_cast<VkExternalMemoryHandleTypeFlagBitsNV>(any_type);
        VkExternalImageFormatPropertiesNV external_image_properties = {};
        result = DispatchGetPhysicalDeviceExternalImageFormatPropertiesNV(
            physical_device, create_info.format, create_info.imageType, create_info.tiling, create_info.usage, create_info.flags,
            handle_type, &external_image_properties);
        const auto compatible_types = external_image_properties.compatibleHandleTypes;

        if (result != VK_SUCCESS) {
            skip |= LogError("VUID-VkImageCreateInfo-pNext-00991", device, create_info_loc,
                             "The handle type (%s) with VkImageCreateInfo\n%s"
                             "is not supported combination of parameters and vkGetPhysicalDeviceExternalImageFormatPropertiesNV "
                             "returned back %s.",
                             string_VkExternalMemoryHandleTypeFlagBitsNV(handle_type),
                             string_VkPhysicalDeviceImageFormatInfo2(image_format_info).c_str(), string_VkResult(result));
        } else if ((external_memory_create_info_nv->handleTypes & compatible_types) !=
                   external_memory_create_info_nv->handleTypes) {
            skip |= LogError("VUID-VkImageCreateInfo-pNext-00991", device,
                             create_info_loc.pNext(Struct::VkExternalMemoryImageCreateInfoNV, Field::handleTypes),
                             "(%s) is not reported as compatible by vkGetPhysicalDeviceExternalImageFormatPropertiesNV.",
                             string_VkExternalMemoryHandleTypeFlagsNV(external_memory_create_info_nv->handleTypes).c_str());
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo,
                                            const VkAllocationCallbacks* pAllocator, VkImage* pImage,
                                            const ErrorObject& error_obj) const {
    bool skip = false;
    skip |= ValidateDeviceQueueSupport(error_obj.location);
    const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo);
    const Location create_flags_loc = GetFlagsLocation(*pCreateInfo, create_info_loc);
    const Location usage_loc = GetUsageLocation(*pCreateInfo, create_info_loc);
    const VkImageCreateFlags2KHR create_flags = GetImageCreateFlags(*pCreateInfo);
    const VkImageUsageFlags2KHR usage = GetImageUsageFlags(*pCreateInfo);
    if (IsExtEnabled(extensions.vk_android_external_memory_android_hardware_buffer)) {
        skip |= ValidateCreateImageANDROID(*pCreateInfo, create_info_loc, create_flags, usage);
    } else {  // These checks are omitted or replaced when Android HW Buffer extension is active
        if (pCreateInfo->format == VK_FORMAT_UNDEFINED) {
            return LogError("VUID-VkImageCreateInfo-pNext-01975", device, create_info_loc.dot(Field::format),
                            "must not be VK_FORMAT_UNDEFINED.");
        }
    }

    const VkImageUsageFlags attach_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
                                           VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
    if (usage & attach_flags) {
        if (pCreateInfo->extent.width > phys_dev_props.limits.maxFramebufferWidth) {
            skip |= LogError("VUID-VkImageCreateInfo-usage-00964", device, usage_loc,
                             "(%s) includes a frame buffer attachment bit and image width (%" PRIu32
                             ") is greater than maxFramebufferWidth (%" PRIu32 ").",
                             string_VkImageUsageFlags2KHR(usage).c_str(), pCreateInfo->extent.width,
                             phys_dev_props.limits.maxFramebufferWidth);
        }
        if (pCreateInfo->extent.height > phys_dev_props.limits.maxFramebufferHeight) {
            skip |= LogError("VUID-VkImageCreateInfo-usage-00965", device, usage_loc,
                             "(%s) includes a frame buffer attachment bit and image height (%" PRIu32
                             ") is greater than maxFramebufferHeight (%" PRIu32 ").",
                             string_VkImageUsageFlags2KHR(usage).c_str(), pCreateInfo->extent.height,
                             phys_dev_props.limits.maxFramebufferHeight);
        }
    }

    if (!enabled_features.fragmentDensityMapOffset && (usage & VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT)) {
        uint32_t ceiling_width = static_cast<uint32_t>(ceilf(
            static_cast<float>(phys_dev_props.limits.maxFramebufferWidth) /
            std::max(static_cast<float>(phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.width), 1.0f)));
        if (pCreateInfo->extent.width > ceiling_width) {
            skip |= LogError(
                "VUID-VkImageCreateInfo-fragmentDensityMapOffset-06514", device, usage_loc,
                "includes VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT and image width (%" PRIu32 ") is greater than %" PRIu32
                ".\n"
                "This is ceiling value of maxFramebufferWidth (%" PRIu32 ") / minFragmentDensityTexelSize.width (%" PRIu32 ").",
                pCreateInfo->extent.width, ceiling_width, phys_dev_props.limits.maxFramebufferWidth,
                phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.width);
        }

        uint32_t ceiling_height = static_cast<uint32_t>(ceilf(
            static_cast<float>(phys_dev_props.limits.maxFramebufferHeight) /
            std::max(static_cast<float>(phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.height), 1.0f)));
        if (pCreateInfo->extent.height > ceiling_height) {
            skip |= LogError(
                "VUID-VkImageCreateInfo-fragmentDensityMapOffset-06515", device, usage_loc,
                "includes VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT and image height (%" PRIu32 ") is greater than %" PRIu32
                ".\n"
                "This is ceiling value of maxFramebufferHeight (%" PRIu32 ") / minFragmentDensityTexelSize.height (%" PRIu32 ").",
                pCreateInfo->extent.height, ceiling_height, phys_dev_props.limits.maxFramebufferHeight,
                phys_dev_ext_props.fragment_density_map_props.minFragmentDensityTexelSize.height);
        }
    }

    VkImageFormatProperties2 image_format_properties = vku::InitStructHelper();
    VkPhysicalDeviceImageFormatInfo2 image_format_info = vku::InitStructHelper();
    image_format_info.type = pCreateInfo->imageType;
    image_format_info.format = pCreateInfo->format;
    image_format_info.tiling = pCreateInfo->tiling;
    image_format_info.usage = pCreateInfo->usage;
    image_format_info.flags = pCreateInfo->flags;

    vvl::PnextChainVkPhysicalDeviceImageFormatInfo2 image_format_info_pnext_chain{};
    image_format_info.pNext = vvl::PnextChainExtract(pCreateInfo->pNext, image_format_info_pnext_chain);

    // Exit early if any thing is not succesful
    VkResult result = VK_SUCCESS;
    if (pCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
        if (IsExtEnabled(extensions.vk_khr_get_physical_device_properties2)) {
            result = DispatchGetPhysicalDeviceImageFormatProperties2Helper(api_version, physical_device, &image_format_info,
                                                                           &image_format_properties);
        } else {
            result = DispatchGetPhysicalDeviceImageFormatProperties(physical_device, pCreateInfo->format, pCreateInfo->imageType,
                                                                    pCreateInfo->tiling, pCreateInfo->usage, pCreateInfo->flags,
                                                                    &image_format_properties.imageFormatProperties);
        }

        // 1. vkGetPhysicalDeviceImageFormatProperties[2] only success code is VK_SUCCESS
        // 2. If call returns an error, then "imageCreateImageFormatPropertiesList" is defined to be the empty list
        // 3. All values in 02251 are undefined if "imageCreateImageFormatPropertiesList" is empty.
        if (result != VK_SUCCESS) {
            // External memory will always have a "imageCreateImageFormatPropertiesList" so skip
#ifdef VK_USE_PLATFORM_ANDROID_KHR
            if (!vku::FindStructInPNextChain<VkExternalFormatANDROID>(pCreateInfo->pNext)) {
#endif  // VK_USE_PLATFORM_ANDROID_KHR
                Func command = IsExtEnabled(extensions.vk_khr_get_physical_device_properties2)
                                   ? Func::vkGetPhysicalDeviceImageFormatProperties2
                                   : Func::vkGetPhysicalDeviceImageFormatProperties;
                skip |= LogError("VUID-VkImageCreateInfo-imageCreateMaxMipLevels-02251", device, error_obj.location,
                                 "The following VkImageCreateInfo returned %s when calling %s\n%s%s", string_VkResult(result),
                                 String(command), string_VkPhysicalDeviceImageFormatInfo2(image_format_info).c_str(),
                                 PrintPNextChain(Struct::VkImageCreateInfo, pCreateInfo->pNext).c_str());
#ifdef VK_USE_PLATFORM_ANDROID_KHR
            }
#endif  // VK_USE_PLATFORM_ANDROID_KHR
        }
    } else {
        auto* modifier_list = vku::FindStructInPNextChain<VkImageDrmFormatModifierListCreateInfoEXT>(pCreateInfo->pNext);
        auto* explicit_modifier = vku::FindStructInPNextChain<VkImageDrmFormatModifierExplicitCreateInfoEXT>(pCreateInfo->pNext);
        VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_format_modifier = vku::InitStructHelper();
        drm_format_modifier.sharingMode = pCreateInfo->sharingMode;
        drm_format_modifier.queueFamilyIndexCount = pCreateInfo->queueFamilyIndexCount;
        drm_format_modifier.pQueueFamilyIndices = pCreateInfo->pQueueFamilyIndices;
        vvl::PnextChainScopedAdd scoped_add_drm_fmt_mod(&image_format_info, &drm_format_modifier);

        if (modifier_list) {
            for (uint32_t i = 0; i < modifier_list->drmFormatModifierCount; i++) {
                drm_format_modifier.drmFormatModifier = modifier_list->pDrmFormatModifiers[i];
                result = DispatchGetPhysicalDeviceImageFormatProperties2Helper(api_version, physical_device, &image_format_info,
                                                                               &image_format_properties);

                // The application gives a list of modifier and the driver selects one. If one is valid, stop there.
                if (result == VK_SUCCESS) {
                    break;
                }
            }
        } else if (explicit_modifier) {
            drm_format_modifier.drmFormatModifier = explicit_modifier->drmFormatModifier;
            result = DispatchGetPhysicalDeviceImageFormatProperties2Helper(api_version, physical_device, &image_format_info,
                                                                           &image_format_properties);
        }

        if (result != VK_SUCCESS) {
            // Will not have to worry about VkExternalFormatANDROID if using DRM format modifier
            std::string drm_source = modifier_list ? "pDrmFormatModifiers[]" : "VkImageDrmFormatModifierExplicitCreateInfoEXT";
            skip |= LogError("VUID-VkImageCreateInfo-imageCreateMaxMipLevels-02251", device, error_obj.location,
                             "The following VkImageCreateInfo returned %s when calling "
                             "VkGetPhysicalDeviceImageFormatProperties2\ndrmFormatModifier (%" PRIu64 ") from %s\n%s%s",
                             string_VkResult(result), drm_format_modifier.drmFormatModifier, drm_source.c_str(),
                             string_VkPhysicalDeviceImageFormatInfo2(image_format_info).c_str(),
                             PrintPNextChain(Struct::VkImageCreateInfo, pCreateInfo->pNext).c_str());
        }
    }

    // only check if we got valid image format info back
    if (result == VK_SUCCESS) {
        const auto format_limits = image_format_properties.imageFormatProperties;
        if (pCreateInfo->mipLevels > format_limits.maxMipLevels) {
            skip |= LogError("VUID-VkImageCreateInfo-mipLevels-02255", device, create_info_loc.dot(Field::mipLevels),
                             "(%" PRIu32 ") exceeds the maxMipLevels (%" PRIu32 ") for format %s.", pCreateInfo->mipLevels,
                             format_limits.maxMipLevels, string_VkFormat(pCreateInfo->format));
        }

        // This likely will only ever happen for 1D images, don't spend time checking things we can't properly calculate.
        // Depth/Stencil formats size can't be accurately calculated.
        // MipLevel image are hard to calculate (because when it is a non-power of 2) so just ignore.
        // Multisampling size can't be calculated
        if (pCreateInfo->imageType == VK_IMAGE_TYPE_1D && pCreateInfo->samples == VK_SAMPLE_COUNT_1_BIT &&
            pCreateInfo->mipLevels == 1 && !vkuFormatIsDepthAndStencil(pCreateInfo->format) &&
            !vkuFormatIsMultiplane(pCreateInfo->format)) {
            const uint64_t texel_block_count =
                (static_cast<uint64_t>(pCreateInfo->extent.width) * static_cast<uint64_t>(pCreateInfo->arrayLayers)) /
                vkuFormatTexelBlockExtent(pCreateInfo->format).width;

            uint64_t total_size =
                static_cast<uint64_t>(vkuFormatTexelBlockSize(pCreateInfo->format)) * static_cast<uint64_t>(texel_block_count);

            // Round up to imageGranularity boundary
            VkDeviceSize image_granularity = phys_dev_props.limits.bufferImageGranularity;
            uint64_t ig_mask = image_granularity - 1;
            total_size = (total_size + ig_mask) & ~ig_mask;

            if (total_size > format_limits.maxResourceSize) {
                skip |= LogWarning("WARNING-VkImageCreateInfo-maxResourceSize", device, error_obj.location,
                                   "will be %" PRIu64 " bytes and the VkImageFormatProperties::maxResourceSize is %" PRIu64
                                   ".\nThe total was calulated from format %s having a texel block size of %" PRIu32
                                   " bytes with %" PRIu64 " texel blocks and a bufferImageGranularity of %" PRIu64 ".",
                                   total_size, format_limits.maxResourceSize, string_VkFormat(pCreateInfo->format),
                                   (uint32_t)vkuFormatTexelBlockSize(pCreateInfo->format), texel_block_count, image_granularity);
            }
        }

        if (pCreateInfo->arrayLayers > format_limits.maxArrayLayers) {
            skip |= LogError("VUID-VkImageCreateInfo-arrayLayers-02256", device, create_info_loc.dot(Field::arrayLayers),
                             "(%" PRIu32 ") exceeds the maxArrayLayers (%" PRIu32 ") for %s", pCreateInfo->arrayLayers,
                             format_limits.maxArrayLayers, string_VkFormat(pCreateInfo->format));
        }

        if ((pCreateInfo->samples & format_limits.sampleCounts) == 0) {
            skip |= LogError("VUID-VkImageCreateInfo-samples-02258", device, create_info_loc.dot(Field::samples),
                             "(%s) is not supported by format %s\nsupported sampleCounts: %s",
                             string_VkSampleCountFlagBits(pCreateInfo->samples), string_VkFormat(pCreateInfo->format),
                             string_VkSampleCountFlags(format_limits.sampleCounts).c_str());
        }

        if (pCreateInfo->extent.width > format_limits.maxExtent.width) {
            skip |= LogError("VUID-VkImageCreateInfo-extent-02252", device, create_info_loc.dot(Field::extent).dot(Field::width),
                             "(%" PRIu32 ") exceeds the maximum image extent width (%" PRIu32 ") for format %s.",
                             pCreateInfo->extent.width, format_limits.maxExtent.width, string_VkFormat(pCreateInfo->format));
        }

        if (pCreateInfo->extent.height > format_limits.maxExtent.height) {
            skip |= LogError("VUID-VkImageCreateInfo-extent-02253", device, create_info_loc.dot(Field::extent).dot(Field::height),
                             "(%" PRIu32 ") exceeds the maximum image extent height (%" PRIu32 ") for format %s.",
                             pCreateInfo->extent.height, format_limits.maxExtent.height, string_VkFormat(pCreateInfo->format));
        }

        if (pCreateInfo->extent.depth > format_limits.maxExtent.depth) {
            skip |= LogError("VUID-VkImageCreateInfo-extent-02254", device, create_info_loc.dot(Field::extent).dot(Field::depth),
                             "(%" PRIu32 ") exceeds the maximum image extent depth (%" PRIu32 ") for format %s.",
                             pCreateInfo->extent.depth, format_limits.maxExtent.depth, string_VkFormat(pCreateInfo->format));
        }
    }

    // Tests for "Formats requiring sampler YCBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views"
    if (FormatRequiresYcbcrConversionExplicitly(pCreateInfo->format)) {
        if (pCreateInfo->mipLevels != 1) {
            skip |= LogError("VUID-VkImageCreateInfo-format-06410", device, create_info_loc.dot(Field::mipLevels),
                             "(%" PRIu32 "), but when using a YCbCr Conversion format (%s), mipLevels must be 1.",
                             pCreateInfo->mipLevels, string_VkFormat(pCreateInfo->format));
        }

        if (pCreateInfo->samples != VK_SAMPLE_COUNT_1_BIT) {
            skip |= LogError("VUID-VkImageCreateInfo-format-06411", device, create_info_loc.dot(Field::samples),
                             "(%s), but when using a YCbCr Conversion format (%s), samples must be "
                             "VK_SAMPLE_COUNT_1_BIT.",
                             string_VkSampleCountFlagBits(pCreateInfo->samples), string_VkFormat(pCreateInfo->format));
        }

        if (pCreateInfo->imageType != VK_IMAGE_TYPE_2D) {
            skip |= LogError("VUID-VkImageCreateInfo-format-06412", device, create_info_loc.dot(Field::imageType),
                             "(%s), but when using a YCbCr Conversion format (%s), imageType must be "
                             "VK_IMAGE_TYPE_2D.",
                             string_VkImageType(pCreateInfo->imageType), string_VkFormat(pCreateInfo->format));
        }
    }

    if (IsExtEnabled(extensions.vk_khr_maintenance2)) {
        if (create_flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT) {
            if (!vkuFormatIsCompressed(pCreateInfo->format)) {
                skip |= LogError(
                    "VUID-VkImageCreateInfo-flags-01572", device, create_info_loc.dot(Field::flags),
                    "contains VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT, but format (%s) must be a compressed image format.",
                    string_VkFormat(pCreateInfo->format));
            }
            if (!(create_flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT)) {
                skip |= LogError("VUID-VkImageCreateInfo-flags-01573", device, create_info_loc.dot(Field::flags),
                                 "contains VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT, "
                                 "flags must also contain VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT.");
            }
        }
    }

    if (pCreateInfo->sharingMode == VK_SHARING_MODE_CONCURRENT && pCreateInfo->pQueueFamilyIndices) {
        skip |= ValidatePhysicalDeviceQueueFamilies(pCreateInfo->queueFamilyIndexCount, pCreateInfo->pQueueFamilyIndices,
                                                    create_info_loc, "VUID-VkImageCreateInfo-sharingMode-01420");
    }

    if (!vkuFormatIsMultiplane(pCreateInfo->format) && !(create_flags & VK_IMAGE_CREATE_ALIAS_BIT) &&
        (create_flags & VK_IMAGE_CREATE_DISJOINT_BIT)) {
        skip |= LogError("VUID-VkImageCreateInfo-format-01577", device, create_info_loc,
                         "format is %s and flags are %s. The flags should not include VK_IMAGE_CREATE_DISJOINT_BIT.",
                         string_VkFormat(pCreateInfo->format), string_VkImageCreateFlags2KHR(create_flags).c_str());
    }

    skip |= ValidateImageFormatFeatures(*pCreateInfo, create_info_loc, create_flags, usage);
    skip |= ValidateImageAlignmentControlCreateInfo(*pCreateInfo, create_info_loc);
    skip |= ValidateImageVideo(*pCreateInfo, create_info_loc, create_flags, create_flags_loc, usage, usage_loc, error_obj);
    skip |= ValidateImageSwapchain(*pCreateInfo, create_info_loc, create_flags, usage);
    skip |= ValidateImageExternalMemory(*pCreateInfo, create_info_loc, image_format_info);

    if (device_state->device_group_create_info.physicalDeviceCount == 1) {
        if (create_flags & VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT) {
            skip |= LogError("VUID-VkImageCreateInfo-physicalDeviceCount-01421", device, create_flags_loc,
                             "contains VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT, but the device was created with "
                             "VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to 1. Device creation with "
                             "VkDeviceGroupDeviceCreateInfo::physicalDeviceCount equal to 1 may have been implicit.");
        }
    }

    auto opaque_data = vku::FindStructInPNextChain<VkOpaqueCaptureDataCreateInfoEXT>(pCreateInfo->pNext);
    if (opaque_data && opaque_data->pData) {
        if ((create_flags & VK_IMAGE_CREATE_DESCRIPTOR_HEAP_CAPTURE_REPLAY_BIT_EXT) == 0) {
            skip |= LogError("VUID-VkImageCreateInfo-flags-11281", device,
                             create_info_loc.pNext(Struct::VkOpaqueCaptureDataCreateInfoEXT, Field::pData),
                             "is not null, but VkImageCreateInfo flags (%s) does contain "
                             "VK_IMAGE_CREATE_DESCRIPTOR_HEAP_CAPTURE_REPLAY_BIT_EXT.",
                             string_VkImageCreateFlags2KHR(create_flags).c_str());
        } else {
            if (opaque_data->pData->size != phys_dev_ext_props.descriptor_heap_props.imageCaptureReplayOpaqueDataSize) {
                skip |= LogError("VUID-VkImageCreateInfo-pData-11286", device,
                                 create_info_loc.pNext(Struct::VkOpaqueCaptureDataCreateInfoEXT, Field::pData).dot(Field::size),
                                 "is %zu, that is not equal to imageCaptureReplayOpaqueDataSize is %zu.", opaque_data->pData->size,
                                 phys_dev_ext_props.descriptor_heap_props.imageCaptureReplayOpaqueDataSize);
            }
        }
    }

    return skip;
}

void CoreChecks::PostCallRecordCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo,
                                           const VkAllocationCallbacks* pAllocator, VkImage* pImage,
                                           const RecordObject& record_obj) {
    if (record_obj.result != VK_SUCCESS) {
        return;
    }

    const VkImageCreateFlags2KHR create_flags = GetImageCreateFlags(*pCreateInfo);
    if ((create_flags & VK_IMAGE_CREATE_SPARSE_BINDING_BIT) != 0) {
        // non-sparse images set up their layout maps when memory is bound
        if (auto image_state = Get<vvl::Image>(*pImage)) {
            image_state->SetInitialLayoutMap();
        }
    }
}

bool CoreChecks::PreCallValidateDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator,
                                             const ErrorObject& error_obj) const {
    bool skip = false;
    if (auto image_state = Get<vvl::Image>(image)) {
        if (image_state->IsSwapchainImage() && image_state->owned_by_swapchain) {
            skip |= LogError("VUID-vkDestroyImage-image-04882", image, error_obj.location.dot(Field::image),
                             "%s is a presentable image controlled by the implementation and must be destroyed "
                             "with vkDestroySwapchainKHR.",
                             FormatHandle(image_state->Handle()).c_str());
        }
        skip |= ValidateObjectNotInUse(image_state.get(), error_obj.location, "VUID-vkDestroyImage-image-01000");
    }
    return skip;
}

void CoreChecks::PreCallRecordDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator,
                                           const RecordObject& record_obj) {
    // Clean up validation specific data
    qfo_release_image_barrier_map.erase(image);
}

bool CoreChecks::ValidateClearImageSubresourceRange(const LogObjectList& objlist, const VkImageSubresourceRange& range,
                                                    const Location& loc) const {
    bool skip = false;

    if (range.aspectMask != VK_IMAGE_ASPECT_COLOR_BIT) {
        skip |= LogError("VUID-vkCmdClearColorImage-aspectMask-02498", objlist, loc.dot(Field::aspectMask),
                         "is %s (must only include COLOR_BIT).", string_VkImageAspectFlags(range.aspectMask).c_str());
    }

    return skip;
}

bool CoreChecks::PreCallValidateCmdClearColorImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
                                                   const VkClearColorValue* pColor, uint32_t rangeCount,
                                                   const VkImageSubresourceRange* pRanges, const ErrorObject& error_obj) const {
    bool skip = false;
    // TODO : Verify memory is in VK_IMAGE_STATE_CLEAR state
    auto cb_state_ptr = GetRead<vvl::CommandBuffer>(commandBuffer);
    auto image_state_ptr = Get<vvl::Image>(image);
    ASSERT_AND_RETURN_SKIP(image_state_ptr);

    const auto& cb_state = *cb_state_ptr;
    const auto& image_state = *image_state_ptr;
    const Location image_loc = error_obj.location.dot(Field::image);
    LogObjectList objlist(commandBuffer, image);
    skip |= ValidateMemoryIsBoundToImage(objlist, image_state, image_loc, "VUID-vkCmdClearColorImage-image-00003");
    skip |= ValidateCmd(cb_state, error_obj.location);
    if (IsExtEnabled(extensions.vk_khr_maintenance1)) {
        skip |= ValidateImageFormatFeatureFlags(commandBuffer, image_state, VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT, image_loc,
                                                "VUID-vkCmdClearColorImage-image-01993");
    }
    skip |= ValidateProtectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearColorImage-commandBuffer-01805");
    skip |= ValidateUnprotectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearColorImage-commandBuffer-01806");
    for (uint32_t i = 0; i < rangeCount; ++i) {
        const Location range_loc = error_obj.location.dot(Field::pRanges, i);
        skip |= ValidateCmdClearColorSubresourceRange(image_state, pRanges[i], objlist, range_loc);
        skip |= ValidateClearImageSubresourceRange(objlist, pRanges[i], range_loc);
        skip |= ValidateClearImageLayout(cb_state, image_state, pRanges[i], imageLayout, range_loc);
    }

    const VkFormat format = image_state.GetFormat();
    if (vkuFormatIsDepthOrStencil(format)) {
        skip |=
            LogError("VUID-vkCmdClearColorImage-image-00007", objlist, image_loc,
                     "(%s) was created with a depth/stencil format (%s).", FormatHandle(image).c_str(), string_VkFormat(format));
    } else if (vkuFormatIsCompressed(format)) {
        skip |= LogError("VUID-vkCmdClearColorImage-image-00007", objlist, image_loc,
                         "(%s) was created with a compressed format (%s).", FormatHandle(image).c_str(), string_VkFormat(format));
    }
    if (vkuFormatIs64bit(format) && vkuFormatComponentCount(format) > 2) {
        skip |= LogError("VUID-vkCmdClearColorImage-image-09678", objlist, image_loc,
                         "(%s) was created with a 64-bit format (%s) but it has more than 2 components. The clear command can only "
                         "clear 16 bytes so this format is too large",
                         FormatHandle(image).c_str(), string_VkFormat(format));
    }

    if (!(image_state.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT)) {
        skip |= LogError("VUID-vkCmdClearColorImage-image-00002", objlist, image_loc,
                         "(%s) was created with usage %s (missing VK_IMAGE_USAGE_TRANSFER_DST_BIT).", FormatHandle(image).c_str(),
                         string_VkImageUsageFlags2KHR(image_state.usage).c_str());
    }

    // Tests for "Formats requiring sampler Y’CBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views"
    if (FormatRequiresYcbcrConversionExplicitly(format)) {
        skip |= LogError("VUID-vkCmdClearColorImage-image-01545", objlist, image_loc, "(%s) was created with format %s.",
                         FormatHandle(image).c_str(), string_VkFormat(format));
    }
    return skip;
}

bool CoreChecks::ValidateClearDepthStencilValue(VkCommandBuffer commandBuffer, VkClearDepthStencilValue clearValue,
                                                const Location& loc) const {
    bool skip = false;

    if (!IsExtEnabled(extensions.vk_ext_depth_range_unrestricted)) {
        const bool depth_in_unit_range = (clearValue.depth >= 0.0 && clearValue.depth <= 1.0);
        if (!depth_in_unit_range) {
            skip |=
                LogError("VUID-VkClearDepthStencilValue-depth-00022", commandBuffer, loc.dot(Field::depth),
                         "is %f (not within the [0.0, 1.0] range) but VK_EXT_depth_range_unrestricted extension is not enabled.",
                         clearValue.depth);
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateCmdClearDepthStencilImage(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout,
                                                          const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount,
                                                          const VkImageSubresourceRange* pRanges,
                                                          const ErrorObject& error_obj) const {
    bool skip = false;

    // TODO : Verify memory is in VK_IMAGE_STATE_CLEAR state
    auto cb_state_ptr = GetRead<vvl::CommandBuffer>(commandBuffer);
    auto image_state_ptr = Get<vvl::Image>(image);
    ASSERT_AND_RETURN_SKIP(image_state_ptr);

    const auto& cb_state = *cb_state_ptr;
    const auto& image_state = *image_state_ptr;
    const Location image_loc = error_obj.location.dot(Field::image);

    const VkFormat image_format = image_state.GetFormat();
    const LogObjectList objlist(commandBuffer, image);
    skip |= ValidateMemoryIsBoundToImage(objlist, image_state, image_loc, "VUID-vkCmdClearDepthStencilImage-image-00010");
    skip |= ValidateCmd(cb_state, error_obj.location);
    if (IsExtEnabled(extensions.vk_khr_maintenance1)) {
        skip |= ValidateImageFormatFeatureFlags(commandBuffer, image_state, VK_FORMAT_FEATURE_2_TRANSFER_DST_BIT, image_loc,
                                                "VUID-vkCmdClearDepthStencilImage-image-01994");
    }
    skip |= ValidateClearDepthStencilValue(commandBuffer, *pDepthStencil, error_obj.location.dot(Field::pDepthStencil));
    skip |= ValidateProtectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearDepthStencilImage-commandBuffer-01807");
    skip |= ValidateUnprotectedImage(cb_state, image_state, image_loc, "VUID-vkCmdClearDepthStencilImage-commandBuffer-01808");

    for (uint32_t i = 0; i < rangeCount; ++i) {
        const Location range_loc = error_obj.location.dot(Field::pRanges, i);
        skip |= ValidateCmdClearDepthSubresourceRange(image_state, pRanges[i], objlist, range_loc);
        skip |= ValidateClearImageLayout(cb_state, image_state, pRanges[i], imageLayout, range_loc);
        // Image aspect must be depth or stencil or both
        VkImageAspectFlags valid_aspects = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
        if (((pRanges[i].aspectMask & valid_aspects) == 0) || ((pRanges[i].aspectMask & ~valid_aspects) != 0)) {
            skip |=
                LogError("VUID-vkCmdClearDepthStencilImage-aspectMask-02824", objlist, range_loc.dot(Field::aspectMask),
                         "is %s (can only be DEPTH_BIT or STENCIL_BIT).", string_VkImageAspectFlags(pRanges[i].aspectMask).c_str());
        }
        if ((pRanges[i].aspectMask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0) {
            if (vkuFormatHasDepth(image_format) == false) {
                skip |= LogError("VUID-vkCmdClearDepthStencilImage-image-02826", objlist, range_loc.dot(Field::aspectMask),
                                 "has a VK_IMAGE_ASPECT_DEPTH_BIT but %s "
                                 "doesn't have a depth component.",
                                 string_VkFormat(image_format));
            }
            if ((image_state.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
                skip |= LogError(
                    "VUID-vkCmdClearDepthStencilImage-pRanges-02660", objlist, range_loc.dot(Field::aspectMask),
                    "includes VK_IMAGE_ASPECT_DEPTH_BIT, but the image was not created with VK_IMAGE_USAGE_TRANSFER_DST_BIT.");
            }
        }
        if ((pRanges[i].aspectMask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0) {
            if (vkuFormatHasStencil(image_format) == false) {
                skip |= LogError("VUID-vkCmdClearDepthStencilImage-image-02825", objlist, range_loc.dot(Field::aspectMask),
                                 "has a VK_IMAGE_ASPECT_STENCIL_BIT but "
                                 "%s doesn't have a stencil component.",
                                 string_VkFormat(image_format));
            }

            if (image_state.stencil_usage.has_value()) {
                if ((image_state.stencil_usage.value() & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
                    skip |= LogError("VUID-vkCmdClearDepthStencilImage-pRanges-02658", objlist, range_loc.dot(Field::aspectMask),
                                     "includes VK_IMAGE_ASPECT_STENCIL_BIT and "
                                     "image was created with VkImageStencilUsageCreateInfo::stencilUsage = %s.",
                                     string_VkImageUsageFlags2KHR(image_state.stencil_usage.value()).c_str());
                }
            } else if ((image_state.usage & VK_IMAGE_USAGE_TRANSFER_DST_BIT) == 0) {
                skip |= LogError("VUID-vkCmdClearDepthStencilImage-pRanges-02659", objlist, range_loc.dot(Field::aspectMask),
                                 "includes VK_IMAGE_ASPECT_STENCIL_BIT and "
                                 "image was not created with VkImageStencilUsageCreateInfo, but was created with "
                                 "VK_IMAGE_USAGE_TRANSFER_DST_BIT.");
            }
        }
    }

    if (!vkuFormatIsDepthOrStencil(image_format)) {
        skip |=
            LogError("VUID-vkCmdClearDepthStencilImage-image-00014", objlist, image_loc,
                     "(%s) doesn't have a depth/stencil format (%s).", FormatHandle(image).c_str(), string_VkFormat(image_format));
    }
    if (VK_IMAGE_USAGE_TRANSFER_DST_BIT != (VK_IMAGE_USAGE_TRANSFER_DST_BIT & image_state.usage)) {
        skip |= LogError("VUID-vkCmdClearDepthStencilImage-pRanges-02659", objlist, image_loc,
                         "(%s) was not created with the "
                         "VK_IMAGE_USAGE_TRANSFER_DST_BIT set.",
                         FormatHandle(image).c_str());
    }
    return skip;
}

bool CoreChecks::ValidateClearAttachmentExtent(const vvl::CommandBuffer& cb_state, const VkRect2D& render_area,
                                               uint32_t render_pass_layer_count, uint32_t rect_count,
                                               const VkClearRect* clear_rects, const Location& loc) const {
    bool skip = false;

    for (uint32_t i = 0; i < rect_count; i++) {
        if (!ContainsRect(render_area, clear_rects[i].rect)) {
            skip |=
                LogError("VUID-vkCmdClearAttachments-pRects-00016", cb_state.Handle(), loc.dot(Field::pRects, i).dot(Field::rect),
                         "(%s) is not contained in the area of "
                         "the current render pass instance (%s).",
                         string_VkRect2D(clear_rects[i].rect).c_str(), string_VkRect2D(render_area).c_str());
        }

        const uint32_t rect_base_layer = clear_rects[i].baseArrayLayer;
        const uint32_t rect_layer_count = clear_rects[i].layerCount;
        // The layer indices specified by elements of pRects must be inferior to render pass layer count
        if (rect_base_layer + rect_layer_count > render_pass_layer_count) {
            skip |= LogError(
                "VUID-vkCmdClearAttachments-pRects-06937", cb_state.Handle(), loc.dot(Field::pRects, i).dot(Field::baseArrayLayer),
                "(%" PRIu32 ") + layerCount (%" PRIu32 ") is %" PRIu32
                ", which is larger than the number of layers rendered to in the current render pass instance (%" PRIu32 ").",
                rect_base_layer, rect_layer_count, rect_base_layer + rect_layer_count, render_pass_layer_count);
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdClearAttachments(VkCommandBuffer commandBuffer, uint32_t attachmentCount,
                                                    const VkClearAttachment* pAttachments, uint32_t rectCount,
                                                    const VkClearRect* pRects, const ErrorObject& error_obj) const {
    bool skip = false;
    auto cb_state_ptr = GetRead<vvl::CommandBuffer>(commandBuffer);
    const vvl::CommandBuffer& cb_state = *cb_state_ptr;

    skip |= ValidateCmd(cb_state, error_obj.location);

    const vvl::RenderPass* rp_state = cb_state.active_render_pass.get();
    if (!rp_state) {
        return skip;
    }

    // Validate that attachments are in reference list of active subpass

    if (cb_state.IsPrimary()) {
        uint32_t layer_count = 0;
        if (rp_state->UsesDynamicRendering()) {
            layer_count = rp_state->dynamic_rendering_begin_rendering_info.layerCount;
        } else {
            layer_count = cb_state.active_framebuffer.get()->create_info.layers;
        }
        skip |= ValidateClearAttachmentExtent(cb_state, cb_state.render_area, layer_count, rectCount, pRects, error_obj.location);
    }

    for (uint32_t attachment_index = 0; attachment_index < attachmentCount; attachment_index++) {
        const Location& attachment_loc = error_obj.location.dot(Field::pAttachments, attachment_index);
        const VkClearAttachment& clear_desc = pAttachments[attachment_index];

        const VkImageAspectFlags aspect_mask = clear_desc.aspectMask;

        const vvl::ImageView* color_view_state = nullptr;
        uint32_t color_attachment_count = 0;

        const vvl::ImageView* depth_view_state = nullptr;
        const vvl::ImageView* stencil_view_state = nullptr;

        bool external_format_resolve = false;

        const bool is_dynamic_rendering = rp_state->UsesDynamicRendering();
        if (is_dynamic_rendering) {
            const uint32_t color_index = clear_desc.colorAttachment;

            if ((clear_desc.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0 && cb_state.rendering_attachments.set_color_locations &&
                color_index < cb_state.rendering_attachments.color_locations.size() &&
                cb_state.rendering_attachments.color_locations[color_index] == VK_ATTACHMENT_UNUSED) {
                const LogObjectList objlist(commandBuffer, rp_state->VkHandle());
                skip |= LogError("VUID-vkCmdClearAttachments-colorAttachment-09503", objlist, attachment_loc,
                                 "cannot be cleared because VkRenderingAttachmentLocationInfo::pColorAttachmentLocations[%" PRIu32
                                 "] is VK_ATTACHMENT_UNUSED.",
                                 color_index);
            }

            color_view_state =
                cb_state.GetActiveAttachmentImageViewState(cb_state.GetDynamicRenderingColorAttachmentIndex(color_index));
            color_attachment_count = cb_state.GetDynamicRenderingColorAttachmentCount();

            depth_view_state = cb_state.GetActiveAttachmentImageViewState(
                cb_state.GetDynamicRenderingAttachmentIndex(AttachmentInfo::Type::Depth));
            stencil_view_state = cb_state.GetActiveAttachmentImageViewState(
                cb_state.GetDynamicRenderingAttachmentIndex(AttachmentInfo::Type::Stencil));

            external_format_resolve = cb_state.HasExternalFormatResolveAttachment();
        } else {
            const auto* renderpass_create_info = rp_state->create_info.ptr();
            const auto* subpass_desc = &renderpass_create_info->pSubpasses[cb_state.GetActiveSubpass()];
            const auto* framebuffer = cb_state.active_framebuffer.get();

            if (subpass_desc) {
                if (framebuffer && (clear_desc.colorAttachment != VK_ATTACHMENT_UNUSED) &&
                    (clear_desc.colorAttachment < subpass_desc->colorAttachmentCount)) {
                    if (subpass_desc->pColorAttachments[clear_desc.colorAttachment].attachment <
                        framebuffer->create_info.attachmentCount) {
                        color_view_state = cb_state.GetActiveAttachmentImageViewState(
                            subpass_desc->pColorAttachments[clear_desc.colorAttachment].attachment);

                        if (subpass_desc->pResolveAttachments) {
                            const uint32_t resolve_attachment =
                                subpass_desc->pResolveAttachments[clear_desc.colorAttachment].attachment;
                            if (resolve_attachment != VK_ATTACHMENT_UNUSED) {
                                external_format_resolve =
                                    GetExternalFormat(renderpass_create_info->pAttachments[resolve_attachment].pNext) != 0;
                            }
                        }
                    }
                }

                color_attachment_count = subpass_desc->colorAttachmentCount;

                if (framebuffer && subpass_desc->pDepthStencilAttachment &&
                    (subpass_desc->pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED)) {
                    depth_view_state =
                        cb_state.GetActiveAttachmentImageViewState(subpass_desc->pDepthStencilAttachment->attachment);

                    if (depth_view_state) {
                        stencil_view_state = depth_view_state;

                        const VkFormat image_view_format = depth_view_state->create_info.format;
                        if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) && !vkuFormatHasDepth(image_view_format)) {
                            const LogObjectList objlist(commandBuffer, rp_state->Handle(), depth_view_state->Handle());
                            skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-07884", objlist, attachment_loc,
                                             "in pSubpasses[%" PRIu32
                                             "] has VK_IMAGE_ASPECT_DEPTH_BIT and is backed by an image view with format (%s).",
                                             cb_state.GetActiveSubpass(), string_VkFormat(image_view_format));
                        }

                        if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) && !vkuFormatHasStencil(image_view_format)) {
                            const LogObjectList objlist(commandBuffer, rp_state->Handle(), stencil_view_state->Handle());
                            skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-07885", objlist, attachment_loc,
                                             "in pSubpasses[%" PRIu32
                                             "] has VK_IMAGE_ASPECT_STENCIL_BIT and is backed by an image view with format (%s).",
                                             cb_state.GetActiveSubpass(), string_VkFormat(image_view_format));
                        }
                    }
                }
            }
        }

        if (aspect_mask & VK_IMAGE_ASPECT_METADATA_BIT) {
            const LogObjectList objlist(commandBuffer, rp_state->Handle());
            skip |= LogError("VUID-VkClearAttachment-aspectMask-00020", objlist, attachment_loc.dot(Field::aspectMask), "is %s.",
                             string_VkImageAspectFlags(aspect_mask).c_str());
        } else if (aspect_mask & (VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT |
                                  VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT)) {
            const LogObjectList objlist(commandBuffer, rp_state->Handle());
            skip |= LogError("VUID-VkClearAttachment-aspectMask-02246", objlist, attachment_loc.dot(Field::aspectMask), "is %s.",
                             string_VkImageAspectFlags(aspect_mask).c_str());
        } else if (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) {
            if (clear_desc.colorAttachment == VK_ATTACHMENT_UNUSED) {
                const LogObjectList objlist(commandBuffer, rp_state->Handle());
                skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-07271", objlist, attachment_loc.dot(Field::colorAttachment),
                                 "is VK_ATTACHMENT_UNUSED, but aspectMask is VK_IMAGE_ASPECT_COLOR_BIT.");
            } else if (color_attachment_count == 0) {
                const LogObjectList objlist(commandBuffer, rp_state->Handle());
                skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-07271", objlist, attachment_loc.dot(Field::aspectMask),
                                 "has VK_IMAGE_ASPECT_COLOR_BIT but there are no color attachments in the renderpass.");
            } else if (clear_desc.colorAttachment >= color_attachment_count) {
                auto describe_color_count = [is_dynamic_rendering, &cb_state]() {
                    std::ostringstream ss;
                    if (is_dynamic_rendering) {
                        ss << "VkRenderingInfo::colorAttachmentCount";
                    } else {
                        ss << "pSubpasses[" << cb_state.GetActiveSubpass() << "].colorAttachmentCount";
                    }
                    return ss.str();
                };
                const LogObjectList objlist(commandBuffer, rp_state->Handle());
                skip |=
                    LogError("VUID-vkCmdClearAttachments-aspectMask-07271", objlist, attachment_loc.dot(Field::colorAttachment),
                             "is index %" PRIu32 ", which is not less than colorAttachmentCount (%" PRIu32 ") which was set by %s",
                             clear_desc.colorAttachment, color_attachment_count, describe_color_count().c_str());
            }

            if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) || (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT)) {
                const LogObjectList objlist(commandBuffer, rp_state->Handle());
                skip |= LogError("VUID-VkClearAttachment-aspectMask-00019", objlist, attachment_loc.dot(Field::aspectMask),
                                 "is %s.", string_VkImageAspectFlags(aspect_mask).c_str());
            }

        } else if (aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) {
            skip |= ValidateClearDepthStencilValue(commandBuffer, clear_desc.clearValue.depthStencil,
                                                   attachment_loc.dot(Field::clearValue).dot(Field::depthStencil));
        } else if (external_format_resolve && IsAnyPlaneAspect(aspect_mask)) {
            const LogObjectList objlist(commandBuffer, rp_state->Handle());
            skip |= LogError("VUID-vkCmdClearAttachments-aspectMask-09298", objlist, attachment_loc.dot(Field::aspectMask),
                             "is %s.", string_VkImageAspectFlags(aspect_mask).c_str());
        }

        std::array<const vvl::ImageView*, 3> image_views = {nullptr, nullptr, nullptr};
        if (aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) {
            image_views[0] = color_view_state;
        }
        if (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) {
            image_views[1] = depth_view_state;
        }
        if (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) {
            image_views[2] = stencil_view_state;
        }
        if (image_views[1] == image_views[2]) {
            image_views[2] = nullptr;
        }

        for (auto image_view : image_views) {
            if (!image_view || !image_view->image_state) {
                continue;
            }
            skip |= ValidateProtectedImage(cb_state, *image_view->image_state, attachment_loc,
                                           "VUID-vkCmdClearAttachments-commandBuffer-02504");
            skip |= ValidateUnprotectedImage(cb_state, *image_view->image_state, attachment_loc,
                                             "VUID-vkCmdClearAttachments-commandBuffer-02505");
            const VkFormat image_view_format = image_view->create_info.format;
            if (vkuFormatIs64bit(image_view_format) && vkuFormatComponentCount(image_view_format) > 2) {
                const LogObjectList objlist(commandBuffer, image_view->Handle());
                skip |= LogError("VUID-vkCmdClearAttachments-None-09679", objlist, attachment_loc,
                                 "(%s) was created with a 64-bit format (%s) but it has more than 2 components. The clear command "
                                 "can only clear 16 bytes so this format is too large",
                                 FormatHandle(image_view->Handle()).c_str(), string_VkFormat(image_view_format));
            }
        }

        // With a non-zero view mask, multiview functionality is considered to be enabled
        const uint32_t view_mask = cb_state.GetViewMask();
        if (view_mask != 0) {
            for (uint32_t i = 0; i < rectCount; ++i) {
                if (pRects[i].baseArrayLayer != 0 || pRects[i].layerCount != 1) {
                    const LogObjectList objlist(commandBuffer, rp_state->Handle());
                    skip |= LogError("VUID-vkCmdClearAttachments-baseArrayLayer-00018", objlist,
                                     error_obj.location.dot(Field::pRects, i).dot(Field::baseArrayLayer),
                                     "is %" PRIu32 " and layerCount is %" PRIu32
                                     ", but the render pass instance viewMask is 0x%" PRIx32 " (non-zero).",
                                     pRects[i].baseArrayLayer, pRects[i].layerCount, view_mask);
                }
            }
        }
    }

    if (cb_state.per_tile_execution_model_enabled) {
        skip |= LogError("VUID-vkCmdClearAttachments-None-10616", commandBuffer, error_obj.location,
                         "the per-tile execution model has been enabled in this command buffer. "
                         "(Don't call vkCmdBeginPerTileExecutionQCOM before vkCmdClearAttachments)");
    }

    return skip;
}

// Helper function to validate usage flags for images
bool CoreChecks::ValidateImageUsageFlags(VkCommandBuffer commandBuffer, const vvl::Image& image_state, VkImageUsageFlags desired,
                                         bool strict, const char* vuid, const Location& image_loc) const {
    bool skip = false;
    LogObjectList objlist(commandBuffer, image_state.Handle());
    bool correct_usage = false;
    if (strict) {
        correct_usage = ((image_state.usage & desired) == desired);
    } else {
        correct_usage = ((image_state.usage & desired) != 0);
    }

    if (!correct_usage) {
        skip |= LogError(vuid, objlist, image_loc, "(%s) was created with %s but requires %s.",
                         FormatHandle(image_state.Handle()).c_str(), string_VkImageUsageFlags2KHR(image_state.usage).c_str(),
                         string_VkImageUsageFlags2KHR(desired).c_str());
    }
    return skip;
}

bool CoreChecks::ValidateImageFormatFeatureFlags(VkCommandBuffer commandBuffer, vvl::Image const& image_state,
                                                 VkFormatFeatureFlags2 desired, const Location& image_loc, const char* vuid,
                                                 bool all_bits_required) const {
    bool skip = false;
    const VkFormatFeatureFlags2 image_format_features = image_state.format_features;
    if ((all_bits_required && (image_format_features & desired) != desired) ||
        (!all_bits_required && (image_format_features & desired) == 0)) {
        const LogObjectList objlist(commandBuffer, image_state.Handle());
        // Same error, but more details if it was an AHB external format
        if (image_state.HasAHBFormat()) {
            skip |= LogError(
                vuid, objlist, image_loc,
                "(%s) was created with an external format having VkFormatFeatureFlags2 (%s) which is missing the required feature "
                "%s (Features found in VkAndroidHardwareBufferFormatPropertiesANDROID::formatFeatures).",
                FormatHandle(image_state).c_str(), string_VkFormatFeatureFlags2(image_format_features).c_str(),
                string_VkFormatFeatureFlags2(desired).c_str());
        } else {
            skip |=
                LogError(vuid, objlist, image_loc,
                         "(%s) was created with format %s and tiling %s which have VkFormatFeatureFlags2 (%s) which in turn is "
                         "missing the required feature %s.",
                         FormatHandle(image_state).c_str(), string_VkFormat(image_state.GetFormat()),
                         string_VkImageTiling(image_state.GetTiling()), string_VkFormatFeatureFlags2(image_format_features).c_str(),
                         string_VkFormatFeatureFlags2(desired).c_str());
        }
    }
    return skip;
}

// For the given format verify that the aspect masks make sense
bool CoreChecks::ValidateImageAspectMask(VkImage image, VkFormat format, VkImageAspectFlags aspect_mask, bool is_image_disjoint,
                                         const Location& loc, const char* vuid) const {
    bool skip = false;
    // checks color format and (single-plane or non-disjoint)
    // if ycbcr extension is not supported then single-plane and non-disjoint are always both true
    if ((vkuFormatIsColor(format)) && ((vkuFormatIsMultiplane(format) == false) || (is_image_disjoint == false))) {
        if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != VK_IMAGE_ASPECT_COLOR_BIT) {
            skip |= LogError(
                vuid, image, loc,
                "Using format (%s) with aspect flags (%s) but color image formats must have the VK_IMAGE_ASPECT_COLOR_BIT set.",
                string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        } else if ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) != aspect_mask) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but color image formats must have ONLY the "
                             "VK_IMAGE_ASPECT_COLOR_BIT set.",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        }
    } else if (vkuFormatIsDepthAndStencil(format)) {
        if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) == 0) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but depth/stencil image formats must have at least one "
                             "of VK_IMAGE_ASPECT_DEPTH_BIT and VK_IMAGE_ASPECT_STENCIL_BIT set.",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        } else if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != aspect_mask) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but combination depth/stencil image formats can have "
                             "only the VK_IMAGE_ASPECT_DEPTH_BIT and VK_IMAGE_ASPECT_STENCIL_BIT set.",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        }
    } else if (vkuFormatIsDepthOnly(format)) {
        if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != VK_IMAGE_ASPECT_DEPTH_BIT) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but depth-only image formats must have the "
                             "VK_IMAGE_ASPECT_DEPTH_BIT set.",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        } else if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != aspect_mask) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but depth-only image formats can have only the "
                             "VK_IMAGE_ASPECT_DEPTH_BIT set.",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        }
    } else if (vkuFormatIsStencilOnly(format)) {
        if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != VK_IMAGE_ASPECT_STENCIL_BIT) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but stencil-only image formats must have the "
                             "VK_IMAGE_ASPECT_STENCIL_BIT set.",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        } else if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != aspect_mask) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but stencil-only image formats can have only the "
                             "VK_IMAGE_ASPECT_STENCIL_BIT set.",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        }
    } else if (vkuFormatIsMultiplane(format)) {
        VkImageAspectFlags valid_flags = VK_IMAGE_ASPECT_COLOR_BIT | VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT;
        if (3 == vkuFormatPlaneCount(format)) {
            valid_flags = valid_flags | VK_IMAGE_ASPECT_PLANE_2_BIT;
        }
        if ((aspect_mask & valid_flags) != aspect_mask) {
            skip |= LogError(vuid, image, loc,
                             "Using format (%s) with aspect flags (%s) but multi-plane image formats may have only "
                             "VK_IMAGE_ASPECT_COLOR_BIT or VK_IMAGE_ASPECT_PLANE_n_BITs set, where n = [0, 1, 2].",
                             string_VkFormat(format), string_VkImageAspectFlags(aspect_mask).c_str());
        }
    }
    return skip;
}

bool CoreChecks::ValidateImageSubresourceRange(const uint32_t image_mip_count, const uint32_t image_layer_count,
                                               const VkImageSubresourceRange& subresourceRange, vvl::Field image_layer_count_var,
                                               const LogObjectList& objlist, const Location& subresource_loc) const {
    bool skip = false;

    // Validate mip levels
    if (subresourceRange.baseMipLevel >= image_mip_count) {
        skip |= LogError(GetSubresourceRangeVUID(subresource_loc, vvl::SubresourceRangeError::BaseMip_01486), objlist,
                         subresource_loc.dot(Field::baseMipLevel),
                         "(%" PRIu32 ") is greater than or equal to the mip level count of the image (%" PRIu32 ").",
                         subresourceRange.baseMipLevel, image_mip_count);
    }

    if (subresourceRange.levelCount != VK_REMAINING_MIP_LEVELS) {
        if (subresourceRange.levelCount == 0) {
            skip |= LogError("VUID-VkImageSubresourceRange-levelCount-01720", objlist, subresource_loc.dot(Field::levelCount),
                             "is zero.");
        } else {
            const uint64_t necessary_mip_count = uint64_t{subresourceRange.baseMipLevel} + uint64_t{subresourceRange.levelCount};

            if (necessary_mip_count > image_mip_count) {
                skip |= LogError(GetSubresourceRangeVUID(subresource_loc, vvl::SubresourceRangeError::MipCount_01724), objlist,
                                 subresource_loc.dot(Field::baseMipLevel),
                                 "(%" PRIu32 ") + levelCount (%" PRIu32 ") is %" PRIu64
                                 ", which is greater than the image mipLevels (%" PRIu32 ").",
                                 subresourceRange.baseMipLevel, subresourceRange.levelCount, necessary_mip_count, image_mip_count);
            }
        }
    }

    // Validate array layers
    if (subresourceRange.baseArrayLayer >= image_layer_count) {
        if (image_layer_count_var != Field::depth || (subresource_loc.function != Func::vkCmdClearColorImage &&
                                                      subresource_loc.function != Func::vkCmdClearDepthStencilImage)) {
            const auto vuid = image_layer_count_var == Field::depth
                                  ? GetSubresourceRangeVUID(subresource_loc, vvl::SubresourceRangeError::BaseLayer_02724_10798)
                                  : GetSubresourceRangeVUID(subresource_loc, vvl::SubresourceRangeError::BaseLayer_01488);
            skip |= LogError(vuid, objlist, subresource_loc.dot(Field::baseArrayLayer),
                             "(%" PRIu32 ") is not less than the %s of the image when it was created (%" PRIu32 ").",
                             subresourceRange.baseArrayLayer, String(image_layer_count_var), image_layer_count);
        }
    }

    if (image_layer_count_var == Field::depth && subresourceRange.levelCount != 1u) {
        const bool check_function = subresource_loc.function != Func::vkCmdClearColorImage &&
                                    subresource_loc.function != Func::vkCmdClearDepthStencilImage &&
                                    subresource_loc.function != Func::vkCreateImageView;
        if (check_function && (subresourceRange.baseArrayLayer != 0 || subresourceRange.layerCount != VK_REMAINING_ARRAY_LAYERS)) {
            const auto vuid = GetSubresourceRangeVUID(subresource_loc, vvl::SubresourceRangeError::LevelCount_10799);
            skip |= LogError(vuid, objlist, subresource_loc.dot(Field::levelCount),
                             "is %" PRIu32 ", baseArrayLayer is %" PRIu32 ", and layerCount is %" PRIu32 ".",
                             subresourceRange.levelCount, subresourceRange.baseArrayLayer, subresourceRange.layerCount);
        }
    }

    if (subresourceRange.layerCount != VK_REMAINING_ARRAY_LAYERS) {
        if (subresourceRange.layerCount == 0) {
            skip |= LogError("VUID-VkImageSubresourceRange-layerCount-01721", objlist, subresource_loc.dot(Field::layerCount),
                             "is zero.");
        } else {
            const uint64_t necessary_layer_count =
                uint64_t{subresourceRange.baseArrayLayer} + uint64_t{subresourceRange.layerCount};

            if (necessary_layer_count > image_layer_count) {
                if (image_layer_count_var != Field::depth || (subresource_loc.function != Func::vkCmdClearColorImage &&
                                                              subresource_loc.function != Func::vkCmdClearDepthStencilImage)) {
                    const auto vuid =
                        image_layer_count_var == Field::depth
                            ? GetSubresourceRangeVUID(subresource_loc, vvl::SubresourceRangeError::LayerCount_02725_10800)
                            : GetSubresourceRangeVUID(subresource_loc, vvl::SubresourceRangeError::LayerCount_01725);
                    skip |= LogError(vuid, objlist, subresource_loc.dot(Field::baseArrayLayer),
                                     "(%" PRIu32 ") + layerCount (%" PRIu32 ") is %" PRIu64
                                     ", which is greater than the %s of the image when it was created (%" PRIu32 ").",
                                     subresourceRange.baseArrayLayer, subresourceRange.layerCount, necessary_layer_count,
                                     String(image_layer_count_var), image_layer_count);
                }
            }
        }
    }

    if (subresourceRange.aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) {
        if (subresourceRange.aspectMask &
            (VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT | VK_IMAGE_ASPECT_PLANE_2_BIT)) {
            skip |= LogError("VUID-VkImageSubresourceRange-aspectMask-01670", objlist, subresource_loc.dot(Field::aspectMask),
                             "is %s.", string_VkImageAspectFlags(subresourceRange.aspectMask).c_str());
        }
    }

    // aspectMask must not contain VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT
    if (subresourceRange.aspectMask & (VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT |
                                       VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT | VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT)) {
        skip |= LogError("VUID-VkImageSubresourceRange-aspectMask-02278", objlist, subresource_loc.dot(Field::aspectMask), "is %s.",
                         string_VkImageAspectFlags(subresourceRange.aspectMask).c_str());
    }

    return skip;
}

bool CoreChecks::ValidateCreateImageViewSubresourceRange(const vvl::Image& image_state, bool is_depth_slice_view,
                                                         const VkImageSubresourceRange& subresourceRange,
                                                         const Location& loc) const {
    uint32_t image_layer_count;
    if (is_depth_slice_view) {
        const VkExtent3D extent = image_state.GetEffectiveSubresourceExtent(subresourceRange);
        image_layer_count = extent.depth;
    } else {
        image_layer_count = image_state.GetArrayLayers();
    }

    const auto image_layer_count_var = is_depth_slice_view ? Field::depth : Field::arrayLayers;

    return ValidateImageSubresourceRange(image_state.GetMipLevels(), image_layer_count, subresourceRange, image_layer_count_var,
                                         image_state.VkHandle(), loc.dot(Field::subresourceRange));
}

bool CoreChecks::ValidateCmdClearColorSubresourceRange(const vvl::Image& image_state,
                                                       const VkImageSubresourceRange& subresourceRange,
                                                       const LogObjectList& objlist, const Location& loc) const {
    auto image_layer_count_var = Field::arrayLayers;
    uint32_t image_layer_count = image_state.GetArrayLayers();
    if (enabled_features.maintenance9 && (image_state.create_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) != 0) {
        image_layer_count_var = Field::depth;
        const auto layers = LayersFromRange(subresourceRange);
        const auto extent = image_state.GetEffectiveSubresourceExtent(layers.aspectMask, layers.mipLevel);
        image_layer_count = extent.depth;
    }
    return ValidateImageSubresourceRange(image_state.GetMipLevels(), image_layer_count, subresourceRange, image_layer_count_var, objlist,
                                         loc.dot(Field::subresourceRange));
}

bool CoreChecks::ValidateCmdClearDepthSubresourceRange(const vvl::Image& image_state,
                                                       const VkImageSubresourceRange& subresourceRange,
                                                       const LogObjectList& objlist, const Location& loc) const {
    auto image_layer_count_var = Field::arrayLayers;
    uint32_t image_layer_count = image_state.GetArrayLayers();
    if (enabled_features.maintenance9 && (image_state.create_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) != 0) {
        image_layer_count_var = Field::depth;
        const auto layers = LayersFromRange(subresourceRange);
        const auto extent = image_state.GetEffectiveSubresourceExtent(layers.aspectMask, layers.mipLevel);
        image_layer_count = extent.depth;
    }
    return ValidateImageSubresourceRange(image_state.GetMipLevels(), image_layer_count, subresourceRange, image_layer_count_var, objlist,
                                         loc.dot(Field::subresourceRange));
}

bool CoreChecks::ValidateImageBarrierSubresourceRange(const VkImageSubresourceRange& subresource_range,
                                                      const vvl::Image& image_state, const LogObjectList& objlist,
                                                      const Location& loc) const {
    bool skip = false;

    // Warning about forward compatibility issue: https://gitlab.khronos.org/vulkan/vulkan/-/issues/4308
    if (!IsExtEnabled(extensions.vk_khr_maintenance9) && image_state.GetImageType() == VK_IMAGE_TYPE_3D &&
        (image_state.create_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) != 0) {
        const bool forward_compatibility_issue = (subresource_range.layerCount == 1 && image_state.GetExtent().depth > 1);
        if (forward_compatibility_issue) {
            skip |= LogWarning("WARNING-VkImageSubresourceRange-layerCount-compatibility", objlist, loc.dot(Field::layerCount),
                               "is 1 for a 3D image (%s) created with VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT. The "
                               "maintenance9 feature is not currently enabled, so layerCount refers to all depth slices. When "
                               "maintenance9 is enabled, it instead refers to a single depth slice. To remain forward-compatible, "
                               "layerCount should be set to VK_REMAINING_ARRAY_LAYERS.",
                               FormatHandle(image_state.Handle()).c_str());
        }
    }

    auto image_layer_count_var = Field::arrayLayers;
    uint32_t image_layer_count = image_state.GetArrayLayers();
    if (enabled_features.maintenance9 && (image_state.create_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) != 0) {
        image_layer_count_var = Field::depth;
        const auto layers = LayersFromRange(subresource_range);
        const auto extent = image_state.GetEffectiveSubresourceExtent(layers.aspectMask, layers.mipLevel);
        image_layer_count = extent.depth;
    }
    skip |= ValidateImageSubresourceRange(image_state.GetMipLevels(), image_layer_count, subresource_range, image_layer_count_var,
                                          objlist, loc.dot(Field::subresourceRange));
    return skip;
}

bool CoreChecks::ValidateImageViewFormatFeatures(const vvl::Image& image_state, const VkFormat view_format,
                                                 const VkImageUsageFlags2KHR image_usage, const Location& create_info_loc) const {
    // Pass in image_usage here instead of extracting it from image_state in case there's a chained VkImageViewUsageCreateInfo
    bool skip = false;

    VkFormatFeatureFlags2 tiling_features = 0;
    const VkImageTiling image_tiling = image_state.GetTiling();

    if (image_state.HasAHBFormat()) {
        // AHB image view and image share same feature sets
        tiling_features = image_state.format_features;
    } else if (image_tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
        VkImageDrmFormatModifierPropertiesEXT drm_format_properties = vku::InitStructHelper();
        DispatchGetImageDrmFormatModifierPropertiesEXT(device, image_state.VkHandle(), &drm_format_properties);

        if (device_state->special_supported.vk_khr_format_feature_flags2) {
            VkDrmFormatModifierPropertiesList2EXT fmt_drm_props = vku::InitStructHelper();
            VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, view_format, &fmt_props_2);

            std::vector<VkDrmFormatModifierProperties2EXT> drm_properties;
            drm_properties.resize(fmt_drm_props.drmFormatModifierCount);
            fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();

            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, view_format, &fmt_props_2);

            for (uint32_t i = 0; i < fmt_drm_props.drmFormatModifierCount; i++) {
                if (fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifier == drm_format_properties.drmFormatModifier) {
                    tiling_features = fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifierTilingFeatures;
                    break;
                }
            }
        } else {
            VkDrmFormatModifierPropertiesListEXT fmt_drm_props = vku::InitStructHelper();
            VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, view_format, &fmt_props_2);

            std::vector<VkDrmFormatModifierPropertiesEXT> drm_properties;
            drm_properties.resize(fmt_drm_props.drmFormatModifierCount);
            fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();

            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, view_format, &fmt_props_2);

            for (uint32_t i = 0; i < fmt_drm_props.drmFormatModifierCount; i++) {
                if (fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifier == drm_format_properties.drmFormatModifier) {
                    tiling_features = fmt_drm_props.pDrmFormatModifierProperties[i].drmFormatModifierTilingFeatures;
                    break;
                }
            }
        }
    } else {
        VkFormatProperties3 format_properties = GetPDFormatProperties(view_format);
        tiling_features = (image_tiling == VK_IMAGE_TILING_LINEAR) ? format_properties.linearTilingFeatures
                                                                   : format_properties.optimalTilingFeatures;
    }

    if (tiling_features == 0) {
        skip |= LogError("VUID-VkImageViewCreateInfo-None-02273", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s with tiling %s has no supported format features on this "
                         "physical device.",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling));
    } else if ((image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(tiling_features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-usage-02274", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s with tiling %s doesn't support VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if ((image_usage & VK_IMAGE_USAGE_STORAGE_BIT) && !(tiling_features & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-usage-02275", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s with tiling %s doesn't support VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if ((image_usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) &&
               !(tiling_features & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV))) {
        skip |= LogError("VUID-VkImageViewCreateInfo-usage-08931", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s with tiling %s doesn't support VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if ((image_usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) &&
               !(tiling_features & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-usage-02277", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s with tiling %s doesn't support VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT.\n."
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if ((image_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT) &&
               !(tiling_features & (VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
                                    VK_FORMAT_FEATURE_2_LINEAR_COLOR_ATTACHMENT_BIT_NV))) {
        if (!enabled_features.externalFormatResolve && !device_state->android_external_format_resolve_null_color_attachment_prop &&
            !image_state.HasAHBFormat()) {
            skip |= LogError("VUID-VkImageViewCreateInfo-usage-08932", image_state.Handle(), create_info_loc.dot(Field::format),
                             "%s with tiling %s doesn't support one of the required formats.\n."
                             "(supported features: %s)",
                             string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                             string_VkFormatFeatureFlags2(tiling_features).c_str());
        }
    } else if ((image_usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) {
        // shadingRateImage is from the NV extension, should mean the same, lacks spec text
        if (!enabled_features.attachmentFragmentShadingRate && !enabled_features.shadingRateImage) {
            skip |= LogError("VUID-VkImageViewCreateInfo-attachmentFragmentShadingRate-12386", image_state.Handle(),
                             create_info_loc.dot(Field::image),
                             "was created with VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, but the "
                             "attachmentFragmentShadingRate feature was not enabled.");
        } else if (!(tiling_features & VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR)) {
            skip |= LogError("VUID-VkImageViewCreateInfo-usage-04550", image_state.Handle(), create_info_loc.dot(Field::format),
                             "%s with tiling %s doesn't support VK_FORMAT_FEATURE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR.\n."
                             "(supported features: %s)",
                             string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                             string_VkFormatFeatureFlags2(tiling_features).c_str());
        }
    } else if ((image_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR) &&
               !(tiling_features & VK_FORMAT_FEATURE_2_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-usage-10259", image_state.Handle(), create_info_loc.dot(Field::usage),
                         "%s and tiling %s doesn't support VK_FORMAT_FEATURE_2_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if ((image_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR) &&
               !(tiling_features & VK_FORMAT_FEATURE_2_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-usage-10260", image_state.Handle(), create_info_loc.dot(Field::usage),
                         "%s and tiling %s doesn't support VK_FORMAT_FEATURE_2_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    }

    if ((image_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) &&
        !(tiling_features & VK_FORMAT_FEATURE_VIDEO_DECODE_OUTPUT_BIT_KHR)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-image-08333", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s and tiling %s doesn't support VK_FORMAT_FEATURE_VIDEO_DECODE_OUTPUT_BIT_KHR.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if ((image_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR) &&
               !(tiling_features & VK_FORMAT_FEATURE_VIDEO_DECODE_DPB_BIT_KHR)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-image-08334", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s and tiling %s doesn't support VK_FORMAT_FEATURE_VIDEO_DECODE_DPB_BIT_KHR.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if (image_usage & VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR) {
        skip |= LogError("VUID-VkImageViewCreateInfo-image-08335", image_state.Handle(), create_info_loc.dot(Field::usage),
                         "is VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR which is not allowed for image views from video profile "
                         "independent images.");
    } else if ((image_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR) &&
               !(tiling_features & VK_FORMAT_FEATURE_VIDEO_ENCODE_INPUT_BIT_KHR)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-image-08336", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s and tiling %s doesn't support VK_FORMAT_FEATURE_VIDEO_ENCODE_INPUT_BIT_KHR.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if ((image_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR) &&
               !(tiling_features & VK_FORMAT_FEATURE_VIDEO_ENCODE_DPB_BIT_KHR)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-image-08337", image_state.Handle(), create_info_loc.dot(Field::format),
                         "%s and tiling %s doesn't support VK_FORMAT_FEATURE_VIDEO_ENCODE_DPB_BIT_KHR.\n"
                         "(supported features: %s)",
                         string_VkFormat(view_format), string_VkImageTiling(image_tiling),
                         string_VkFormatFeatureFlags2(tiling_features).c_str());
    } else if (image_usage & VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR) {
        skip |= LogError("VUID-VkImageViewCreateInfo-image-08338", image_state.Handle(), create_info_loc.dot(Field::usage),
                         "is VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR which is not allowed for image views from video profile "
                         "independent images.");
    }

    return skip;
}
// Returns whether two formats have identical components (compares the size and type of each component)
// EX. (R8G8B8A8, B8G8R8A8) -> true
bool FormatsEqualComponentBits(VkFormat format_a, VkFormat format_b) {
    const VKU_FORMAT_INFO format_info_a = vkuGetFormatInfo(format_a);
    const VKU_FORMAT_INFO format_info_b = vkuGetFormatInfo(format_b);
    if (format_info_a.compatibility == VKU_FORMAT_COMPATIBILITY_CLASS_NONE ||
        format_info_b.compatibility == VKU_FORMAT_COMPATIBILITY_CLASS_NONE) {
        return false;
    } else if (format_info_a.component_count != format_info_b.component_count) {
        return false;
    }
    // Need to loop match each component type is found in both formats
    // formats are maxed at 4 components, so the double loop is not going to scale
    for (uint32_t i = 0; i < format_info_a.component_count; i++) {
        const VKU_FORMAT_COMPONENT_INFO component_a = format_info_a.components[i];
        bool component_match = false;
        for (uint32_t j = 0; j < format_info_b.component_count; j++) {
            const VKU_FORMAT_COMPONENT_INFO component_b = format_info_b.components[j];
            if ((component_a.type == component_b.type) && (component_a.size == component_b.size)) {
                component_match = true;
                break;
            }
        }
        if (!component_match) {
            return false;
        }
    }
    return true;
}

bool CoreChecks::ValidateImageViewSlicedCreateInfo(const VkImageViewCreateInfo& create_info, const vvl::Image& image_state,
                                                   const VkImageSubresourceRange& normalized_subresource_range,
                                                   const Location& create_info_loc) const {
    bool skip = false;
    const auto sliced_create_info_ext = vku::FindStructInPNextChain<VkImageViewSlicedCreateInfoEXT>(create_info.pNext);
    if (!sliced_create_info_ext) {
        return skip;
    }

    const bool feature_disabled = (enabled_features.imageSlicedViewOf3D == VK_FALSE);
    if (feature_disabled) {
        skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-None-07871", create_info.image, create_info_loc,
                         "imageSlicedViewOf3D is not enabled.");
    }

    if (image_state.GetImageType() != VK_IMAGE_TYPE_3D) {
        skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-image-07869", create_info.image, create_info_loc.dot(Field::image),
                         "(%s) must be VK_IMAGE_TYPE_3D (since pNext includes a VkImageViewSlicedCreateInfoEXT structure).\n%s",
                         string_VkImageType(image_state.GetImageType()),
                         PrintPNextChain(Struct::VkImageViewCreateInfo, create_info.pNext).c_str());
    }

    if (create_info.viewType != VK_IMAGE_VIEW_TYPE_3D) {
        skip |=
            LogError("VUID-VkImageViewSlicedCreateInfoEXT-viewType-07909", create_info.image, create_info_loc.dot(Field::viewType),
                     "(%s) must be VK_IMAGE_VIEW_TYPE_3D (since pNext includes a VkImageViewSlicedCreateInfoEXT structure).\n%s",
                     string_VkImageViewType(create_info.viewType),
                     PrintPNextChain(Struct::VkImageViewCreateInfo, create_info.pNext).c_str());
    }

    if (normalized_subresource_range.levelCount != 1) {
        skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-None-07870", create_info.image,
                         create_info_loc.dot(Field::subresourceRange).dot(Field::levelCount),
                         "(%s) must be 1 (since pNext includes a VkImageViewSlicedCreateInfoEXT structure).\n%s",
                         string_LevelCount(image_state.GetMipLevels(), create_info.subresourceRange).c_str(),
                         PrintPNextChain(Struct::VkImageViewCreateInfo, create_info.pNext).c_str());
    }

    const uint32_t effective_view_depth = image_state.GetEffectiveSubresourceExtent(create_info.subresourceRange).depth;

    const uint32_t slice_offset = sliced_create_info_ext->sliceOffset;
    const uint32_t slice_count = sliced_create_info_ext->sliceCount;

    if (slice_offset >= effective_view_depth) {
        skip |=
            LogError("VUID-VkImageViewSlicedCreateInfoEXT-sliceOffset-07867", create_info.image,
                     create_info_loc.pNext(Struct::VkImageViewSlicedCreateInfoEXT, Field::sliceOffset),
                     "(%" PRIu32 ") must be less than the effective view depth (%" PRIu32 ").", slice_offset, effective_view_depth);
    }

    if (slice_count != VK_REMAINING_3D_SLICES_EXT) {
        if (slice_count == 0) {
            skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-sliceCount-07868", create_info.image,
                             create_info_loc.pNext(Struct::VkImageViewSlicedCreateInfoEXT, Field::sliceCount), "is 0.");
        }

        if ((slice_offset + slice_count) > effective_view_depth) {
            skip |= LogError("VUID-VkImageViewSlicedCreateInfoEXT-sliceCount-07868", create_info.image,
                             create_info_loc.pNext(Struct::VkImageViewSlicedCreateInfoEXT, Field::sliceOffset),
                             "(%" PRIu32 ") + sliceCount (%" PRIu32 ") is greater than effective view depth (%" PRIu32 ").",
                             slice_offset, slice_count, effective_view_depth);
        }
    }
    return skip;
}

bool CoreChecks::ValidateImageViewCreateInfo(const VkImageViewCreateInfo& create_info, const Location& create_info_loc) const {
    bool skip = false;

    auto image_state_ptr = Get<vvl::Image>(create_info.image);
    ASSERT_AND_RETURN_SKIP(image_state_ptr);
    const auto& image_state = *image_state_ptr;

    const VkImageUsageFlags valid_usage_flags =
        VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
        VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
        VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR |
        VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT | VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR |
        VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR |
        VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR | VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM |
        VK_IMAGE_USAGE_SAMPLE_BLOCK_MATCH_BIT_QCOM | VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR |
        VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR;
    skip |= ValidateImageUsageFlags(VK_NULL_HANDLE, image_state, valid_usage_flags, false, "VUID-VkImageViewCreateInfo-image-04441",
                                    create_info_loc.dot(Field::image));
    // If this isn't a sparse image, it needs to have memory backing it at CreateImageView time
    skip |= ValidateMemoryIsBoundToImage(LogObjectList(device, create_info.image), image_state, create_info_loc.dot(Field::image),
                                         "VUID-VkImageViewCreateInfo-image-01020");
    // Checks imported from image layer
    skip |= ValidateCreateImageViewSubresourceRange(
        image_state, IsDepthSliceView(image_state.GetImageType(), image_state.create_flags, create_info.viewType),
        create_info.subresourceRange, create_info_loc);

    auto normalized_subresource_range = vvl::ImageView::NormalizeImageViewSubresourceRange(image_state, create_info);

    const VkImageCreateFlags2KHR image_flags = image_state.create_flags;
    const VkFormat image_format = image_state.GetFormat();
    const VkFormat view_format = create_info.format;
    const VkImageAspectFlags aspect_mask = create_info.subresourceRange.aspectMask;
    const VkImageType image_type = image_state.GetImageType();
    const VkImageViewType view_type = create_info.viewType;

    // If there's a chained VkImageViewUsageCreateInfo struct, modify |image_usage| to match.
    // VkImageViewUsageCreateInfo must have a subset (no new usage) and remaining checks only look for inclusion of usage flag
    VkImageUsageFlags2KHR image_usage = image_state.usage;

    const auto image_view_usage = GetImageViewUsageFlags(create_info);
    if (image_view_usage.has_value()) {
        if (IsExtEnabled(extensions.vk_khr_maintenance2)) {
            if (!image_state.stencil_usage.has_value()) {
                if ((image_usage | image_view_usage.value()) != image_usage) {
                    skip |=
                        LogError("VUID-VkImageViewCreateInfo-pNext-02662", create_info.image,
                                 create_info_loc.pNext(Struct::VkImageViewUsageCreateInfo, Field::usage),
                                 "(%s) must not include any bits that were not set in VkImageCreateInfo::usage (%s) of the image.",
                                 string_VkImageUsageFlags2KHR(image_view_usage.value()).c_str(),
                                 string_VkImageUsageFlags2KHR(image_usage).c_str());
                }
            } else {
                if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) == VK_IMAGE_ASPECT_STENCIL_BIT &&
                    (image_state.stencil_usage.value() | image_view_usage.value()) != image_state.stencil_usage) {
                    skip |=
                        LogError("VUID-VkImageViewCreateInfo-pNext-02663", create_info.image,
                                 create_info_loc.pNext(Struct::VkImageViewUsageCreateInfo, Field::usage),
                                 "(%s) must not include any bits that were not set in VkImageStencilUsageCreateInfo::stencilUsage "
                                 "(%s) if subResourceRange.aspectMask includes VK_IMAGE_ASPECT_STENCIL_BIT.",
                                 string_VkImageUsageFlags2KHR(image_view_usage.value()).c_str(),
                                 string_VkImageUsageFlags2KHR(image_state.stencil_usage.value()).c_str());
                }
                if ((aspect_mask & ~VK_IMAGE_ASPECT_STENCIL_BIT) != 0 && (image_usage | image_view_usage.value()) != image_usage) {
                    skip |=
                        LogError("VUID-VkImageViewCreateInfo-pNext-02664", create_info.image,
                                 create_info_loc.pNext(Struct::VkImageViewUsageCreateInfo, Field::usage),
                                 "(%s) must not include any bits that were not set in VkImageCreateInfo::usage (%s) of the image "
                                 "if subResourceRange.aspectMask (%s) includes bits other than VK_IMAGE_ASPECT_STENCIL_BIT.",
                                 string_VkImageUsageFlags2KHR(image_view_usage.value()).c_str(),
                                 string_VkImageUsageFlags2KHR(image_usage).c_str(), string_VkImageAspectFlags(aspect_mask).c_str());
                }
            }
        }

        image_usage = image_view_usage.value();
    }

    skip |= ValidateImageViewSlicedCreateInfo(create_info, image_state, normalized_subresource_range, create_info_loc);

    // If image used VkImageFormatListCreateInfo need to make sure a format from list is used
    if (const auto format_list_info = vku::FindStructInPNextChain<VkImageFormatListCreateInfo>(image_state.GetPNext());
        format_list_info && (format_list_info->viewFormatCount > 0)) {
        bool found_format = false;
        for (uint32_t i = 0; i < format_list_info->viewFormatCount; i++) {
            if (format_list_info->pViewFormats[i] == view_format) {
                found_format = true;
                break;
            }
        }
        if (found_format == false) {
            skip |= LogError("VUID-VkImageViewCreateInfo-pNext-01585", create_info.image,
                             create_info_loc.pNext(Struct::VkImageFormatListCreateInfo, Field::pViewFormats),
                             "has no formats that match the VkImageViewCreateInfo::format (%s).", string_VkFormat(view_format));
        }
    }

    if (image_flags & VK_IMAGE_CREATE_ALIAS_SINGLE_LAYER_DESCRIPTOR_BIT_KHR) {
        if (vkuFormatIsMultiplane(view_format)) {
            skip |= LogError("VUID-VkImageViewCreateInfo-image-13357", create_info.image, create_info_loc.dot(Field::format),
                             "(%s) is a multiplane format, but the image was created with flag "
                             "VK_IMAGE_CREATE_ALIAS_SINGLE_LAYER_DESCRIPTOR_BIT_KHR. (image flags: %s).",
                             string_VkFormat(view_format), string_VkImageCreateFlags2KHR(image_flags).c_str());
        }
    }

    const bool multiplane_image = vkuFormatIsMultiplane(image_format);
    if (multiplane_image) {
        if (IsMultiplePlaneAspect(aspect_mask)) {
            skip |= LogError("VUID-VkImageViewCreateInfo-subresourceRange-07818", create_info.image,
                             create_info_loc.dot(Field::subresourceRange).dot(Field::aspectMask), "(%s) is invalid for %s.",
                             string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
        }
    }

    if ((image_flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) && (image_format != view_format)) {
        const auto view_class = vkuFormatCompatibilityClass(view_format);
        if (multiplane_image) {
            // Multiplane format can only be different (with MUTABLE_FORMAT) if it is taking a plane view
            // If taking a COLOR_ASPECT view, then it must be the same
            if (aspect_mask == VK_IMAGE_ASPECT_COLOR_BIT) {
                skip |=
                    LogError("VUID-VkImageViewCreateInfo-format-12398", create_info.image, create_info_loc.dot(Field::format),
                             "%s is different from %s format (%s). Multiplane formats taking a view with VK_IMAGE_ASPECT_COLOR_BIT "
                             "must have identical formats.\nIf a VK_IMAGE_ASPECT_PLANE_*_BIT aspect mask is used, it can be a "
                             "different format that matches the plane.",
                             string_VkFormat(view_format), FormatHandle(create_info.image).c_str(), string_VkFormat(image_format));
            } else {
                const VkFormat compat_format =
                vkuFindMultiplaneCompatibleFormat(image_format, static_cast<VkImageAspectFlagBits>(aspect_mask));
                const auto image_class = vkuFormatCompatibilityClass(compat_format);
                // Need valid aspect mask otherwise will throw extra error when getting compatible format
                const bool has_valid_aspect = IsOnlyOneValidPlaneAspect(image_format, aspect_mask);
                if (has_valid_aspect && ((image_class != view_class) || (image_class == VKU_FORMAT_COMPATIBILITY_CLASS_NONE))) {
                    // Need to only check if one is NONE to handle edge case both are NONE
                    // View format must match the multiplane compatible format
                    skip |= LogError("VUID-VkImageViewCreateInfo-image-01586", create_info.image, create_info_loc.dot(Field::format),
                                 "(%s) is not compatible with plane %" PRIu32 " of the %s format %s, must be compatible with %s.",
                                 string_VkFormat(view_format), vkuGetPlaneIndex(static_cast<VkImageAspectFlagBits>(aspect_mask)),
                                 FormatHandle(create_info.image).c_str(), string_VkFormat(image_format),
                                 string_VkFormat(compat_format));
                                }
            }
        } else if (!(image_flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT)) {
            // Format MUST be compatible (in the same format compatibility class) as the format the image was created with
            const auto image_class = vkuFormatCompatibilityClass(image_format);
            // Need to only check if one is NONE to handle edge case both are NONE
            if ((image_class != view_class) || (image_class == VKU_FORMAT_COMPATIBILITY_CLASS_NONE)) {
                skip |=
                    LogError("VUID-VkImageViewCreateInfo-image-01761", create_info.image, create_info_loc.dot(Field::format),
                             "%s is not in the same format compatibility class as %s format %s. Images created with the "
                             "VK_IMAGE_CREATE_MUTABLE_FORMAT BIT can support ImageViews with differing formats but they must be in "
                             "the same compatibility class.",
                             string_VkFormat(view_format), FormatHandle(create_info.image).c_str(), string_VkFormat(image_format));
            }
        }
    } else if (image_format != view_format) {
        skip |=
            LogError("VUID-VkImageViewCreateInfo-image-12397", create_info.image, create_info_loc.dot(Field::format),
                     "%s is different from %s format (%s). Formats must be identical unless VK_IMAGE_CREATE_MUTABLE_FORMAT)BIT was "
                     "set on image creation.",
                     string_VkFormat(view_format), FormatHandle(create_info.image).c_str(), string_VkFormat(image_format));
    }

    if (image_state.GetSamples() != VK_SAMPLE_COUNT_1_BIT && view_type != VK_IMAGE_VIEW_TYPE_2D &&
        view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
        skip |= LogError("VUID-VkImageViewCreateInfo-image-04972", create_info.image, create_info_loc.dot(Field::image),
                         "was created with sample count %s, but create_info.viewType is %s.",
                         string_VkSampleCountFlagBits(image_state.GetSamples()), string_VkImageViewType(view_type));
    }

    // Validate correct image aspect bits for desired formats and format consistency
    skip |= ValidateImageAspectMask(image_state.VkHandle(), image_format, aspect_mask, image_state.disjoint, create_info_loc,
                                    "VUID-VkImageViewCreateInfo-subresourceRange-09594");

    // Valdiate Image/ImageView type compatibility #resources-image-views-compatibility
    switch (image_type) {
        case VK_IMAGE_TYPE_1D:
            if (view_type != VK_IMAGE_VIEW_TYPE_1D && view_type != VK_IMAGE_VIEW_TYPE_1D_ARRAY) {
                skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", create_info.image,
                                 create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
                                 string_VkImageViewType(view_type), string_VkImageType(image_type));
            }
            break;
        case VK_IMAGE_TYPE_2D:
            if (view_type != VK_IMAGE_VIEW_TYPE_2D && view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
                if ((view_type == VK_IMAGE_VIEW_TYPE_CUBE || view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) &&
                    !(image_flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)) {
                    skip |= LogError("VUID-VkImageViewCreateInfo-image-01003", create_info.image,
                                     create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
                                     string_VkImageViewType(view_type), string_VkImageType(image_type));
                } else if (view_type != VK_IMAGE_VIEW_TYPE_CUBE && view_type != VK_IMAGE_VIEW_TYPE_CUBE_ARRAY) {
                    skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", create_info.image,
                                     create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
                                     string_VkImageViewType(view_type), string_VkImageType(image_type));
                }
            }
            break;
        case VK_IMAGE_TYPE_3D:
            if (IsExtEnabled(extensions.vk_khr_maintenance1)) {
                if (view_type != VK_IMAGE_VIEW_TYPE_3D) {
                    if ((view_type == VK_IMAGE_VIEW_TYPE_2D || view_type == VK_IMAGE_VIEW_TYPE_2D_ARRAY)) {
                        if ((image_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) == 0) {
                            if (view_type == VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
                                skip |=
                                    LogError("VUID-VkImageViewCreateInfo-image-06723", create_info.image,
                                             create_info_loc.dot(Field::viewType),
                                             "(%s) is not compatible with image type "
                                             "%s since the image doesn't have VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT flag set.",
                                             string_VkImageViewType(view_type), string_VkImageType(image_type));
                            }
                            if (view_type == VK_IMAGE_VIEW_TYPE_2D &&
                                (image_flags & VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT) == 0) {
                                skip |= LogError("VUID-VkImageViewCreateInfo-image-06728", create_info.image,
                                                 create_info_loc.dot(Field::viewType),
                                                 "(%s) is not compatible with image type "
                                                 "%s since the image doesn't have VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT or "
                                                 "VK_IMAGE_CREATE_2D_VIEW_COMPATIBLE_BIT_EXT flag set.",
                                                 string_VkImageViewType(view_type), string_VkImageType(image_type));
                            }
                        }
                        if (normalized_subresource_range.levelCount != 1) {
                            skip |= LogError("VUID-VkImageViewCreateInfo-image-04970", create_info.image,
                                             create_info_loc.dot(Field::viewType),
                                             "(%s) is with image type %s must have a "
                                             "subresourcesRange.levelCount of 1 but it is %s.",
                                             string_VkImageViewType(view_type), string_VkImageType(image_type),
                                             string_LevelCount(image_state.GetMipLevels(), create_info.subresourceRange).c_str());
                        }
                    } else {
                        skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", create_info.image,
                                         create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
                                         string_VkImageViewType(view_type), string_VkImageType(image_type));
                    }
                }
            } else {
                if (view_type != VK_IMAGE_VIEW_TYPE_3D) {
                    // Help point to VK_KHR_maintenance1
                    if ((view_type == VK_IMAGE_VIEW_TYPE_2D || view_type == VK_IMAGE_VIEW_TYPE_2D_ARRAY)) {
                        skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", create_info.image,
                                         create_info_loc.dot(Field::viewType),
                                         "(%s) is not compatible with image type %s "
                                         "without VK_KHR_maintenance1 enabled which was promoted in Vulkan 1.0.",
                                         string_VkImageViewType(view_type), string_VkImageType(image_type));
                    } else {
                        skip |= LogError("VUID-VkImageViewCreateInfo-subResourceRange-01021", create_info.image,
                                         create_info_loc.dot(Field::viewType), "(%s) is not compatible with image type %s.",
                                         string_VkImageViewType(view_type), string_VkImageType(image_type));
                    }
                }
            }
            break;
        default:
            break;
    }

    skip |= ValidateCreateImageViewANDROID(create_info, image_state, create_info_loc);
    skip |= ValidateImageViewFormatFeatures(image_state, view_format, image_usage, create_info_loc);

    if (enabled_features.shadingRateImage) {
        if (image_usage & VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV) {
            if (view_format != VK_FORMAT_R8_UINT) {
                skip |= LogError("VUID-VkImageViewCreateInfo-image-02087", create_info.image, create_info_loc.dot(Field::image),
                                 "was created with usage containing "
                                 "VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV, format must be VK_FORMAT_R8_UINT.");
            }
        }
    }

    if (enabled_features.shadingRateImage || enabled_features.attachmentFragmentShadingRate) {
        if (image_usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR) {
            if (view_type != VK_IMAGE_VIEW_TYPE_2D && view_type != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
                skip |= LogError("VUID-VkImageViewCreateInfo-image-02086", create_info.image, create_info_loc.dot(Field::image),
                                 "was created with usage containing "
                                 "VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR, viewType must be "
                                 "VK_IMAGE_VIEW_TYPE_2D or VK_IMAGE_VIEW_TYPE_2D_ARRAY.");
            }
        }
    }

    if (!phys_dev_ext_props.fragment_shading_rate_props.layeredShadingRateAttachments &&
        image_usage & VK_IMAGE_USAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR && normalized_subresource_range.layerCount != 1) {
        skip |= LogError("VUID-VkImageViewCreateInfo-usage-04551", create_info.image,
                         create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
                         "is %s for a shading rate attachment image view.",
                         string_LayerCount(image_state.GetArrayLayers(), create_info.subresourceRange).c_str());
    }

    if (create_info.subresourceRange.layerCount == VK_REMAINING_ARRAY_LAYERS) {
        const uint32_t remaining_layers = image_state.GetArrayLayers() - create_info.subresourceRange.baseArrayLayer;
        if (view_type == VK_IMAGE_VIEW_TYPE_CUBE && remaining_layers != 6) {
            skip |= LogError("VUID-VkImageViewCreateInfo-viewType-02962", create_info.image,
                             create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
                             "is VK_REMAINING_ARRAY_LAYERS (%" PRIu32 ") but must be 6.", remaining_layers);
        }
        if (view_type == VK_IMAGE_VIEW_TYPE_CUBE_ARRAY && !IsIntegerMultipleOf(remaining_layers, 6)) {
            skip |= LogError("VUID-VkImageViewCreateInfo-viewType-02963", create_info.image,
                             create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
                             "is VK_REMAINING_ARRAY_LAYERS (%" PRIu32 ") but must be a multiple of 6.", remaining_layers);
        }
        if ((remaining_layers != 1) && ((view_type == VK_IMAGE_VIEW_TYPE_1D) || (view_type == VK_IMAGE_VIEW_TYPE_2D) ||
                                        (view_type == VK_IMAGE_VIEW_TYPE_3D))) {
            skip |=
                LogError("VUID-VkImageViewCreateInfo-imageViewType-04974", create_info.image, create_info_loc.dot(Field::viewType),
                         "is %s and the subresourceRange.layerCount "
                         "is VK_REMAINING_ARRAY_LAYERS (%" PRIu32 ") but must be 1 (try looking into VK_IMAGE_VIEW_TYPE_%s_ARRAY).",
                         string_VkImageViewType(view_type), remaining_layers,
                         view_type == VK_IMAGE_VIEW_TYPE_1D   ? "1D"
                         : view_type == VK_IMAGE_VIEW_TYPE_2D ? "2D"
                                                              : "CUBE");
        }
    } else {
        if ((create_info.subresourceRange.layerCount != 1) &&
            ((view_type == VK_IMAGE_VIEW_TYPE_1D) || (view_type == VK_IMAGE_VIEW_TYPE_2D) ||
             (view_type == VK_IMAGE_VIEW_TYPE_3D))) {
            skip |= LogError("VUID-VkImageViewCreateInfo-imageViewType-04973", create_info.image,
                             create_info_loc.dot(Field::subresourceRange).dot(Field::layerCount),
                             "(%" PRIu32 ") must be 1 when using viewType %s (try looking into VK_IMAGE_VIEW_TYPE_*_ARRAY).",
                             create_info.subresourceRange.layerCount, string_VkImageViewType(view_type));
        }
    }

    if (image_usage & VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT) {
        if (normalized_subresource_range.levelCount != 1) {
            skip |= LogError("VUID-VkImageViewCreateInfo-image-02571", create_info.image, create_info_loc.dot(Field::image),
                             "was created with usage containing "
                             "VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT and subresourcesRange.levelCount must be 1 but it is %s",
                             string_LevelCount(image_state.GetMipLevels(), create_info.subresourceRange).c_str());
        }
    }
    if (create_info.flags & VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT) {
        if (!enabled_features.fragmentDensityMapDynamic) {
            skip |= LogError("VUID-VkImageViewCreateInfo-flags-02572", create_info.image, create_info_loc.dot(Field::flags),
                             "contains VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT but the fragmentDensityMapDynamic "
                             "feature is not enabled.");
        }
    } else {
        if (image_usage & VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT) {
            if (image_flags & (VK_IMAGE_CREATE_PROTECTED_BIT | VK_IMAGE_CREATE_SPARSE_BINDING_BIT |
                               VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT | VK_IMAGE_CREATE_SPARSE_ALIASED_BIT)) {
                skip |= LogError("VUID-VkImageViewCreateInfo-flags-04116", create_info.image, create_info_loc.dot(Field::image),
                                 "was created with usage containing "
                                 "VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT flags must not contain any of "
                                 "VK_IMAGE_CREATE_PROTECTED_BIT, VK_IMAGE_CREATE_SPARSE_BINDING_BIT, "
                                 "VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT, or VK_IMAGE_CREATE_SPARSE_ALIASED_BIT");
            }
        }
    }

    if (image_flags & VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT) {
        if (!vkuFormatIsCompressed(view_format)) {
            // Is an uncompressed view format on a compressed image format
            if (normalized_subresource_range.levelCount != 1) {
                skip |= LogError(
                    "VUID-VkImageViewCreateInfo-image-07072", create_info.image, create_info_loc.dot(Field::image),
                    "was created with VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT bit, "
                    "and the image view format (%s) is uncompressed, but subresourcesRange.levelCount is %s (instead of 1).",
                    string_VkFormat(view_format), string_LevelCount(image_state.GetMipLevels(), create_info.subresourceRange).c_str());
            }
            if (normalized_subresource_range.layerCount != 1 &&
                (!IsExtEnabled(extensions.vk_khr_maintenance6) || !phys_dev_props_core14.blockTexelViewCompatibleMultipleLayers)) {
                skip |= LogError(
                    "VUID-VkImageViewCreateInfo-image-09487", create_info.image, create_info_loc.dot(Field::image),
                    "was created with VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT bit, "
                    "and the image view format (%s) is uncompressed, but subresourcesRange.layerCount is %s (instead of 1).",
                    string_VkFormat(view_format), string_LayerCount(image_state.GetArrayLayers(), create_info.subresourceRange).c_str());
            }
            if (!AreFormatsSizeCompatible(view_format, image_format, aspect_mask)) {
                skip |= LogError("VUID-VkImageViewCreateInfo-image-01583", create_info.image, create_info_loc.dot(Field::image),
                                 "was created with VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT bit and "
                                 "format (%s), but the uncompressed image view format (%s) is not size compatible. %s",
                                 string_VkFormat(image_format), string_VkFormat(view_format),
                                 DescribeFormatsSizeCompatible(image_format, view_format).c_str());
            }
        } else {
            // both image and view are compressed format, the BLOCK_TEXEL_VIEW_COMPATIBLE is set for no reason here
            if (vkuFormatCompatibilityClass(view_format) != vkuFormatCompatibilityClass(image_format)) {
                skip |= LogError("VUID-VkImageViewCreateInfo-image-01583", create_info.image, create_info_loc.dot(Field::format),
                                 "(%s) and the image format (%s) are not compatible.", string_VkFormat(view_format),
                                 string_VkFormat(image_format));
            }
        }
    }

    if (create_info.flags & VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DEFERRED_BIT_EXT) {
        if (!enabled_features.fragmentDensityMapDeferred) {
            skip |= LogError("VUID-VkImageViewCreateInfo-flags-03567", create_info.image, create_info_loc.dot(Field::flags),
                             "includes VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DEFERRED_BIT_EXT but the "
                             "fragmentDensityMapDeferred feature is not enabled.");
        }
        if (create_info.flags & VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT) {
            skip |= LogError("VUID-VkImageViewCreateInfo-flags-03568", create_info.image, create_info_loc.dot(Field::flags),
                             "(%s) includes both DEFERRED_BIT and DYNAMIC_BIT.",
                             string_VkImageViewCreateFlags(create_info.flags).c_str());
        }
    }
    if (IsExtEnabled(extensions.vk_ext_fragment_density_map2)) {
        if ((image_flags & VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT) && (image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) &&
            (normalized_subresource_range.layerCount > phys_dev_ext_props.fragment_density_map2_props.maxSubsampledArrayLayers)) {
            skip |= LogError("VUID-VkImageViewCreateInfo-image-03569", create_info.image, create_info_loc.dot(Field::image),
                             "was created with flags containing "
                             "VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT and usage containing VK_IMAGE_USAGE_SAMPLED_BIT "
                             "but subresourceRange.layerCount (%s) must be less than or equal to maxSubsampledArrayLayers (%" PRIu32
                             ")",
                             string_LayerCount(image_state.GetArrayLayers(), create_info.subresourceRange).c_str(),
                             phys_dev_ext_props.fragment_density_map2_props.maxSubsampledArrayLayers);
        }
    }

    if (const auto astc_decode_mode = vku::FindStructInPNextChain<VkImageViewASTCDecodeModeEXT>(create_info.pNext);
        (astc_decode_mode != nullptr)) {
        if ((enabled_features.decodeModeSharedExponent == VK_FALSE) &&
            (astc_decode_mode->decodeMode == VK_FORMAT_E5B9G9R9_UFLOAT_PACK32)) {
            skip |= LogError("VUID-VkImageViewASTCDecodeModeEXT-decodeMode-02231", create_info.image,
                             create_info_loc.pNext(Struct::VkImageViewASTCDecodeModeEXT, Field::decodeMode),
                             "is VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 but decodeModeSharedExponent feature is not enabled.");
        }
    }

    if (IsExtEnabled(extensions.vk_khr_portability_subset)) {
        // If swizzling is disabled, make sure it isn't used
        // NOTE: as of spec version 1.2.183, VUID 04465 states: "all elements of components _must_ be
        // VK_COMPONENT_SWIZZLE_IDENTITY."
        //       However, issue https://github.com/KhronosGroup/Vulkan-Portability/issues/27 points out that the identity can
        //       also be defined via R, G, B, A enums in the correct order.
        //       Spec change is at https://gitlab.khronos.org/vulkan/vulkan/-/merge_requests/4600
        if ((VK_FALSE == enabled_features.imageViewFormatSwizzle) && !IsIdentitySwizzle(create_info.components)) {
            skip |= LogError("VUID-VkImageViewCreateInfo-imageViewFormatSwizzle-04465", create_info.image, create_info_loc,
                             "(portability error): swizzle is disabled for this device.");
        }

        // Ensure ImageView's format has the same number of bits and components as Image's format if format reinterpretation is
        // disabled
        if (!enabled_features.imageViewFormatReinterpretation &&
            !FormatsEqualComponentBits(create_info.format, image_state.GetFormat())) {
            skip |= LogError("VUID-VkImageViewCreateInfo-imageViewFormatReinterpretation-04466", create_info.image, create_info_loc,
                             "(portability error): ImageView format must have"
                             " the same number of components and bits per component as the Image's format");
        }
    }

    if (const auto image_view_min_lod = vku::FindStructInPNextChain<VkImageViewMinLodCreateInfoEXT>(create_info.pNext);
        image_view_min_lod) {
        if ((!enabled_features.minLod) && (image_view_min_lod->minLod != 0)) {
            skip |= LogError("VUID-VkImageViewMinLodCreateInfoEXT-minLod-06455", create_info.image,
                             create_info_loc.pNext(Struct::VkImageViewMinLodCreateInfoEXT, Field::minLod),
                             "%f, but the minLod feature is not enabled.", image_view_min_lod->minLod);
        }
        const float max_level =
            static_cast<float>(create_info.subresourceRange.baseMipLevel + (normalized_subresource_range.levelCount - 1));
        if (image_view_min_lod->minLod > max_level) {
            skip |= LogError("VUID-VkImageViewMinLodCreateInfoEXT-minLod-06456", create_info.image,
                             create_info_loc.pNext(Struct::VkImageViewMinLodCreateInfoEXT, Field::minLod),
                             "(%f) must be less or equal to the index of the last mipmap level "
                             "accessible to the view (%f).",
                             image_view_min_lod->minLod, max_level);
        }
    }

    const auto ycbcr_conversion = vku::FindStructInPNextChain<VkSamplerYcbcrConversionInfo>(create_info.pNext);
    if (ycbcr_conversion && ycbcr_conversion->conversion != VK_NULL_HANDLE) {
        auto ycbcr_state = Get<vvl::SamplerYcbcrConversion>(ycbcr_conversion->conversion);
        if (ycbcr_state && (create_info.format != ycbcr_state->create_info.format)) {
            skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06658", create_info.image,
                             create_info_loc.pNext(Struct::VkSamplerYcbcrConversionInfo, Field::conversion),
                             "was created with format %s which is different than create_info.format %s.",
                             string_VkFormat(ycbcr_state->create_info.format), string_VkFormat(view_format));
        }
    } else if ((image_usage & VK_IMAGE_USAGE_SAMPLED_BIT) && FormatRequiresYcbcrConversionExplicitly(view_format)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-format-06415", create_info.image, create_info_loc.dot(Field::image),
                         "was created with VK_IMAGE_USAGE_SAMPLED_BIT, but create_info.format %s requires a "
                         "VkSamplerYcbcrConversion but one was not passed in the pNext chain.\n%s",
                         string_VkFormat(view_format), PrintPNextChain(Struct::VkImageViewCreateInfo, create_info.pNext).c_str());
    }

    if (create_info.viewType != VK_IMAGE_VIEW_TYPE_2D && create_info.viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY) {
        VkImageUsageFlags decode_usage = VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_SRC_BIT_KHR |
                                         VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
        if (image_usage & decode_usage) {
            skip |= LogError("VUID-VkImageViewCreateInfo-image-04817", create_info.image, create_info_loc.dot(Field::viewType),
                             "%s is incompatible with video decode usage.", string_VkImageViewType(create_info.viewType));
        }

        VkImageUsageFlags encode_usage = VK_IMAGE_USAGE_VIDEO_ENCODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR |
                                         VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR;
        if (image_usage & encode_usage) {
            skip |= LogError("VUID-VkImageViewCreateInfo-image-04818", create_info.image, create_info_loc.dot(Field::viewType),
                             "%s is incompatible with video encode usage.", string_VkImageViewType(create_info.viewType));
        }

        VkImageUsageFlags quantization_map_usage =
            VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR;
        if (image_usage & quantization_map_usage) {
            skip |= LogError("VUID-VkImageViewCreateInfo-image-10261", create_info.image, create_info_loc.dot(Field::viewType),
                             "%s is incompatible with video encode quantization map usage.",
                             string_VkImageViewType(create_info.viewType));
        }
    }

    if ((create_info.flags & VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT) &&
        !enabled_features.descriptorBufferCaptureReplay) {
        skip |= LogError("VUID-VkImageViewCreateInfo-flags-08106", create_info.image, create_info_loc.dot(Field::flags),
                         "includes VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT but the "
                         "descriptorBufferCaptureReplay feature is not enabled.");
    }

    if (const auto opaque_capture_descriptor_buffer =
            vku::FindStructInPNextChain<VkOpaqueCaptureDescriptorDataCreateInfoEXT>(create_info.pNext);
        opaque_capture_descriptor_buffer && !(create_info.flags & VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-08107", create_info.image, create_info_loc.dot(Field::flags),
                         "(%s) is missing VK_IMAGE_VIEW_CREATE_DESCRIPTOR_BUFFER_CAPTURE_REPLAY_BIT_EXT but "
                         "VkOpaqueCaptureDescriptorDataCreateInfoEXT is in pNext chain.\n%s",
                         string_VkImageViewCreateFlags(create_info.flags).c_str(),
                         PrintPNextChain(Struct::VkImageViewCreateInfo, create_info.pNext).c_str());
    }

    skip |= ValidateImageViewSampleWeightQCOM(create_info, image_state, create_info_loc);

    // If Chroma subsampled format ( _420_ or _422_ )
    if (vkuFormatIsXChromaSubsampled(view_format) && !IsIntegerMultipleOf(image_state.GetExtent().width, 2)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-format-04714", device, create_info_loc.dot(Field::format),
                         "(%s) is X Chroma Subsampled (has _422 or _420 suffix) so the image width (%" PRIu32
                         ") must be a multiple of 2.",
                         string_VkFormat(view_format), image_state.GetExtent().width);
    }
    if (vkuFormatIsYChromaSubsampled(view_format) && !IsIntegerMultipleOf(image_state.GetExtent().height, 2)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-format-04715", device, create_info_loc.dot(Field::format),
                         "(%s) is Y Chroma Subsampled (has _420 suffix) so the image height (%" PRIu32 ") must be a multiple of 2.",
                         string_VkFormat(view_format), image_state.GetExtent().height);
    }

    return skip;
}

bool CoreChecks::PreCallValidateCreateImageView(VkDevice device, const VkImageViewCreateInfo* pCreateInfo,
                                                [[maybe_unused]] const VkAllocationCallbacks* pAllocator,
                                                [[maybe_unused]] VkImageView* pView, const ErrorObject& error_obj) const {
    bool skip = false;

    skip |= ValidateDeviceQueueSupport(error_obj.location);
    const Location create_info_loc = error_obj.location.dot(Field::pCreateInfo);
    skip |= ValidateImageViewCreateInfo(*pCreateInfo, create_info_loc);

    return skip;
}

bool CoreChecks::PreCallValidateDestroyImageView(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator,
                                                 const ErrorObject& error_obj) const {
    bool skip = false;
    if (auto image_view_state = Get<vvl::ImageView>(imageView)) {
        skip |= ValidateObjectNotInUse(image_view_state.get(), error_obj.location, "VUID-vkDestroyImageView-imageView-01026");
    }
    return skip;
}

bool CoreChecks::ValidateGetImageSubresourceLayout(const vvl::Image& image_state, const VkImageSubresource& subresource,
                                                   const Location& subresource_loc) const {
    bool skip = false;
    const bool is_2 = subresource_loc.function != Func::vkGetImageSubresourceLayout;
    const VkImageAspectFlags aspect_mask = subresource.aspectMask;

    // The aspectMask member of pSubresource must only have a single bit set
    if (CountSetBits(aspect_mask) != 1) {
        const char* vuid =
            is_2 ? "VUID-vkGetImageSubresourceLayout2-aspectMask-00997" : "VUID-vkGetImageSubresourceLayout-aspectMask-00997";
        skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask), "(%s) must have exactly 1 bit set.",
                         string_VkImageAspectFlags(aspect_mask).c_str());
    }

    // mipLevel must be less than the mipLevels specified in VkImageCreateInfo when the image was created
    if (subresource.mipLevel >= image_state.GetMipLevels()) {
        const char* vuid =
            is_2 ? "VUID-vkGetImageSubresourceLayout2-mipLevel-01716" : "VUID-vkGetImageSubresourceLayout-mipLevel-01716";
        skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::mipLevel),
                         "(%" PRIu32 ") must be less than the mipLevel used to create the image (%" PRIu32 ").",
                         subresource.mipLevel, image_state.GetMipLevels());
    }

    // arrayLayer must be less than the arrayLayers specified in VkImageCreateInfo when the image was created
    if (subresource.arrayLayer >= image_state.GetArrayLayers()) {
        const char* vuid =
            is_2 ? "VUID-vkGetImageSubresourceLayout2-arrayLayer-01717" : "VUID-vkGetImageSubresourceLayout-arrayLayer-01717";
        skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::arrayLayer),
                         "(%" PRIu32 ") must be less than the arrayLayer used to create the image (%" PRIu32 ").",
                         subresource.arrayLayer, image_state.GetArrayLayers());
    }

    const VkFormat image_format = image_state.GetFormat();
    const bool tiling_linear_optimal =
        image_state.GetTiling() == VK_IMAGE_TILING_LINEAR || image_state.GetTiling() == VK_IMAGE_TILING_OPTIMAL;
    if (vkuFormatIsColor(image_format) && !vkuFormatIsMultiplane(image_format) && (aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT) &&
        tiling_linear_optimal) {
        const char* vuid =
            is_2 ? "VUID-vkGetImageSubresourceLayout2-format-08886" : "VUID-vkGetImageSubresourceLayout-format-08886";
        skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask),
                         "is %s but image was created with color format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                         string_VkFormat(image_format));
    }

    if (vkuFormatHasDepth(image_format) && ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) == 0)) {
        const char* vuid =
            is_2 ? "VUID-vkGetImageSubresourceLayout2-format-04462" : "VUID-vkGetImageSubresourceLayout-format-04462";
        skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask),
                         "is %s but image was created with depth format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                         string_VkFormat(image_format));
    }

    if (vkuFormatHasStencil(image_format) && ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) == 0)) {
        const char* vuid =
            is_2 ? "VUID-vkGetImageSubresourceLayout2-format-04463" : "VUID-vkGetImageSubresourceLayout-format-04463";
        skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask),
                         "is %s but image was created with stencil format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                         string_VkFormat(image_format));
    }

    if (!vkuFormatHasDepth(image_format) && !vkuFormatHasStencil(image_format)) {
        if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) {
            const char* vuid =
                is_2 ? "VUID-vkGetImageSubresourceLayout2-format-04464" : "VUID-vkGetImageSubresourceLayout-format-04464";
            skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask),
                             "is %s but image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                             string_VkFormat(image_format));
        }
    }

    // subresource's aspect must be compatible with image's format.
    if (image_state.GetTiling() == VK_IMAGE_TILING_LINEAR) {
        if (vkuFormatIsMultiplane(image_format) && !IsOnlyOneValidPlaneAspect(image_format, aspect_mask)) {
            const char* vuid =
                is_2 ? "VUID-vkGetImageSubresourceLayout2-tiling-08717" : "VUID-vkGetImageSubresourceLayout-tiling-08717";
            skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask), "(%s) is invalid for format %s.",
                             string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
        }
    } else if (image_state.GetTiling() == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
        if ((aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT) && (aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT) &&
            (aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT) && (aspect_mask != VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT)) {
            const char* vuid =
                is_2 ? "VUID-vkGetImageSubresourceLayout2-tiling-09435" : "VUID-vkGetImageSubresourceLayout-tiling-09433";
            skip |=
                LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask),
                         "(%s) must be VK_IMAGE_ASPECT_MEMORY_PLANE_i_BIT_EXT.", string_VkImageAspectFlags(aspect_mask).c_str());
        } else {
            VkImageDrmFormatModifierPropertiesEXT drm_format_properties = vku::InitStructHelper();
            DispatchGetImageDrmFormatModifierPropertiesEXT(device, image_state.VkHandle(), &drm_format_properties);

            VkDrmFormatModifierPropertiesListEXT fmt_drm_props = vku::InitStructHelper();
            VkFormatProperties2 fmt_props_2 = vku::InitStructHelper(&fmt_drm_props);
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, image_state.GetFormat(), &fmt_props_2);
            std::vector<VkDrmFormatModifierPropertiesEXT> drm_properties{fmt_drm_props.drmFormatModifierCount};
            fmt_drm_props.pDrmFormatModifierProperties = drm_properties.data();
            DispatchGetPhysicalDeviceFormatProperties2Helper(api_version, physical_device, image_state.GetFormat(), &fmt_props_2);

            uint32_t max_plane_count = 0u;

            for (auto const& drm_property : drm_properties) {
                if (drm_format_properties.drmFormatModifier == drm_property.drmFormatModifier) {
                    max_plane_count = drm_property.drmFormatModifierPlaneCount;
                    break;
                }
            }

            VkImageAspectFlagBits allowed_plane_indices[] = {
                VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT, VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT,
                VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT, VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT};

            bool is_valid = false;

            for (uint32_t i = 0u; i < max_plane_count; ++i) {
                if (aspect_mask == allowed_plane_indices[i]) {
                    is_valid = true;
                    break;
                }
            }

            if (!is_valid) {
                const char* vuid =
                    is_2 ? "VUID-vkGetImageSubresourceLayout2-tiling-09435" : "VUID-vkGetImageSubresourceLayout-tiling-09433";
                skip |= LogError(vuid, image_state.Handle(), subresource_loc.dot(Field::aspectMask),
                                 "is %s for image format %s, but drmFormatModifierPlaneCount is %" PRIu32
                                 " (drmFormatModifier = %" PRIu64 ").",
                                 string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_state.GetFormat()),
                                 max_plane_count, drm_format_properties.drmFormatModifier);
            }
        }
    }

    if (image_state.IsExternalBuffer() && image_state.GetBoundMemoryStates().empty()) {
        const char* vuid = is_2 ? "VUID-vkGetImageSubresourceLayout2-image-09434" : "VUID-vkGetImageSubresourceLayout-image-09432";
        skip |= LogError(vuid, image_state.Handle(), subresource_loc,
                         "Attempt to query layout from an image created with "
                         "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID handleType which has not yet been "
                         "bound to memory.");
    }

    return skip;
}

bool CoreChecks::PreCallValidateGetImageSubresourceLayout(VkDevice device, VkImage image, const VkImageSubresource* pSubresource,
                                                          VkSubresourceLayout* pLayout, const ErrorObject& error_obj) const {
    bool skip = false;
    auto image_state = Get<vvl::Image>(image);
    if (pSubresource && pLayout && image_state) {
        skip |= ValidateGetImageSubresourceLayout(*image_state, *pSubresource, error_obj.location.dot(Field::pSubresource));
        const VkImageTiling image_tiling = image_state->GetTiling();
        if (image_tiling != VK_IMAGE_TILING_LINEAR && image_tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
            skip |= LogError("VUID-vkGetImageSubresourceLayout-image-07790", image, error_obj.location,
                             "image was created with tiling %s.", string_VkImageTiling(image_tiling));
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateGetImageSubresourceLayout2(VkDevice device, VkImage image, const VkImageSubresource2* pSubresource,
                                                           VkSubresourceLayout2* pLayout, const ErrorObject& error_obj) const {
    bool skip = false;
    auto image_state = Get<vvl::Image>(image);
    if (pSubresource && pLayout && image_state) {
        skip |= ValidateGetImageSubresourceLayout(*image_state, pSubresource->imageSubresource,
                                                  error_obj.location.dot(Field::pSubresource).dot(Field::imageSubresource));
    }
    return skip;
}

bool CoreChecks::PreCallValidateGetImageSubresourceLayout2KHR(VkDevice device, VkImage image,
                                                              const VkImageSubresource2KHR* pSubresource,
                                                              VkSubresourceLayout2KHR* pLayout,
                                                              const ErrorObject& error_obj) const {
    return PreCallValidateGetImageSubresourceLayout2(device, image, pSubresource, pLayout, error_obj);
}

bool CoreChecks::PreCallValidateGetImageSubresourceLayout2EXT(VkDevice device, VkImage image,
                                                              const VkImageSubresource2EXT* pSubresource,
                                                              VkSubresourceLayout2EXT* pLayout,
                                                              const ErrorObject& error_obj) const {
    return PreCallValidateGetImageSubresourceLayout2(device, image, pSubresource, pLayout, error_obj);
}

bool CoreChecks::PreCallValidateGetImageDrmFormatModifierPropertiesEXT(VkDevice device, VkImage image,
                                                                       VkImageDrmFormatModifierPropertiesEXT* pProperties,
                                                                       const ErrorObject& error_obj) const {
    bool skip = false;
    if (auto image_state = Get<vvl::Image>(image)) {
        if (image_state->GetTiling() != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT) {
            skip |=
                LogError("VUID-vkGetImageDrmFormatModifierPropertiesEXT-image-02272", image, error_obj.location.dot(Field::image),
                         "was created with tiling %s.", string_VkImageTiling(image_state->GetTiling()));
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateTransitionImageLayout(VkDevice device, uint32_t transitionCount,
                                                      const VkHostImageLayoutTransitionInfo* pTransitions,
                                                      const ErrorObject& error_obj) const {
    bool skip = false;

    for (uint32_t i = 0; i < transitionCount; ++i) {
        const Location transition_loc = error_obj.location.dot(Struct::VkHostImageLayoutTransitionInfo, Field::pTransitions, i);
        const auto& transition = pTransitions[i];
        const auto image_state = Get<vvl::Image>(transition.image);
        if (!image_state) continue;
        const VkFormat image_format = image_state->GetFormat();
        const auto aspect_mask = transition.subresourceRange.aspectMask;
        const bool has_depth_mask = (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != 0;
        const bool has_stencil_mask = (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != 0;

        if ((image_state->usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT) == 0) {
            const LogObjectList objlist(device, image_state->Handle());
            skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-09055", objlist, transition_loc.dot(Field::image),
                             "was created with usage (%s) which does not contain "
                             "VK_IMAGE_USAGE_HOST_TRANSFER_BIT.",
                             string_VkImageUsageFlags2KHR(image_state->usage).c_str());
        }

        if (!enabled_features.synchronization2) {
            if (IsValueIn(transition.oldLayout, {VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL})) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-synchronization2-07793", objlist,
                                 transition_loc.dot(Field::oldLayout), "is %s but the synchronization2 feature was not enabled.",
                                 string_VkImageLayout(transition.oldLayout));
            }
            if (IsValueIn(transition.newLayout, {VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL})) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-synchronization2-07794", objlist,
                                 transition_loc.dot(Field::newLayout), "is %s but the synchronization2 feature was not enabled.",
                                 string_VkImageLayout(transition.newLayout));
            }
        }

        if (!enabled_features.dynamicRenderingLocalRead) {
            if (transition.oldLayout == VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-dynamicRenderingLocalRead-09551", objlist,
                                 transition_loc.dot(Field::oldLayout),
                                 "is %s but the dynamicRenderingLocalRead feature was not enabled",
                                 string_VkImageLayout(transition.oldLayout));
            }
            if (transition.newLayout == VK_IMAGE_LAYOUT_RENDERING_LOCAL_READ) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-dynamicRenderingLocalRead-09552", objlist,
                                 transition_loc.dot(Field::newLayout),
                                 "is %s but the dynamicRenderingLocalRead feature was not enabled",
                                 string_VkImageLayout(transition.newLayout));
            }
        }

        if (transition.oldLayout == VK_IMAGE_LAYOUT_ZERO_INITIALIZED_EXT) {
            const LogObjectList objlist(device, image_state->Handle());
            if (!enabled_features.zeroInitializeDeviceMemory) {
                skip |=
                    LogError("VUID-VkHostImageLayoutTransitionInfo-oldLayout-10767", objlist, transition_loc.dot(Field::oldLayout),
                             "is %s, but the zeroInitializeDeviceMemory feature was not enabled.",
                             string_VkImageLayout(transition.oldLayout));
            }
            skip |= ValidateImageBarrierZeroInitializedSubresourceRange(transition.subresourceRange, *image_state, objlist,
                                                                        transition_loc);
        }
        if (transition.newLayout == VK_IMAGE_LAYOUT_ATTACHMENT_FEEDBACK_LOOP_OPTIMAL_EXT) {
            if (!enabled_features.attachmentFeedbackLoopLayout) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-attachmentFeedbackLoopLayout-07313", objlist,
                                 transition_loc.dot(Field::newLayout),
                                 "is %s, but the attachmentFeedbackLoopLayout feature was not enabled.",
                                 string_VkImageLayout(transition.newLayout));
            }
        }

        skip |= ValidateImageLayoutAgainstImageUsage(transition_loc.dot(Field::oldLayout), transition.image, transition.oldLayout,
                                                     image_state->usage);
        skip |= ValidateImageLayoutAgainstImageUsage(transition_loc.dot(Field::newLayout), transition.image, transition.newLayout,
                                                     image_state->usage);

        auto image_layer_count_var = Field::arrayLayers;
        uint32_t image_layer_count = image_state->GetArrayLayers();
        if (enabled_features.maintenance9 && (image_state->create_flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT) != 0) {
            image_layer_count_var = Field::depth;
            const auto layers = LayersFromRange(transition.subresourceRange);
            const auto extent = image_state->GetEffectiveSubresourceExtent(layers.aspectMask, layers.mipLevel);
            image_layer_count = extent.depth;
        }
        skip |= ValidateImageSubresourceRange(image_state->GetMipLevels(), image_layer_count, transition.subresourceRange,
                                              image_layer_count_var, image_state->VkHandle(),
                                              transition_loc.dot(Field::subresourceRange));
        skip |= ValidateMemoryIsBoundToImage(LogObjectList(device, transition.image), *image_state,
                                             transition_loc.dot(Field::image), "VUID-VkHostImageLayoutTransitionInfo-image-01932");

        if (vkuFormatIsColor(image_format) && (aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT)) {
            if (!vkuFormatIsMultiplane(image_format)) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-09241", objlist,
                                 transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                 "is %s (not VK_IMAGE_ASPECT_COLOR_BIT) and image was created with format %s.",
                                 string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
            } else if (!image_state->disjoint) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-09242", objlist,
                                 transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                 "is %s (not VK_IMAGE_ASPECT_COLOR_BIT) and image was created with format %s.",
                                 string_VkImageAspectFlags(aspect_mask).c_str(), string_VkFormat(image_format));
            }
        }
        if ((vkuFormatIsMultiplane(image_format)) && (image_state->disjoint)) {
            if (!IsValidPlaneAspect(image_format, aspect_mask) && ((aspect_mask & VK_IMAGE_ASPECT_COLOR_BIT) == 0)) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-01672", objlist,
                                 transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                 "is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                                 string_VkFormat(image_format));
            }
        }
        if (vkuFormatIsDepthAndStencil(image_format)) {
            if (enabled_features.separateDepthStencilLayouts) {
                if (!has_depth_mask && !has_stencil_mask) {
                    const LogObjectList objlist(device, image_state->Handle());
                    skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-03319", objlist,
                                     transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                     "is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                                     string_VkFormat(image_format));
                }
            } else {
                if (!has_depth_mask || !has_stencil_mask) {
                    const LogObjectList objlist(device, image_state->Handle());
                    skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-03320", objlist,
                                     transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                     "is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                                     string_VkFormat(image_format));
                }
            }
        } else if (vkuFormatIsDepthOnly(image_format)) {
            if ((aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) != VK_IMAGE_ASPECT_DEPTH_BIT) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-10749", objlist,
                                 transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                 "is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                                 string_VkFormat(image_format));
            }
        } else if (vkuFormatIsStencilOnly(image_format)) {
            if ((aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) != VK_IMAGE_ASPECT_STENCIL_BIT) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-image-10750", objlist,
                                 transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                 "is %s and image was created with format %s.", string_VkImageAspectFlags(aspect_mask).c_str(),
                                 string_VkFormat(image_format));
            }
        }
        if (aspect_mask & VK_IMAGE_ASPECT_DEPTH_BIT) {
            if ((transition.oldLayout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL) ||
                (transition.oldLayout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL) ||
                (transition.newLayout == VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL) ||
                (transition.newLayout == VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL)) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-aspectMask-08702", objlist,
                                 transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                 "is %s meaning that neither oldLayout nor newLayout can be "
                                 "VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL"
                                 " (oldLayout = %s and newLayout = %s).",
                                 string_VkImageAspectFlags(aspect_mask).c_str(), string_VkImageLayout(transition.oldLayout),
                                 string_VkImageLayout(transition.newLayout));
            }
        }
        if (aspect_mask & VK_IMAGE_ASPECT_STENCIL_BIT) {
            if ((transition.oldLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ||
                (transition.oldLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL) ||
                (transition.newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ||
                (transition.newLayout == VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL)) {
                const LogObjectList objlist(device, image_state->Handle());
                skip |= LogError("VUID-VkHostImageLayoutTransitionInfo-aspectMask-08703", objlist,
                                 transition_loc.dot(Field::subresourceRange).dot(Field::aspectMask),
                                 "is %s meaning that neither oldLayout nor newLayout can be "
                                 "VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL or VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL"
                                 " (oldLayout = %s and newLayout = %s).",
                                 string_VkImageAspectFlags(aspect_mask).c_str(), string_VkImageLayout(transition.oldLayout),
                                 string_VkImageLayout(transition.newLayout));
            }
        }
        if (!IsValueIn(transition.oldLayout,
                       {VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PREINITIALIZED, VK_IMAGE_LAYOUT_ZERO_INITIALIZED_EXT})) {
            skip |= ValidateHostCopyImageLayout(transition.image, transition.oldLayout, transition_loc.dot(Field::oldLayout),
                                                Field::pCopySrcLayouts, "VUID-VkHostImageLayoutTransitionInfo-oldLayout-09230");
        }

        skip |= ValidateHostCopyImageLayout(transition.image, transition.newLayout, transition_loc.dot(Field::newLayout),
                                            Field::pCopyDstLayouts, "VUID-VkHostImageLayoutTransitionInfo-newLayout-09057");
        if (transition.oldLayout != VK_IMAGE_LAYOUT_UNDEFINED) {
            skip |= ValidateHostCopyCurrentLayout(transition.oldLayout, transition.subresourceRange, *image_state,
                                                  transition_loc.dot(Field::oldLayout));
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateTransitionImageLayoutEXT(VkDevice device, uint32_t transitionCount,
                                                         const VkHostImageLayoutTransitionInfoEXT* pTransitions,
                                                         const ErrorObject& error_obj) const {
    return PreCallValidateTransitionImageLayout(device, transitionCount, pTransitions, error_obj);
}

void CoreChecks::PostCallRecordTransitionImageLayout(VkDevice device, uint32_t transitionCount,
                                                     const VkHostImageLayoutTransitionInfo* pTransitions,
                                                     const RecordObject& record_obj) {
    if (record_obj.result != VK_SUCCESS) {
        return;
    }

    for (uint32_t i = 0; i < transitionCount; ++i) {
        auto& transition = pTransitions[i];
        auto image_state = Get<vvl::Image>(transition.image);
        if (!image_state) continue;
        image_state->SetImageLayout(transition.subresourceRange, transition.newLayout);
    }
}

void CoreChecks::PostCallRecordTransitionImageLayoutEXT(VkDevice device, uint32_t transitionCount,
                                                        const VkHostImageLayoutTransitionInfoEXT* pTransitions,
                                                        const RecordObject& record_obj) {
    PostCallRecordTransitionImageLayout(device, transitionCount, pTransitions, record_obj);
}

// Validates the image is allowed to be protected
bool CoreChecks::ValidateProtectedImage(const vvl::CommandBuffer& cb_state, const vvl::Image& image_state, const Location& loc,
                                        const char* vuid, const char* more_message) const {
    bool skip = false;

    // if driver supports protectedNoFault the operation is valid, just has undefined values
    if ((!phys_dev_props_core11.protectedNoFault) && (cb_state.unprotected == true) && (image_state.unprotected == false)) {
        const LogObjectList objlist(cb_state.Handle(), image_state.Handle());
        skip |= LogError(vuid, objlist, loc, "(%s) is a protected image, but command buffer (%s) is unprotected.%s",
                         FormatHandle(image_state).c_str(), FormatHandle(cb_state).c_str(), more_message);
    }
    return skip;
}

// Validates the image is allowed to be unprotected
bool CoreChecks::ValidateUnprotectedImage(const vvl::CommandBuffer& cb_state, const vvl::Image& image_state, const Location& loc,
                                          const char* vuid, const char* more_message) const {
    bool skip = false;

    // if driver supports protectedNoFault the operation is valid, just has undefined values
    if ((!phys_dev_props_core11.protectedNoFault) && (cb_state.unprotected == false) && (image_state.unprotected == true)) {
        const LogObjectList objlist(cb_state.Handle(), image_state.Handle());
        skip |= LogError(vuid, objlist, loc, "(%s) is an unprotected image, but command buffer (%s) is protected.%s",
                         FormatHandle(image_state).c_str(), FormatHandle(cb_state).c_str(), more_message);
    }
    return skip;
}

bool CoreChecks::ValidateImageViewSampleWeightQCOM(const VkImageViewCreateInfo& create_info, const vvl::Image& image_state,
                                                   const Location& create_info_loc) const {
    bool skip = false;

    auto sample_weight_info = vku::FindStructInPNextChain<VkImageViewSampleWeightCreateInfoQCOM>(create_info.pNext);
    if (!sample_weight_info) {
        return skip;
    }

    const auto normalized_subresource_range = image_state.NormalizeSubresourceRange(create_info.subresourceRange);
    const VkImageAspectFlags aspect_mask = create_info.subresourceRange.aspectMask;
    const VkImageType image_type = image_state.GetImageType();
    const VkImageUsageFlags2KHR image_usage = image_state.usage;
    const VkExtent3D image_extent = image_state.GetExtent();

    if ((enabled_features.textureSampleWeighted == VK_FALSE)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06944", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "but textureSampleWeighted feature is not enabled.");
    }

    // Validate VkImage and VkImageView compatibility with  VkImageViewSampleWeightCreateInfoQCOM
    if ((image_usage & VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM) == 0) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06945", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "but image not created with VK_IMAGE_USAGE_SAMPLE_WEIGHT_BIT_QCOM.");
    }
    if (!IsIdentitySwizzle(create_info.components)) {
        skip |= LogError(
            "VUID-VkImageViewCreateInfo-pNext-06946", create_info.image, create_info_loc.dot(Field::pNext),
            "chain includes VkImageViewSampleWeightCreateInfoQCOM "
            "but not all members of pCreateInfo->components have identity swizzle. Here are the actual swizzle values:\n%s",
            string_VkComponentMapping(create_info.components).c_str());
    }

    if (aspect_mask != VK_IMAGE_ASPECT_COLOR_BIT) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06947", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "but the subresource range aspect mask (%s) is not VK_IMAGE_ASPECT_COLOR_BIT.",
                         string_VkImageAspectFlags(aspect_mask).c_str());
    }
    if (normalized_subresource_range.levelCount != 1) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06948", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "so subresourcesRange.levelCount must be 1, but it is %s.",
                         string_LevelCount(image_state.GetMipLevels(), create_info.subresourceRange).c_str());
    }
    if ((create_info.viewType != VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (create_info.viewType != VK_IMAGE_VIEW_TYPE_2D_ARRAY)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06949", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "but the view type (%s) is not VK_IMAGE_VIEW_TYPE_1D_ARRAY or VK_IMAGE_VIEW_TYPE_2D_ARRAY.",
                         string_VkImageViewType(create_info.viewType));
    }
    if ((create_info.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (image_type != VK_IMAGE_TYPE_1D)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06950", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "and the viewType is VK_IMAGE_VIEW_TYPE_1D_ARRAY but the image type (%s) is not VK_IMAGE_TYPE_1D.",
                         string_VkImageType(image_type));
    }
    if ((create_info.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (normalized_subresource_range.layerCount != 2)) {
        skip |=
            LogError("VUID-VkImageViewCreateInfo-pNext-06951", create_info.image, create_info_loc.dot(Field::pNext),
                     "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                     "and the view type is VK_IMAGE_VIEW_TYPE_1D_ARRAY so the subresourceRange.layerCount must be 2, but it is %s.",
                     string_LayerCount(image_state.GetArrayLayers(), create_info.subresourceRange).c_str());
    }

    const uint32_t filter_width_aligned_to_four = (sample_weight_info->filterSize.width + 3) & ~(3);
    const uint32_t min_image_width =
        sample_weight_info->numPhases * (std::max(filter_width_aligned_to_four, sample_weight_info->filterSize.height));
    if ((create_info.viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY) && (image_extent.width < min_image_width)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06952", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "for a VK_IMAGE_VIEW_TYPE_1D_ARRAY with an image created with extent.width (%" PRIu32
                         "), but numPhases (%" PRIu32 "), filterSize.width (%" PRIu32 "), and filterSize.height (%" PRIu32
                         ") requires an image extent.width greater than or equal to (%" PRIu32 ").",
                         image_extent.width, sample_weight_info->numPhases, sample_weight_info->filterSize.width,
                         sample_weight_info->filterSize.height, min_image_width);
    }
    if ((create_info.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (image_type != VK_IMAGE_TYPE_2D)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06953", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "and the view type is VK_IMAGE_VIEW_TYPE_2D_ARRAY but the image created type (%s) is "
                         "not VK_IMAGE_TYPE_2D.",
                         string_VkImageType(image_type));
    }
    if ((create_info.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) &&
        (normalized_subresource_range.layerCount < (sample_weight_info->numPhases))) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06954", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "and the view type is VK_IMAGE_VIEW_TYPE_1D_ARRAY but the subresourceRange.layerCount (%s) is not greater "
                         "than or equal to "
                         "VkImageViewSampleWeightCreateInfoQCOM::numPhases (%" PRIu32 ").",
                         string_LayerCount(image_state.GetArrayLayers(), create_info.subresourceRange).c_str(),
                         sample_weight_info->numPhases);
    }
    if ((create_info.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (image_extent.width < sample_weight_info->filterSize.width)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06955", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "for a VK_IMAGE_VIEW_TYPE_2D_ARRAY but the created image extent.width (%" PRIu32
                         ") is not greater than or equal to filterSize.width (%" PRIu32 ").",
                         image_extent.width, sample_weight_info->filterSize.width);
    }
    if ((create_info.viewType == VK_IMAGE_VIEW_TYPE_2D_ARRAY) && (image_extent.width < sample_weight_info->filterSize.height)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06956", create_info.image, create_info_loc.dot(Field::pNext),
                         "chain includes VkImageViewSampleWeightCreateInfoQCOM "
                         "for a VK_IMAGE_VIEW_TYPE_2D_ARRAY but the created image extent.height (%" PRIu32
                         ") is not greater than or equal to filterSize.height (%" PRIu32 ").",
                         image_extent.height, sample_weight_info->filterSize.height);
    }

    const Location filter_size_loc = create_info_loc.pNext(vvl::Struct::VkImageViewSampleWeightCreateInfoQCOM, Field::filterSize);
    if ((sample_weight_info->filterSize.height > phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.height)) {
        skip |= LogError("VUID-VkImageViewCreateInfo-pNext-06957", create_info.image, filter_size_loc.dot(Field::height),
                         "(%" PRIu32
                         ") is not less than or equal to "
                         "VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterDimension.height (%" PRIu32 ").",
                         sample_weight_info->filterSize.height,
                         phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.height);
    }

    // Valiate VkImageViewSampleWeightCreateInfoQCOM
    if ((sample_weight_info->filterSize.width > phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.width)) {
        skip |= LogError(
            "VUID-VkImageViewSampleWeightCreateInfoQCOM-filterSize-06958", create_info.image, filter_size_loc.dot(Field::width),
            "(%" PRIu32
            ") is not less than or equal to "
            "VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterDimension.width (%" PRIu32 ").",
            sample_weight_info->filterSize.width, phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.width);
    }
    if ((sample_weight_info->filterSize.height > phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.height)) {
        skip |= LogError(
            "VUID-VkImageViewSampleWeightCreateInfoQCOM-filterSize-06959", create_info.image, filter_size_loc.dot(Field::height),
            "(%" PRIu32
            ") is not less than or equal to "
            "VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterDimension.height (%" PRIu32 ").",
            sample_weight_info->filterSize.width, phys_dev_ext_props.image_processing_props.maxWeightFilterDimension.width);
    }

    const Location filter_center_loc =
        create_info_loc.pNext(vvl::Struct::VkImageViewSampleWeightCreateInfoQCOM, Field::filterCenter);
    if ((static_cast<uint32_t>(sample_weight_info->filterCenter.x) >= sample_weight_info->filterSize.width)) {
        skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-filterCenter-06960", create_info.image,
                         filter_center_loc.dot(Field::x),
                         "(%" PRId32
                         ") is not less than "
                         "VkImageViewSampleWeightCreateInfoQCOM::filterSize.width (%" PRIu32 ").",
                         sample_weight_info->filterCenter.x, sample_weight_info->filterSize.width);
    }
    if ((static_cast<uint32_t>(sample_weight_info->filterCenter.y) >= sample_weight_info->filterSize.height)) {
        skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-filterCenter-06961", create_info.image,
                         filter_center_loc.dot(Field::y),
                         "(%" PRId32
                         ") is not less than "
                         "VkImageViewSampleWeightCreateInfoQCOM::filterSize.height (%" PRIu32 ").",
                         sample_weight_info->filterCenter.y, sample_weight_info->filterSize.height);
    }
    if (!IsPowerOfTwo(sample_weight_info->numPhases) || (MostSignificantBit(sample_weight_info->numPhases) % 2 != 0)) {
        skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-numPhases-06962", create_info.image,
                         create_info_loc.pNext(vvl::Struct::VkImageViewSampleWeightCreateInfoQCOM, Field::numPhases),
                         "(%" PRIu32
                         ") is not a power of two squared "
                         "value (i.e., 1, 4, 16, 64, 256, etc.)",
                         sample_weight_info->numPhases);
    }
    if ((sample_weight_info->numPhases > phys_dev_ext_props.image_processing_props.maxWeightFilterPhases)) {
        skip |= LogError("VUID-VkImageViewSampleWeightCreateInfoQCOM-numPhases-06963", create_info.image,
                         create_info_loc.pNext(vvl::Struct::VkImageViewSampleWeightCreateInfoQCOM, Field::numPhases),
                         "(%" PRIu32
                         ") is not less than or equal to "
                         "VkPhysicalDeviceImageProcessingPropertiesQCOM::maxWeightFilterPhases (%" PRIu32 ")",
                         sample_weight_info->numPhases, phys_dev_ext_props.image_processing_props.maxWeightFilterPhases);
    }
    return skip;
}
