/* Copyright (c) 2022-2023 The Khronos Group Inc.
 * Copyright (c) 2022-2023 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.
 *
 * Author: Daniel Rakos <daniel.rakos@rastergrid.com>
 */

#include <assert.h>
#include <vector>

#include "vk_enum_string_helper.h"
#include "chassis.h"
#include "core_validation.h"

// Flags validation error if the associated call is made inside a video coding block.
// The apiName routine should ONLY be called outside a video coding block.
bool CoreChecks::InsideVideoCodingScope(const CMD_BUFFER_STATE &cb_state, const char *apiName, const char *msgCode) const {
    bool inside = false;
    if (cb_state.bound_video_session) {
        inside = LogError(cb_state.commandBuffer(), msgCode, "%s: It is invalid to issue this call inside a video coding block.",
                          apiName);
    }
    return inside;
}

// Flags validation error if the associated call is made outside a video coding block.
// The apiName routine should ONLY be called inside a video coding block.
bool CoreChecks::OutsideVideoCodingScope(const CMD_BUFFER_STATE &cb_state, const char *apiName, const char *msgCode) const {
    bool outside = false;
    if (!cb_state.bound_video_session) {
        outside = LogError(cb_state.commandBuffer(), msgCode, "%s: This call must be issued inside a video coding block.", apiName);
    }
    return outside;
}

std::vector<VkVideoFormatPropertiesKHR> CoreChecks::GetVideoFormatProperties(VkImageUsageFlags image_usage,
                                                                             const VkVideoProfileListInfoKHR *profile_list) const {
    auto format_info = LvlInitStruct<VkPhysicalDeviceVideoFormatInfoKHR>();
    format_info.imageUsage = image_usage;
    format_info.pNext = profile_list;

    uint32_t format_count = 0;
    DispatchGetPhysicalDeviceVideoFormatPropertiesKHR(physical_device, &format_info, &format_count, nullptr);
    std::vector<VkVideoFormatPropertiesKHR> format_props(format_count, LvlInitStruct<VkVideoFormatPropertiesKHR>());
    DispatchGetPhysicalDeviceVideoFormatPropertiesKHR(physical_device, &format_info, &format_count, format_props.data());

    return format_props;
}

std::vector<VkVideoFormatPropertiesKHR> CoreChecks::GetVideoFormatProperties(VkImageUsageFlags image_usage,
                                                                             const VkVideoProfileInfoKHR *profile) const {
    auto profile_list = LvlInitStruct<VkVideoProfileListInfoKHR>();
    profile_list.profileCount = 1;
    profile_list.pProfiles = profile;

    return GetVideoFormatProperties(image_usage, &profile_list);
}

bool CoreChecks::IsVideoFormatSupported(VkFormat format, VkImageUsageFlags image_usage,
                                        const VkVideoProfileInfoKHR *profile) const {
    auto format_props_list = GetVideoFormatProperties(image_usage, profile);
    for (const auto &format_props : format_props_list) {
        if (format_props.format == format) return true;
    }
    return false;
}

bool CoreChecks::ValidateVideoPictureResource(const VideoPictureResource &picture_resource, VkCommandBuffer cmdbuf,
                                              const VIDEO_SESSION_STATE &vs_state, const char *api_name, const char *where,
                                              const char *coded_offset_vuid, const char *coded_extent_vuid) const {
    bool skip = false;

    const auto &profile_caps = vs_state.profile->GetCapabilities();

    if (coded_offset_vuid) {
        VkOffset2D offset_granularity{0, 0};
        if (vs_state.GetCodecOp() == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR &&
            vs_state.GetH264PictureLayout() == VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_SEPARATE_PLANES_BIT_KHR) {
            offset_granularity = profile_caps.decode_h264.fieldOffsetGranularity;
        }

        if (!IsIntegerMultipleOf(picture_resource.coded_offset, offset_granularity)) {
            LogObjectList objlist(cmdbuf);
            objlist.add(vs_state.videoSession());
            skip |= LogError(objlist, coded_offset_vuid,
                             "%s(): codedOffset (%u,%u) is not an integer multiple of the codedOffsetGranularity (%u,%u).%s",
                             api_name, picture_resource.coded_offset.x, picture_resource.coded_offset.y, offset_granularity.x,
                             offset_granularity.y, where);
        }
    }

    if (coded_extent_vuid &&
        !IsBetweenInclusive(picture_resource.coded_extent, profile_caps.base.minCodedExtent, vs_state.create_info.maxCodedExtent)) {
        LogObjectList objlist(cmdbuf);
        objlist.add(vs_state.videoSession());
        skip |= LogError(objlist, coded_extent_vuid,
                         "%s(): codedExtent (%u,%u) is outside of the range (%u,%u)-(%u,%u) supported by %s.%s", api_name,
                         picture_resource.coded_extent.width, picture_resource.coded_extent.height,
                         profile_caps.base.minCodedExtent.width, profile_caps.base.minCodedExtent.height,
                         vs_state.create_info.maxCodedExtent.width, vs_state.create_info.maxCodedExtent.height,
                         report_data->FormatHandle(vs_state.videoSession()).c_str(), where);
    }

    if (picture_resource.base_array_layer >= picture_resource.image_view_state->create_info.subresourceRange.layerCount) {
        LogObjectList objlist(cmdbuf);
        objlist.add(vs_state.videoSession());
        objlist.add(picture_resource.image_view_state->Handle());
        objlist.add(picture_resource.image_state->Handle());
        skip |= LogError(objlist, "VUID-VkVideoPictureResourceInfoKHR-baseArrayLayer-07175",
                         "%s(): baseArrayLayer (%u) is greater than or equal to the layerCount (%u) "
                         "the %s specified in imageViewBinding was created with.%s",
                         api_name, picture_resource.base_array_layer,
                         picture_resource.image_view_state->create_info.subresourceRange.layerCount,
                         report_data->FormatHandle(picture_resource.image_view_state->Handle()).c_str(), where);
    }

    return skip;
}

template bool CoreChecks::ValidateVideoProfileInfo<VkDevice>(const VkVideoProfileInfoKHR *profile, const VkDevice object,
                                                             const char *api_name, const char *where) const;
template bool CoreChecks::ValidateVideoProfileInfo<VkPhysicalDevice>(const VkVideoProfileInfoKHR *profile,
                                                                     const VkPhysicalDevice object, const char *api_name,
                                                                     const char *where) const;

template <typename T1>
bool CoreChecks::ValidateVideoProfileInfo(const VkVideoProfileInfoKHR *profile, const T1 object, const char *api_name,
                                          const char *where) const {
    bool skip = false;

    const char *profile_pnext_msg = "%s(): missing %s from the pNext chain of %s";

    if (GetBitSetCount(profile->chromaSubsampling) != 1) {
        skip |= LogError(object, "VUID-VkVideoProfileInfoKHR-chromaSubsampling-07013",
                         "%s(): chromaSubsampling in %s must have a single bit set", api_name, where);
    }

    if (GetBitSetCount(profile->lumaBitDepth) != 1) {
        skip |= LogError(object, "VUID-VkVideoProfileInfoKHR-lumaBitDepth-07014",
                         "%s(): lumaBitDepth in %s must have a single bit set", api_name, where);
    }

    if (profile->chromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR) {
        if (GetBitSetCount(profile->chromaBitDepth) != 1) {
            skip |= LogError(object, "VUID-VkVideoProfileInfoKHR-chromaSubsampling-07015",
                             "%s(): chromaBitDepth in %s must have a single bit set", api_name, where);
        }
    }

    switch (profile->videoCodecOperation) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
            const auto decode_h264 = LvlFindInChain<VkVideoDecodeH264ProfileInfoKHR>(profile->pNext);
            if (decode_h264 == nullptr) {
                skip |= LogError(object, "VUID-VkVideoProfileInfoKHR-videoCodecOperation-07179", profile_pnext_msg, api_name,
                                 "VkVideoDecodeH264ProfileInfoKHR", where);
            }
            break;
        }

        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
            const auto decode_h265 = LvlFindInChain<VkVideoDecodeH265ProfileInfoKHR>(profile->pNext);
            if (decode_h265 == nullptr) {
                skip |= LogError(object, "VUID-VkVideoProfileInfoKHR-videoCodecOperation-07180", profile_pnext_msg, api_name,
                                 "VkVideoDecodeH265ProfileInfoKHR", where);
            }
            break;
        }

        default:
            assert(false);
            skip = true;
            break;
    }

    return skip;
}

template bool CoreChecks::ValidateVideoProfileListInfo<VkDevice>(
    const VkVideoProfileListInfoKHR *profile_list, const VkDevice object, const char *api_name, bool expect_decode_profile,
    const char *missing_decode_profile_msg_code, bool expect_encode_profile, const char *missing_encode_profile_msg_code) const;
template bool CoreChecks::ValidateVideoProfileListInfo<VkPhysicalDevice>(
    const VkVideoProfileListInfoKHR *profile_list, const VkPhysicalDevice object, const char *api_name, bool expect_decode_profile,
    const char *missing_decode_profile_msg_code, bool expect_encode_profile, const char *missing_encode_profile_msg_code) const;

template <typename T1>
bool CoreChecks::ValidateVideoProfileListInfo(const VkVideoProfileListInfoKHR *profile_list, const T1 object, const char *api_name,
                                              bool expect_decode_profile, const char *missing_decode_profile_msg_code,
                                              bool expect_encode_profile, const char *missing_encode_profile_msg_code) const {
    bool skip = false;

    bool has_decode_profile = false;
    bool has_encode_profile = false;

    if (profile_list) {
        char where[64];
        for (uint32_t i = 0; i < profile_list->profileCount; ++i) {
            snprintf(where, sizeof(where), "VkVideoProfileListInfoKHR::pProfiles[%u]", i);
            skip |= ValidateVideoProfileInfo(&profile_list->pProfiles[i], object, api_name, where);

            switch (profile_list->pProfiles[i].videoCodecOperation) {
                case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
                case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
                    if (has_decode_profile) {
                        skip |= LogError(object, "VUID-VkVideoProfileListInfoKHR-pProfiles-06813",
                                         "%s(): the video profile list contains more than one profile with decode "
                                         "codec operation",
                                         api_name);
                    }
                    has_decode_profile = true;
                    break;

                case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_EXT:
                case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_EXT:
                    has_encode_profile = true;
                    break;

                default:
                    assert(false);
                    skip = true;
                    break;
            }
        }
    }

    if (expect_decode_profile && !has_decode_profile) {
        skip |= LogError(device, missing_decode_profile_msg_code,
                         "%s(): the video profile list contains no profile with decode codec operation", api_name);
    }

    if (expect_encode_profile && !has_encode_profile) {
        skip |= LogError(device, missing_encode_profile_msg_code,
                         "%s(): the video profile list contains no profile with encode codec operation", api_name);
    }

    return skip;
}

bool CoreChecks::ValidateDecodeH264ParametersAddInfo(const VkVideoDecodeH264SessionParametersAddInfoKHR *add_info, VkDevice device,
                                                     const char *api_name, const char *where,
                                                     const VkVideoDecodeH264SessionParametersCreateInfoKHR *create_info,
                                                     const VIDEO_SESSION_PARAMETERS_STATE *template_state) const {
    bool skip = false;

    layer_data::unordered_set<VIDEO_SESSION_PARAMETERS_STATE::ParameterKey> keys;
    auto template_data = template_state ? template_state->Lock() : VIDEO_SESSION_PARAMETERS_STATE::ReadOnlyAccessor();

    if (add_info) {
        // Verify SPS key uniqueness
        for (uint32_t i = 0; i < add_info->stdSPSCount; ++i) {
            auto key = VIDEO_SESSION_PARAMETERS_STATE::GetH264SPSKey(add_info->pStdSPSs[i]);
            if (!keys.emplace(key).second) {
                skip |= LogError(device, "VUID-VkVideoDecodeH264SessionParametersAddInfoKHR-None-04825",
                                 "%s(): H.264 SPS keys are not unique in %s", api_name, where);
                break;
            }
        }
    }

    if (create_info) {
        // Verify SPS capacity
        if (template_data) {
            for (const auto &it : template_data->h264.sps) {
                keys.emplace(it.first);
            }
        }
        if (keys.size() > create_info->maxStdSPSCount) {
            skip |= LogError(device, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSession-07204",
                             "%s(): number of H.264 SPS entries to add (%zu) is larger than "
                             "VkVideoDecodeH264SessionParametersCreateInfoKHR::maxStdSPSCount (%u)",
                             api_name, keys.size(), create_info->maxStdSPSCount);
        }
    }

    keys.clear();

    if (add_info) {
        // Verify PPS key uniqueness
        for (uint32_t i = 0; i < add_info->stdPPSCount; ++i) {
            auto key = VIDEO_SESSION_PARAMETERS_STATE::GetH264PPSKey(add_info->pStdPPSs[i]);
            if (!keys.emplace(key).second) {
                skip |= LogError(device, "VUID-VkVideoDecodeH264SessionParametersAddInfoKHR-None-04826",
                                 "%s(): H.264 PPS keys are not unique in %s", api_name, where);
                break;
            }
        }
    }

    if (create_info) {
        // Verify PPS capacity
        if (template_data) {
            for (const auto &it : template_data->h264.pps) {
                keys.emplace(it.first);
            }
        }
        if (keys.size() > create_info->maxStdPPSCount) {
            skip |= LogError(device, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSession-07205",
                             "%s(): number of H.264 PPS entries to add (%zu) is larger than "
                             "VkVideoDecodeH264SessionParametersCreateInfoKHR::maxStdPPSCount (%u)",
                             api_name, keys.size(), create_info->maxStdPPSCount);
        }
    }

    return skip;
}

bool CoreChecks::ValidateDecodeH265ParametersAddInfo(const VkVideoDecodeH265SessionParametersAddInfoKHR *add_info, VkDevice device,
                                                     const char *api_name, const char *where,
                                                     const VkVideoDecodeH265SessionParametersCreateInfoKHR *create_info,
                                                     const VIDEO_SESSION_PARAMETERS_STATE *template_state) const {
    bool skip = false;

    layer_data::unordered_set<VIDEO_SESSION_PARAMETERS_STATE::ParameterKey> keys;
    auto template_data = template_state ? template_state->Lock() : VIDEO_SESSION_PARAMETERS_STATE::ReadOnlyAccessor();

    if (add_info) {
        // Verify VPS key uniqueness
        for (uint32_t i = 0; i < add_info->stdVPSCount; ++i) {
            auto key = VIDEO_SESSION_PARAMETERS_STATE::GetH265VPSKey(add_info->pStdVPSs[i]);
            if (!keys.emplace(key).second) {
                skip |= LogError(device, "VUID-VkVideoDecodeH265SessionParametersAddInfoKHR-None-04833",
                                 "%s(): H.265 VPS keys are not unique in %s", api_name, where);
                break;
            }
        }
    }

    if (create_info) {
        // Verify VPS capacity
        if (template_data) {
            for (const auto &it : template_data->h265.vps) {
                keys.emplace(it.first);
            }
        }
        if (keys.size() > create_info->maxStdVPSCount) {
            skip |= LogError(device, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSession-07207",
                             "%s(): number of H.265 VPS entries to add (%zu) is larger than "
                             "VkVideoDecodeH265SessionParametersCreateInfoKHR::maxStdVPSCount (%u)",
                             api_name, keys.size(), create_info->maxStdVPSCount);
        }
    }

    keys.clear();

    if (add_info) {
        // Verify SPS key uniqueness
        for (uint32_t i = 0; i < add_info->stdSPSCount; ++i) {
            auto key = VIDEO_SESSION_PARAMETERS_STATE::GetH265SPSKey(add_info->pStdSPSs[i]);
            if (!keys.emplace(key).second) {
                skip |= LogError(device, "VUID-VkVideoDecodeH265SessionParametersAddInfoKHR-None-04834",
                                 "%s(): H.265 SPS keys are not unique in %s", api_name, where);
                break;
            }
        }
    }

    if (create_info) {
        // Verify SPS capacity
        if (template_data) {
            for (const auto &it : template_data->h265.sps) {
                keys.emplace(it.first);
            }
        }
        if (keys.size() > create_info->maxStdSPSCount) {
            skip |= LogError(device, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSession-07208",
                             "%s(): number of H.265 SPS entries to add (%zu) is larger than "
                             "VkVideoDecodeH265SessionParametersCreateInfoKHR::maxStdSPSCount (%u)",
                             api_name, keys.size(), create_info->maxStdSPSCount);
        }
    }

    keys.clear();

    if (add_info) {
        // Verify PPS key uniqueness
        for (uint32_t i = 0; i < add_info->stdPPSCount; ++i) {
            auto key = VIDEO_SESSION_PARAMETERS_STATE::GetH265PPSKey(add_info->pStdPPSs[i]);
            if (!keys.emplace(key).second) {
                skip |= LogError(device, "VUID-VkVideoDecodeH265SessionParametersAddInfoKHR-None-04835",
                                 "%s(): H.265 PPS keys are not unique in %s", api_name, where);
                break;
            }
        }
    }

    if (create_info) {
        // Verify PPS capacity
        if (template_data) {
            for (const auto &it : template_data->h265.pps) {
                keys.emplace(it.first);
            }
        }
        if (keys.size() > create_info->maxStdPPSCount) {
            skip |= LogError(device, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSession-07209",
                             "%s(): number of H.265 PPS entries to add (%zu) is larger than "
                             "VkVideoDecodeH265SessionParametersCreateInfoKHR::maxStdPPSCount (%u)",
                             api_name, keys.size(), create_info->maxStdPPSCount);
        }
    }

    return skip;
}

bool CoreChecks::ValidateVideoDecodeInfoH264(const CMD_BUFFER_STATE &cb_state, const VkVideoDecodeInfoKHR &decode_info) const {
    bool skip = false;

    const char *pnext_msg = "%s(): missing %s from the pNext chain of %s";

    const auto &vs_state = *cb_state.bound_video_session;
    const auto &vsp_state = *cb_state.bound_video_session_parameters;
    const auto session_params = vsp_state.Lock();

    bool interlaced_frame_support =
        (vs_state.profile->GetH264PictureLayout() != VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR);

    auto picture_info = LvlFindInChain<VkVideoDecodeH264PictureInfoKHR>(decode_info.pNext);
    if (picture_info) {
        auto std_picture_info = picture_info->pStdPictureInfo;

        if (!interlaced_frame_support && std_picture_info->flags.field_pic_flag) {
            LogObjectList objlist(cb_state.commandBuffer());
            objlist.add(vs_state.videoSession());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-None-07258",
                             "vkCmdDecodeVideoKHR(): decode output picture is a field but the bound video session "
                             "%s was not created with interlaced frame support",
                             report_data->FormatHandle(vs_state.videoSession()).c_str());
        }

        for (uint32_t i = 0; i < picture_info->sliceCount; ++i) {
            if (picture_info->pSliceOffsets[i] >= decode_info.srcBufferRange) {
                skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pSliceOffsets-07153",
                                 "vkCmdDecodeVideoKHR(): VkVideoDecodeH264PictureInfoKHR::pSliceOffsets[%u] "
                                 "(%u) is greater than or equal to pDecodeInfo->srcBufferRange (%" PRIu64 ")",
                                 i, picture_info->pSliceOffsets[i], decode_info.srcBufferRange);
            }
        }

        if (session_params.GetH264SPS(std_picture_info->seq_parameter_set_id) == nullptr) {
            LogObjectList objlist(cb_state.commandBuffer());
            objlist.add(vsp_state.videoSessionParameters());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-StdVideoH264SequenceParameterSet-07154",
                             "vkCmdDecodeVideoKHR(): no H.264 SPS with seq_parameter_set_id = %u "
                             "exists in the bound video session parameters object %s",
                             std_picture_info->seq_parameter_set_id,
                             report_data->FormatHandle(vsp_state.videoSessionParameters()).c_str());
        }

        if (session_params.GetH264PPS(std_picture_info->seq_parameter_set_id, std_picture_info->pic_parameter_set_id) == nullptr) {
            LogObjectList objlist(cb_state.commandBuffer());
            objlist.add(vsp_state.videoSessionParameters());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-StdVideoH264PictureParameterSet-07155",
                             "vkCmdDecodeVideoKHR(): no H.264 PPS with seq_parameter_set_id = %u "
                             "and pic_parameter_set_id = %u exists in the bound video session parameters object %s",
                             std_picture_info->seq_parameter_set_id, std_picture_info->pic_parameter_set_id,
                             report_data->FormatHandle(vsp_state.videoSessionParameters()).c_str());
        }
    } else {
        skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pNext-07152", pnext_msg, "vkCmdDecodeVideoKHR()",
                         "VkVideoDecodeH264PictureInfoKHR", "pDecodeInfo");
    }

    if (decode_info.pSetupReferenceSlot) {
        auto dpb_slot_info = LvlFindInChain<VkVideoDecodeH264DpbSlotInfoKHR>(decode_info.pSetupReferenceSlot->pNext);
        if (dpb_slot_info) {
            VideoPictureID picture_id(*vs_state.profile, *decode_info.pSetupReferenceSlot);
            if (!interlaced_frame_support && !picture_id.IsFrame()) {
                LogObjectList objlist(cb_state.commandBuffer());
                objlist.add(vs_state.videoSession());
                skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07259",
                                 "vkCmdDecodeVideoKHR(): reconstructed picture is a field but the bound "
                                 "video session %s was not created with interlaced frame support",
                                 report_data->FormatHandle(vs_state.videoSession()).c_str());
            }

            if (picture_info) {
                bool dst_is_field = (picture_info->pStdPictureInfo->flags.field_pic_flag != 0);
                bool dst_is_bottom_field = (picture_info->pStdPictureInfo->flags.bottom_field_flag != 0);

                if (!dst_is_field && !picture_id.IsFrame()) {
                    skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07261",
                                     "vkCmdDecodeVideoKHR(): decode output picture is a frame but the "
                                     "reconstructed picture is not a frame");
                }

                if (dst_is_field && !dst_is_bottom_field && !picture_id.IsTopField()) {
                    skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07262",
                                     "vkCmdDecodeVideoKHR(): decode output picture is a top field but the "
                                     "reconstructed picture is not a top field");
                }

                if (dst_is_field && dst_is_bottom_field && !picture_id.IsBottomField()) {
                    skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07263",
                                     "vkCmdDecodeVideoKHR(): decode output picture is a bottom field but the "
                                     "reconstructed picture is not a bottom field");
                }
            }
        } else {
            skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07156", pnext_msg,
                             "vkCmdDecodeVideoKHR()", "VkVideoDecodeH264DpbSlotInfoKHR", "pDecodeInfo->pSetupReferenceSlot");
        }
    }

    for (uint32_t i = 0; i < decode_info.referenceSlotCount; ++i) {
        auto dpb_slot_info = LvlFindInChain<VkVideoDecodeH264DpbSlotInfoKHR>(decode_info.pReferenceSlots[i].pNext);
        if (dpb_slot_info) {
            VideoPictureID picture_id(*vs_state.profile, decode_info.pReferenceSlots[i]);
            if (!interlaced_frame_support && !picture_id.IsFrame()) {
                LogObjectList objlist(cb_state.commandBuffer());
                objlist.add(vs_state.videoSession());
                skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07260",
                                 "vkCmdDecodeVideoKHR(): reference picture specified in "
                                 "pDecodeInfo->pReferneceSlots[%u] is a field but the bound "
                                 "video session %s was not created with interlaced frame support",
                                 i, report_data->FormatHandle(vs_state.videoSession()).c_str());
            }
        } else {
            skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pNext-07157",
                             "vkCmdDecodeVideoKHR(): missing VkVideoDecodeH264DpbSlotInfoKHR from the "
                             "pNext chain of pDecodeInfo->pReferenceSlots[%u]",
                             i);
        }
    }

    return skip;
}

bool CoreChecks::ValidateVideoDecodeInfoH265(const CMD_BUFFER_STATE &cb_state, const VkVideoDecodeInfoKHR &decode_info) const {
    bool skip = false;

    const char *pnext_msg = "%s(): missing %s from the pNext chain of %s";

    const auto &vsp_state = *cb_state.bound_video_session_parameters;
    const auto session_params = vsp_state.Lock();

    auto picture_info = LvlFindInChain<VkVideoDecodeH265PictureInfoKHR>(decode_info.pNext);
    if (picture_info) {
        auto std_picture_info = picture_info->pStdPictureInfo;

        for (uint32_t i = 0; i < picture_info->sliceSegmentCount; ++i) {
            if (picture_info->pSliceSegmentOffsets[i] >= decode_info.srcBufferRange) {
                skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pSliceSegmentOffsets-07159",
                                 "vkCmdDecodeVideoKHR(): VkVideoDecodeH265PictureInfoKHR::pSliceSegmentOffsets[%u] "
                                 "(%u) is greater than or equal to pDecodeInfo->srcBufferRange (%" PRIu64 ")",
                                 i, picture_info->pSliceSegmentOffsets[i], decode_info.srcBufferRange);
            }
        }

        if (session_params.GetH265VPS(std_picture_info->sps_video_parameter_set_id) == nullptr) {
            LogObjectList objlist(cb_state.commandBuffer());
            objlist.add(vsp_state.videoSessionParameters());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-StdVideoH265VideoParameterSet-07160",
                             "vkCmdDecodeVideoKHR(): no H.265 VPS with sps_video_parameter_set_id = %u "
                             "exists in the bound video session parameters object %s",
                             std_picture_info->sps_video_parameter_set_id,
                             report_data->FormatHandle(vsp_state.videoSessionParameters()).c_str());
        }

        if (session_params.GetH265SPS(std_picture_info->sps_video_parameter_set_id, std_picture_info->pps_seq_parameter_set_id) ==
            nullptr) {
            LogObjectList objlist(cb_state.commandBuffer());
            objlist.add(vsp_state.videoSessionParameters());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-StdVideoH265SequenceParameterSet-07161",
                             "vkCmdDecodeVideoKHR(): no H.265 SPS with sps_video_parameter_set_id = %u "
                             "and pps_seq_parameter_set_id = %u exists in the bound video session "
                             "parameters object %s",
                             std_picture_info->sps_video_parameter_set_id, std_picture_info->pps_seq_parameter_set_id,
                             report_data->FormatHandle(vsp_state.videoSessionParameters()).c_str());
        }

        if (session_params.GetH265PPS(std_picture_info->sps_video_parameter_set_id, std_picture_info->pps_seq_parameter_set_id,
                                      std_picture_info->pps_pic_parameter_set_id) == nullptr) {
            LogObjectList objlist(cb_state.commandBuffer());
            objlist.add(vsp_state.videoSessionParameters());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-StdVideoH265PictureParameterSet-07162",
                             "vkCmdDecodeVideoKHR(): no H.265 SPS with sps_video_parameter_set_id = %u, "
                             "pps_seq_parameter_set_id = %u, and pps_pic_parameter_set_id = %u exists in "
                             "the bound video session parameters object %s",
                             std_picture_info->sps_video_parameter_set_id, std_picture_info->pps_seq_parameter_set_id,
                             std_picture_info->pps_pic_parameter_set_id,
                             report_data->FormatHandle(vsp_state.videoSessionParameters()).c_str());
        }
    } else {
        skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pNext-07158", pnext_msg, "vkCmdDecodeVideoKHR()",
                         "VkVideoDecodeH265PictureInfoKHR", "pDecodeInfo");
    }

    if (decode_info.pSetupReferenceSlot) {
        auto dpb_slot_info = LvlFindInChain<VkVideoDecodeH265DpbSlotInfoKHR>(decode_info.pSetupReferenceSlot->pNext);
        if (!dpb_slot_info) {
            skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07163", pnext_msg,
                             "vkCmdDecodeVideoKHR()", "VkVideoDecodeH265DpbSlotInfoKHR", "pDecodeInfo->pSetupReferenceSlot");
        }
    }

    for (uint32_t i = 0; i < decode_info.referenceSlotCount; ++i) {
        auto dpb_slot_info = LvlFindInChain<VkVideoDecodeH265DpbSlotInfoKHR>(decode_info.pReferenceSlots[i].pNext);
        if (!dpb_slot_info) {
            skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-pNext-07164",
                             "vkCmdDecodeVideoKHR(): missing VkVideoDecodeH265DpbSlotInfoKHR from the "
                             "pNext chain of pDecodeInfo->pReferenceSlots[%u]",
                             i);
        }
    }

    return skip;
}

bool CoreChecks::ValidateActiveReferencePictureCount(const CMD_BUFFER_STATE &cb_state,
                                                     const VkVideoDecodeInfoKHR &decode_info) const {
    bool skip = false;

    const auto &vs_state = *cb_state.bound_video_session;

    uint32_t active_reference_picture_count = decode_info.referenceSlotCount;

    if (vs_state.GetCodecOp() == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR) {
        for (uint32_t i = 0; i < decode_info.referenceSlotCount; ++i) {
            auto dpb_slot_info = LvlFindInChain<VkVideoDecodeH264DpbSlotInfoKHR>(decode_info.pReferenceSlots[i].pNext);
            if (!dpb_slot_info) continue;

            auto std_reference_info = dpb_slot_info->pStdReferenceInfo;
            if (!std_reference_info) continue;

            if (std_reference_info->flags.top_field_flag && std_reference_info->flags.bottom_field_flag) {
                ++active_reference_picture_count;
            }
        }
    }

    if (active_reference_picture_count > vs_state.create_info.maxActiveReferencePictures) {
        LogObjectList objlist(cb_state.commandBuffer());
        objlist.add(vs_state.videoSession());
        skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-activeReferencePictureCount-07150",
                         "vkCmdDecodeVideoKHR(): more active reference pictures (%u) were specified than "
                         "the maxActiveReferencePictures (%u) the bound video session %s was created with",
                         active_reference_picture_count, vs_state.create_info.maxActiveReferencePictures,
                         report_data->FormatHandle(vs_state.videoSession()).c_str());
    }

    return skip;
}

bool CoreChecks::ValidateReferencePictureUseCount(const CMD_BUFFER_STATE &cb_state, const VkVideoDecodeInfoKHR &decode_info) const {
    bool skip = false;

    const auto &vs_state = *cb_state.bound_video_session;

    std::vector<uint32_t> dpb_frame_use_count(vs_state.create_info.maxDpbSlots, 0);

    bool interlaced_frame_support = false;
    std::vector<uint32_t> dpb_top_field_use_count;
    std::vector<uint32_t> dpb_bottom_field_use_count;

    if (vs_state.GetCodecOp() == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR) {
        if (vs_state.profile->GetH264PictureLayout() != VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR) {
            interlaced_frame_support = true;
            dpb_top_field_use_count.resize(vs_state.create_info.maxDpbSlots, 0);
            dpb_bottom_field_use_count.resize(vs_state.create_info.maxDpbSlots, 0);
        }
    }

    // Collect use count for each DPB across the elements pReferenceSlots and pSetupReferenceSlot
    for (uint32_t i = 0; i <= decode_info.referenceSlotCount; ++i) {
        const VkVideoReferenceSlotInfoKHR *slot =
            (i == decode_info.referenceSlotCount) ? decode_info.pSetupReferenceSlot : &decode_info.pReferenceSlots[i];

        if (slot == nullptr) continue;
        if (slot->slotIndex < 0 || (uint32_t)slot->slotIndex >= vs_state.create_info.maxDpbSlots) continue;

        ++dpb_frame_use_count[slot->slotIndex];

        if (!interlaced_frame_support) continue;

        if (vs_state.GetCodecOp() == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR) {
            auto dpb_slot_info = LvlFindInChain<VkVideoDecodeH264DpbSlotInfoKHR>(slot->pNext);
            if (!dpb_slot_info) continue;

            auto std_reference_info = dpb_slot_info->pStdReferenceInfo;
            if (!std_reference_info) continue;

            if (std_reference_info->flags.top_field_flag || std_reference_info->flags.bottom_field_flag) {
                --dpb_frame_use_count[slot->slotIndex];
            }
            if (std_reference_info->flags.top_field_flag) {
                ++dpb_top_field_use_count[slot->slotIndex];
            }
            if (std_reference_info->flags.bottom_field_flag) {
                ++dpb_bottom_field_use_count[slot->slotIndex];
            }
        }
    }

    for (uint32_t i = 0; i < vs_state.create_info.maxDpbSlots; ++i) {
        if (dpb_frame_use_count[i] > 1) {
            skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-dpbFrameUseCount-07176",
                             "vkCmdDecodeVideoKHR(): frame in DPB slot %u is referred to multiple times across "
                             "pDecodeInfo->pSetupReferenceSlot and the elements of pDecodeInfo->pReferenceSlots",
                             i);
        }
        if (interlaced_frame_support) {
            if (dpb_top_field_use_count[i] > 1) {
                skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-dpbTopFieldUseCount-07177",
                                 "vkCmdDecodeVideoKHR(): top field in DPB slot %u is referred to multiple "
                                 "times across pDecodeInfo->pSetupReferenceSlot and the elements of "
                                 "pDecodeInfo->pReferenceSlots",
                                 i);
            }
            if (dpb_bottom_field_use_count[i] > 1) {
                skip |= LogError(cb_state.commandBuffer(), "VUID-vkCmdDecodeVideoKHR-dpbBottomFieldUseCount-07178",
                                 "vkCmdDecodeVideoKHR(): bottom field in DPB slot %u is referred to multiple "
                                 "times across pDecodeInfo->pSetupReferenceSlot and the elements of "
                                 "pDecodeInfo->pReferenceSlots",
                                 i);
            }
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateGetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice,
                                                                      const VkVideoProfileInfoKHR *pVideoProfile,
                                                                      VkVideoCapabilitiesKHR *pCapabilities) const {
    bool skip = false;

    skip |= ValidateVideoProfileInfo(pVideoProfile, device, "vkGetPhysicalDeviceVideoCapabilitiesKHR", "pVideoProfile");

    const char *caps_pnext_msg = "vkGetPhysicalDeviceVideoCapabilitiesKHR(): Missing %s from the pNext chain of pCapabilities";

    bool is_decode = false;

    switch (pVideoProfile->videoCodecOperation) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
            is_decode = true;
            if (!LvlFindInChain<VkVideoDecodeH264CapabilitiesKHR>(pCapabilities->pNext)) {
                skip |= LogError(physicalDevice, "VUID-vkGetPhysicalDeviceVideoCapabilitiesKHR-pVideoProfile-07184", caps_pnext_msg,
                                 "VkVideoDecodeH264CapabilitiesKHR");
            }
            break;

        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
            is_decode = true;
            if (!LvlFindInChain<VkVideoDecodeH265CapabilitiesKHR>(pCapabilities->pNext)) {
                skip |= LogError(physicalDevice, "VUID-vkGetPhysicalDeviceVideoCapabilitiesKHR-pVideoProfile-07185", caps_pnext_msg,
                                 "VkVideoDecodeH265CapabilitiesKHR");
            }
            break;

        default:
            break;
    }

    if (is_decode && !LvlFindInChain<VkVideoDecodeCapabilitiesKHR>(pCapabilities->pNext)) {
        skip |= LogError(physicalDevice, "VUID-vkGetPhysicalDeviceVideoCapabilitiesKHR-pVideoProfile-07183", caps_pnext_msg,
                         "VkVideoDecodeCapabilitiesKHR");
    }

    return skip;
}

bool CoreChecks::PreCallValidateGetPhysicalDeviceVideoFormatPropertiesKHR(
    VkPhysicalDevice physicalDevice, const VkPhysicalDeviceVideoFormatInfoKHR *pVideoFormatInfo,
    uint32_t *pVideoFormatPropertyCount, VkVideoFormatPropertiesKHR *pVideoFormatProperties) const {
    bool skip = false;

    const auto *video_profiles = LvlFindInChain<VkVideoProfileListInfoKHR>(pVideoFormatInfo->pNext);
    if (video_profiles && video_profiles->profileCount != 0) {
        skip |= ValidateVideoProfileListInfo(video_profiles, physicalDevice, "vkGetPhysicalDeviceVideoFormatPropertiesKHR", false,
                                             nullptr, false, nullptr);
    } else {
        const char *msg = video_profiles ? "no VkVideoProfileListInfoKHR structure found in the pNext chain of pVideoFormatInfo"
                                         : "profileCount is zero in the VkVideoProfileListInfoKHR structure included in the "
                                           "pNext chain of pVideoFormatInfo";
        skip |= LogError(physicalDevice, "VUID-vkGetPhysicalDeviceVideoFormatPropertiesKHR-pNext-06812",
                         "vkGetPhysicalDeviceVideoFormatPropertiesKHR(): %s", msg);
    }

    return skip;
}

bool CoreChecks::PreCallValidateCreateVideoSessionKHR(VkDevice device, const VkVideoSessionCreateInfoKHR *pCreateInfo,
                                                      const VkAllocationCallbacks *pAllocator,
                                                      VkVideoSessionKHR *pVideoSession) const {
    bool skip = false;

    skip |= ValidateVideoProfileInfo(pCreateInfo->pVideoProfile, device, "vkCreateVideoSessionKHR", "pCreateInfo->pVideoProfile");

    VideoProfileDesc profile_desc(this, pCreateInfo->pVideoProfile);
    const auto &profile_caps = profile_desc.GetCapabilities();

    if (profile_caps.supported) {
        if (pCreateInfo->flags & VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR) {
            const char *error_msg = nullptr;
            if (enabled_features.core11.protectedMemory == VK_FALSE) {
                error_msg = "the protectedMemory feature is not enabled";
            } else if ((profile_caps.base.flags & VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR) == 0) {
                error_msg = "protected content is not supported for the video profile";
            }
            if (error_msg != nullptr) {
                skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-protectedMemory-07189",
                                 "vkCreateVideoSessionKHR(): VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR "
                                 "was specified but %s",
                                 error_msg);
            }
        }

        if (!IsBetweenInclusive(pCreateInfo->maxCodedExtent, profile_caps.base.minCodedExtent, profile_caps.base.maxCodedExtent)) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-maxCodedExtent-04851",
                             "vkCreateVideoSessionKHR(): pCreateInfo->maxCodedExtent (%u,%u) is outside of the "
                             "range (%u,%u)-(%u,%u) supported by the video profile",
                             pCreateInfo->maxCodedExtent.width, pCreateInfo->maxCodedExtent.height,
                             profile_caps.base.minCodedExtent.width, profile_caps.base.minCodedExtent.height,
                             profile_caps.base.maxCodedExtent.width, profile_caps.base.maxCodedExtent.height);
        }

        if (pCreateInfo->maxDpbSlots > profile_caps.base.maxDpbSlots) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-maxDpbSlots-04847",
                             "vkCreateVideoSessionKHR(): pCreateInfo->maxDpbSlots (%u) is greater than the "
                             "maxDpbSlots (%u) supported by the video profile",
                             pCreateInfo->maxDpbSlots, profile_caps.base.maxDpbSlots);
        }

        if (pCreateInfo->maxActiveReferencePictures > profile_caps.base.maxActiveReferencePictures) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-maxActiveReferencePictures-04849",
                             "vkCreateVideoSessionKHR(): pCreateInfo->maxActiveReferencePictures (%u) is greater "
                             "than the maxActiveReferencePictures (%u) supported by the video profile",
                             pCreateInfo->maxActiveReferencePictures, profile_caps.base.maxActiveReferencePictures);
        }

        if ((pCreateInfo->maxDpbSlots == 0 && pCreateInfo->maxActiveReferencePictures != 0) ||
            (pCreateInfo->maxDpbSlots != 0 && pCreateInfo->maxActiveReferencePictures == 0)) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-maxDpbSlots-04850",
                             "vkCreateVideoSessionKHR(): if either pCreateInfo->maxDpbSlots (%u) or "
                             "pCreateInfo->maxActiveReferencePictures (%u) is zero then both must be zero",
                             pCreateInfo->maxDpbSlots, pCreateInfo->maxActiveReferencePictures);
        }

        if (profile_desc.GetProfile().is_decode && pCreateInfo->maxActiveReferencePictures > 0 &&
            !IsVideoFormatSupported(pCreateInfo->referencePictureFormat, VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR,
                                    pCreateInfo->pVideoProfile)) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-referencePictureFormat-04852",
                             "vkCreateVideoSessionKHR(): pCreateInfo->referencePictureFormat (%s) is not a supported "
                             "decode DPB format for the video profile specified in pCreateInfo->pVideoProfile",
                             string_VkFormat(pCreateInfo->referencePictureFormat));
        }

        if (profile_desc.GetProfile().is_decode &&
            !IsVideoFormatSupported(pCreateInfo->pictureFormat, VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR,
                                    pCreateInfo->pVideoProfile)) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-pictureFormat-04853",
                             "vkCreateVideoSessionKHR(): pCreateInfo->pictureFormat (%s) is not a supported "
                             "decode output format for the video profile specified in pCreateInfo->pVideoProfile",
                             string_VkFormat(pCreateInfo->pictureFormat));
        }

        if (strncmp(pCreateInfo->pStdHeaderVersion->extensionName, profile_caps.base.stdHeaderVersion.extensionName,
                    VK_MAX_EXTENSION_NAME_SIZE)) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-pStdHeaderVersion-07190",
                             "vkCreateVideoSessionKHR(): unsupported Video Std header name '%.*s' specified in "
                             "pCreateInfo->pStdHeaderVersion->extensionName, expected '%.*s'",
                             VK_MAX_EXTENSION_NAME_SIZE, pCreateInfo->pStdHeaderVersion->extensionName, VK_MAX_EXTENSION_NAME_SIZE,
                             profile_caps.base.stdHeaderVersion.extensionName);
        }

        if (pCreateInfo->pStdHeaderVersion->specVersion > profile_caps.base.stdHeaderVersion.specVersion) {
            skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-pStdHeaderVersion-07191",
                             "vkCreateVideoSessionKHR(): Video Std header version (0x%08x) specified in "
                             "pCreateInfo->pStdHeaderVersion->specVersion is larger than the supported version (0x%08x)",
                             pCreateInfo->pStdHeaderVersion->specVersion, profile_caps.base.stdHeaderVersion.specVersion);
        }
    } else {
        skip |= LogError(device, "VUID-VkVideoSessionCreateInfoKHR-pVideoProfile-04845",
                         "vkCreateVideoSessionKHR(): the video profile specified in pCreateInfo->pVideoProfile "
                         "is not supported");
    }

    return skip;
}

bool CoreChecks::PreCallValidateDestroyVideoSessionKHR(VkDevice device, VkVideoSessionKHR videoSession,
                                                       const VkAllocationCallbacks *pAllocator) const {
    auto video_session_state = Get<VIDEO_SESSION_STATE>(videoSession);
    bool skip = false;
    if (video_session_state) {
        skip |= ValidateObjectNotInUse(video_session_state.get(), "vkDestroyVideoSessionKHR",
                                       "VUID-vkDestroyVideoSessionKHR-videoSession-07192");
    }
    return skip;
}

bool CoreChecks::PreCallValidateBindVideoSessionMemoryKHR(VkDevice device, VkVideoSessionKHR videoSession,
                                                          uint32_t bindSessionMemoryInfoCount,
                                                          const VkBindVideoSessionMemoryInfoKHR *pBindSessionMemoryInfos) const {
    bool skip = false;

    auto vs_state = Get<VIDEO_SESSION_STATE>(videoSession);
    assert(vs_state);

    if (pBindSessionMemoryInfos) {
        {
            layer_data::unordered_set<uint32_t> memory_bind_indices;
            for (uint32_t i = 0; i < bindSessionMemoryInfoCount; ++i) {
                uint32_t mem_bind_index = pBindSessionMemoryInfos[i].memoryBindIndex;
                if (memory_bind_indices.find(mem_bind_index) != memory_bind_indices.end()) {
                    skip |= LogError(videoSession, "VUID-vkBindVideoSessionMemoryKHR-memoryBindIndex-07196",
                                     "vkBindVideoSessionMemoryKHR(): memoryBindIndex values in pBindSessionMemoryInfos "
                                     "array are not unique");
                    break;
                }
                memory_bind_indices.emplace(mem_bind_index);
            }
        }

        for (uint32_t i = 0; i < bindSessionMemoryInfoCount; ++i) {
            const auto &bind_info = pBindSessionMemoryInfos[i];
            const auto &mem_binding_info = vs_state->GetMemoryBindingInfo(bind_info.memoryBindIndex);
            if (mem_binding_info != nullptr) {
                auto mem_state = Get<DEVICE_MEMORY_STATE>(bind_info.memory);
                if (mem_state) {
                    if (((1 << mem_state->alloc_info.memoryTypeIndex) & mem_binding_info->requirements.memoryTypeBits) == 0) {
                        LogObjectList objlist(videoSession);
                        objlist.add(mem_state->Handle());
                        skip |= LogError(objlist, "VUID-vkBindVideoSessionMemoryKHR-pBindSessionMemoryInfos-07198",
                                         "vkBindVideoSessionMemoryKHR(): memoryTypeBits (0x%X) for memory binding "
                                         "with index %u of %s are not compatible with the memory type index (%u) of "
                                         "%s specified in pBindSessionMemoryInfos[%u].memory",
                                         mem_binding_info->requirements.memoryTypeBits, bind_info.memoryBindIndex,
                                         report_data->FormatHandle(videoSession).c_str(), mem_state->alloc_info.memoryTypeIndex,
                                         report_data->FormatHandle(mem_state->Handle()).c_str(), i);
                    }

                    if (bind_info.memoryOffset >= mem_state->alloc_info.allocationSize) {
                        LogObjectList objlist(videoSession);
                        objlist.add(mem_state->Handle());
                        skip |= LogError(objlist, "VUID-VkBindVideoSessionMemoryInfoKHR-memoryOffset-07201",
                                         "vkBindVideoSessionMemoryKHR(): pBindSessionMemoryInfos[%u].memoryOffset (%" PRIuLEAST64
                                         ") must be less than the size (%" PRIuLEAST64 ") of %s",
                                         i, bind_info.memoryOffset, mem_state->alloc_info.allocationSize,
                                         report_data->FormatHandle(mem_state->Handle()).c_str());
                    } else if (bind_info.memoryOffset + bind_info.memorySize > mem_state->alloc_info.allocationSize) {
                        LogObjectList objlist(videoSession);
                        objlist.add(mem_state->Handle());
                        skip |= LogError(
                            objlist, "VUID-VkBindVideoSessionMemoryInfoKHR-memorySize-07202",
                            "vkBindVideoSessionMemoryKHR(): memoryOffset (%" PRIuLEAST64 ") + memory size (%" PRIuLEAST64
                            ") specified in pBindSessionMemoryInfos[%u] must be less than or equal to the size (%" PRIuLEAST64
                            ") of %s",
                            bind_info.memoryOffset, bind_info.memorySize, i, mem_state->alloc_info.allocationSize,
                            report_data->FormatHandle(mem_state->Handle()).c_str());
                    }
                }

                if (SafeModulo(bind_info.memoryOffset, mem_binding_info->requirements.alignment) != 0) {
                    skip |= LogError(videoSession, "VUID-vkBindVideoSessionMemoryKHR-pBindSessionMemoryInfos-07199",
                                     "vkBindVideoSessionMemoryKHR(): pBindSessionMemoryInfos[%u].memoryOffset is %" PRIuLEAST64
                                     " but must be an integer multiple of the alignment value %" PRIuLEAST64
                                     " for the memory binding index %u of %s",
                                     i, bind_info.memoryOffset, mem_binding_info->requirements.alignment, bind_info.memoryBindIndex,
                                     report_data->FormatHandle(videoSession).c_str());
                }

                if (bind_info.memorySize != mem_binding_info->requirements.size) {
                    skip |= LogError(videoSession, "VUID-vkBindVideoSessionMemoryKHR-pBindSessionMemoryInfos-07200",
                                     "vkBindVideoSessionMemoryKHR(): pBindSessionMemoryInfos[%u].memorySize (%" PRIuLEAST64
                                     ") does not equal the required size (%" PRIuLEAST64 ") for the memory binding index %u of %s",
                                     i, bind_info.memorySize, mem_binding_info->requirements.size, bind_info.memoryBindIndex,
                                     report_data->FormatHandle(videoSession).c_str());
                }

                if (mem_binding_info->bound) {
                    skip |= LogError(videoSession, "VUID-vkBindVideoSessionMemoryKHR-videoSession-07195",
                                     "vkBindVideoSessionMemoryKHR(): memory binding with index %u of %s is already "
                                     "bound but was specified in pBindSessionMemoryInfos[%u].memoryBindIndex",
                                     bind_info.memoryBindIndex, report_data->FormatHandle(videoSession).c_str(), i);
                }
            } else {
                skip |= LogError(videoSession, "VUID-vkBindVideoSessionMemoryKHR-pBindSessionMemoryInfos-07197",
                                 "vkBindVideoSessionMemoryKHR(): %s does not have a memory binding corresponding "
                                 "to the memoryBindIndex specified in pBindSessionMemoryInfos[%u]",
                                 report_data->FormatHandle(videoSession).c_str(), i);
            }
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateCreateVideoSessionParametersKHR(VkDevice device,
                                                                const VkVideoSessionParametersCreateInfoKHR *pCreateInfo,
                                                                const VkAllocationCallbacks *pAllocator,
                                                                VkVideoSessionParametersKHR *pVideoSessionParameters) const {
    bool skip = false;

    std::shared_ptr<const VIDEO_SESSION_PARAMETERS_STATE> template_state;
    if (pCreateInfo->videoSessionParametersTemplate != VK_NULL_HANDLE) {
        template_state = Get<VIDEO_SESSION_PARAMETERS_STATE>(pCreateInfo->videoSessionParametersTemplate);
        if (template_state->vs_state->videoSession() != pCreateInfo->videoSession) {
            LogObjectList objlist(device);
            objlist.add(pCreateInfo->videoSessionParametersTemplate);
            objlist.add(pCreateInfo->videoSession);
            skip |= LogError(objlist, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSessionParametersTemplate-04855",
                             "vkCreateVideoSessionParametersKHR(): template %s was not created against the same %s",
                             report_data->FormatHandle(pCreateInfo->videoSessionParametersTemplate).c_str(),
                             report_data->FormatHandle(pCreateInfo->videoSession).c_str());
        }
    }

    auto vs_state = Get<VIDEO_SESSION_STATE>(pCreateInfo->videoSession);
    assert(vs_state);

    const char *pnext_chain_msg = "vkCreateVideoSessionParametersKHR(): missing %s from pCreateInfo pNext chain";
    switch (vs_state->GetCodecOp()) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
            auto codec_info = LvlFindInChain<VkVideoDecodeH264SessionParametersCreateInfoKHR>(pCreateInfo->pNext);
            if (codec_info) {
                skip |= ValidateDecodeH264ParametersAddInfo(
                    codec_info->pParametersAddInfo, device, "vkCreateVideoSessionParametersKHR",
                    "VkVideoDecodeH264SessionParametersCreateInfoKHR::pParametersAddInfo", codec_info, template_state.get());
            } else {
                skip |= LogError(device, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSession-07203", pnext_chain_msg,
                                 "VkVideoDecodeH264SessionParametersCreateInfoKHR");
            }
            break;
        }

        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
            auto codec_info = LvlFindInChain<VkVideoDecodeH265SessionParametersCreateInfoKHR>(pCreateInfo->pNext);
            if (codec_info) {
                skip |= ValidateDecodeH265ParametersAddInfo(
                    codec_info->pParametersAddInfo, device, "vkCreateVideoSessionParametersKHR",
                    "VkVideoDecodeH265SessionParametersCreateInfoKHR::pParametersAddInfo", codec_info, template_state.get());
            } else {
                skip |= LogError(device, "VUID-VkVideoSessionParametersCreateInfoKHR-videoSession-07206", pnext_chain_msg,
                                 "VkVideoDecodeH265SessionParametersCreateInfoKHR");
            }
            break;
        }

        default:
            break;
    }

    return skip;
}

bool CoreChecks::PreCallValidateUpdateVideoSessionParametersKHR(VkDevice device, VkVideoSessionParametersKHR videoSessionParameters,
                                                                const VkVideoSessionParametersUpdateInfoKHR *pUpdateInfo) const {
    bool skip = false;

    auto vsp_state = Get<VIDEO_SESSION_PARAMETERS_STATE>(videoSessionParameters);
    assert(vsp_state);

    auto vsp_data = vsp_state->Lock();

    if (pUpdateInfo->updateSequenceCount != vsp_data->update_sequence_counter + 1) {
        skip |= LogError(device, "VUID-vkUpdateVideoSessionParametersKHR-pUpdateInfo-07215",
                         "vkUpdateVideoSessionParametersKHR(): incorrect updateSequenceCount");
    }

    switch (vsp_state->GetCodecOp()) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
            auto add_info = LvlFindInChain<VkVideoDecodeH264SessionParametersAddInfoKHR>(pUpdateInfo->pNext);
            if (add_info) {
                skip |= ValidateDecodeH264ParametersAddInfo(add_info, device, "vkUpdateVideoSessionParametersKHR",
                                                            "VkVideoDecodeH264SessionParametersAddInfoKHR");

                for (uint32_t i = 0; i < add_info->stdSPSCount; ++i) {
                    if (vsp_data.GetH264SPS(add_info->pStdSPSs[i].seq_parameter_set_id)) {
                        skip |= LogError(
                            videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07216",
                            "vkUpdateVideoSessionParametersKHR(): H.264 SPS with key "
                            "(SPS ID = %u) already exists in %s",
                            add_info->pStdSPSs[i].seq_parameter_set_id, report_data->FormatHandle(videoSessionParameters).c_str());
                    }
                }

                if (add_info->stdSPSCount + vsp_data->h264.sps.size() > vsp_data->h264.spsCapacity) {
                    skip |= LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07217",
                                     "vkUpdateVideoSessionParametersKHR(): number of H.264 SPS entries to add "
                                     "(%u) plus the already used capacity (%zu) is greater than the maximum H.264 "
                                     "SPS capacity (%u) the %s was created with",
                                     add_info->stdSPSCount, vsp_data->h264.sps.size(), vsp_data->h264.spsCapacity,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                }

                for (uint32_t i = 0; i < add_info->stdPPSCount; ++i) {
                    if (vsp_data.GetH264PPS(add_info->pStdPPSs[i].seq_parameter_set_id,
                                            add_info->pStdPPSs[i].pic_parameter_set_id)) {
                        skip |=
                            LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07218",
                                     "vkUpdateVideoSessionParametersKHR(): H.264 PPS with key "
                                     "(SPS ID = %u, PPS ID = %u) already exists in %s",
                                     add_info->pStdPPSs[i].seq_parameter_set_id, add_info->pStdPPSs[i].pic_parameter_set_id,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                    }
                }

                if (add_info->stdPPSCount + vsp_data->h264.pps.size() > vsp_data->h264.ppsCapacity) {
                    skip |= LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07219",
                                     "vkUpdateVideoSessionParametersKHR(): number of H.264 PPS entries to add "
                                     "(%u) plus the already used capacity (%zu) is greater than the maximum H.264 "
                                     "PPS capacity (%u) the %s was created with",
                                     add_info->stdPPSCount, vsp_data->h264.pps.size(), vsp_data->h264.ppsCapacity,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                }
            }
            break;
        }

        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
            auto add_info = LvlFindInChain<VkVideoDecodeH265SessionParametersAddInfoKHR>(pUpdateInfo->pNext);
            if (add_info) {
                skip |= ValidateDecodeH265ParametersAddInfo(add_info, device, "vkUpdateVideoSessionParametersKHR",
                                                            "VkVideoDecodeH265SessionParametersAddInfoKHR");

                for (uint32_t i = 0; i < add_info->stdVPSCount; ++i) {
                    if (vsp_data.GetH265VPS(add_info->pStdVPSs[i].vps_video_parameter_set_id)) {
                        skip |=
                            LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07220",
                                     "vkUpdateVideoSessionParametersKHR(): H.265 VPS with key "
                                     "(VPS ID = %u) already exists in %s",
                                     add_info->pStdVPSs[i].vps_video_parameter_set_id,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                    }
                }

                if (add_info->stdVPSCount + vsp_data->h265.vps.size() > vsp_data->h265.vpsCapacity) {
                    skip |= LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07221",
                                     "vkUpdateVideoSessionParametersKHR(): number of H.265 VPS entries to add "
                                     "(%u) plus the already used capacity (%zu) is greater than the maximum H.265 "
                                     "VPS capacity (%u) the %s was created with",
                                     add_info->stdVPSCount, vsp_data->h265.vps.size(), vsp_data->h265.vpsCapacity,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                }

                for (uint32_t i = 0; i < add_info->stdSPSCount; ++i) {
                    if (vsp_data.GetH265SPS(add_info->pStdSPSs[i].sps_video_parameter_set_id,
                                            add_info->pStdSPSs[i].sps_seq_parameter_set_id)) {
                        skip |= LogError(
                            videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07222",
                            "vkUpdateVideoSessionParametersKHR(): H.265 SPS with key "
                            "(VPS ID = %u, SPS ID = %u) already exists in %s",
                            add_info->pStdSPSs[i].sps_video_parameter_set_id, add_info->pStdSPSs[i].sps_seq_parameter_set_id,
                            report_data->FormatHandle(videoSessionParameters).c_str());
                    }
                }

                if (add_info->stdSPSCount + vsp_data->h265.sps.size() > vsp_data->h265.spsCapacity) {
                    skip |= LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07223",
                                     "vkUpdateVideoSessionParametersKHR(): number of H.265 SPS entries to add "
                                     "(%u) plus the already used capacity (%zu) is greater than the maximum H.265 "
                                     "SPS capacity (%u) the %s was created with",
                                     add_info->stdSPSCount, vsp_data->h265.sps.size(), vsp_data->h265.spsCapacity,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                }

                for (uint32_t i = 0; i < add_info->stdPPSCount; ++i) {
                    if (vsp_data.GetH265PPS(add_info->pStdPPSs[i].sps_video_parameter_set_id,
                                            add_info->pStdPPSs[i].pps_seq_parameter_set_id,
                                            add_info->pStdPPSs[i].pps_pic_parameter_set_id)) {
                        skip |=
                            LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07224",
                                     "vkUpdateVideoSessionParametersKHR(): H.265 PPS with key "
                                     "(VPS ID = %u, SPS ID = %u, PPS ID = %u) already exists in %s",
                                     add_info->pStdPPSs[i].sps_video_parameter_set_id,
                                     add_info->pStdPPSs[i].pps_seq_parameter_set_id, add_info->pStdPPSs[i].pps_pic_parameter_set_id,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                    }
                }

                if (add_info->stdPPSCount + vsp_data->h265.pps.size() > vsp_data->h265.ppsCapacity) {
                    skip |= LogError(videoSessionParameters, "VUID-vkUpdateVideoSessionParametersKHR-videoSessionParameters-07225",
                                     "vkUpdateVideoSessionParametersKHR(): number of H.265 PPS entries to add "
                                     "(%u) plus the already used capacity (%zu) is greater than the maximum H.265 "
                                     "PPS capacity (%u) the %s was created with",
                                     add_info->stdPPSCount, vsp_data->h265.pps.size(), vsp_data->h265.ppsCapacity,
                                     report_data->FormatHandle(videoSessionParameters).c_str());
                }
            }
            break;
        }

        default:
            break;
    }

    return skip;
}

bool CoreChecks::PreCallValidateDestroyVideoSessionParametersKHR(VkDevice device,
                                                                 VkVideoSessionParametersKHR videoSessionParameters,
                                                                 const VkAllocationCallbacks *pAllocator) const {
    auto video_session_parameters_state = Get<VIDEO_SESSION_PARAMETERS_STATE>(videoSessionParameters);
    bool skip = false;
    if (video_session_parameters_state) {
        skip |= ValidateObjectNotInUse(video_session_parameters_state.get(), "vkDestroyVideoSessionParametersKHR",
                                       "VUID-vkDestroyVideoSessionParametersKHR-videoSessionParameters-07212");
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdBeginVideoCodingKHR(VkCommandBuffer commandBuffer,
                                                       const VkVideoBeginCodingInfoKHR *pBeginInfo) const {
    bool skip = false;
    auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
    if (!cb_state) return false;

    if (cb_state->activeQueries.size() > 0) {
        skip |= LogError(commandBuffer, "VUID-vkCmdBeginVideoCodingKHR-None-07232",
                         "vkCmdBeginVideoCodingKHR(): %s has active queries", report_data->FormatHandle(commandBuffer).c_str());
    }

    auto vs_state = Get<VIDEO_SESSION_STATE>(pBeginInfo->videoSession);
    assert(vs_state);

    auto vsp_state = Get<VIDEO_SESSION_PARAMETERS_STATE>(pBeginInfo->videoSessionParameters);

    auto qf_ext_props = queue_family_ext_props[cb_state->command_pool->queueFamilyIndex];

    if ((qf_ext_props.video_props.videoCodecOperations & vs_state->GetCodecOp()) == 0) {
        LogObjectList objlist(commandBuffer);
        objlist.add(pBeginInfo->videoSession);
        objlist.add(cb_state->command_pool->Handle());
        skip |= LogError(objlist, "VUID-vkCmdBeginVideoCodingKHR-commandBuffer-07231",
                         "vkCmdBeginVideoCodingKHR(): %s does not support video codec operation %s "
                         "that %s specified in pBeginInfo->videoSession was created with",
                         report_data->FormatHandle(cb_state->command_pool->Handle()).c_str(),
                         string_VkVideoCodecOperationFlagBitsKHR(vs_state->GetCodecOp()),
                         report_data->FormatHandle(pBeginInfo->videoSession).c_str());
    }

    if (vs_state->GetUnboundMemoryBindingCount() > 0) {
        LogObjectList objlist(commandBuffer);
        objlist.add(pBeginInfo->videoSession);
        skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-videoSession-07237",
                         "vkCmdBeginVideoCodingKHR(): %s has %u unbound memory binding indices",
                         report_data->FormatHandle(pBeginInfo->videoSession).c_str(), vs_state->GetUnboundMemoryBindingCount());
    }

    // if driver supports protectedNoFault the operation is valid, just has undefined values
    if ((!phys_dev_props_core11.protectedNoFault) && (cb_state->unprotected == true) &&
        ((vs_state->create_info.flags & VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR) != 0)) {
        LogObjectList objlist(commandBuffer);
        objlist.add(pBeginInfo->videoSession);
        skip |=
            LogError(objlist, "VUID-vkCmdBeginVideoCodingKHR-commandBuffer-07233",
                     "vkCmdBeginVideoCodingKHR(): %s is unprotected while %s was created with "
                     "VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR",
                     report_data->FormatHandle(commandBuffer).c_str(), report_data->FormatHandle(pBeginInfo->videoSession).c_str());
    }

    // if driver supports protectedNoFault the operation is valid, just has undefined values
    if ((!phys_dev_props_core11.protectedNoFault) && (cb_state->unprotected == false) &&
        ((vs_state->create_info.flags & VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR) == 0)) {
        LogObjectList objlist(commandBuffer);
        objlist.add(pBeginInfo->videoSession);
        skip |=
            LogError(objlist, "VUID-vkCmdBeginVideoCodingKHR-commandBuffer-07234",
                     "vkCmdBeginVideoCodingKHR(): %s is protected while %s was created without "
                     "VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR",
                     report_data->FormatHandle(commandBuffer).c_str(), report_data->FormatHandle(pBeginInfo->videoSession).c_str());
    }

    if (pBeginInfo->pReferenceSlots) {
        VideoPictureResources unique_resources{};
        bool resources_unique = true;
        bool has_separate_images = false;
        const IMAGE_STATE *last_dpb_image = nullptr;
        char where[64];

        for (uint32_t i = 0; i < pBeginInfo->referenceSlotCount; ++i) {
            const auto &slot = pBeginInfo->pReferenceSlots[i];

            if (slot.slotIndex >= 0 && (uint32_t)slot.slotIndex >= vs_state->create_info.maxDpbSlots) {
                LogObjectList objlist(commandBuffer);
                objlist.add(pBeginInfo->videoSession);
                skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-slotIndex-04856",
                                 "vkCmdBeginVideoCodingKHR(): pBeginInfo->pReferenceSlots[%u].slotIndex (%d) "
                                 "is greater than the maxDpbSlots %s was created with",
                                 i, slot.slotIndex, report_data->FormatHandle(pBeginInfo->videoSession).c_str());
            }

            if (slot.pPictureResource != nullptr) {
                snprintf(where, sizeof(where), " Image referenced in pBeginInfo->pReferenceSlots[%u]", i);
                auto reference_resource = VideoPictureResource(this, *slot.pPictureResource);
                skip |= ValidateVideoPictureResource(reference_resource, commandBuffer, *vs_state, "vkCmdBeginVideoCodingKHR()",
                                                     where, "VUID-VkVideoBeginCodingInfoKHR-pPictureResource-07242",
                                                     "VUID-VkVideoBeginCodingInfoKHR-pPictureResource-07243");
                if (reference_resource) {
                    if (!unique_resources.emplace(reference_resource).second) {
                        resources_unique = false;
                    }

                    if (last_dpb_image != nullptr && last_dpb_image != reference_resource.image_state.get()) {
                        has_separate_images = true;
                    }

                    skip |= ValidateProtectedImage(*cb_state, *reference_resource.image_state, "vkCmdBeginVideoCodingKHR()",
                                                   "VUID-vkCmdBeginVideoCodingKHR-commandBuffer-07235", where);
                    skip |= ValidateUnprotectedImage(*cb_state, *reference_resource.image_state, "vkCmdBeginVideoCodingKHR()",
                                                     "VUID-vkCmdBeginVideoCodingKHR-commandBuffer-07236", where);

                    const auto &supported_profiles = reference_resource.image_state->supported_video_profiles;
                    if (supported_profiles.find(vs_state->profile) == supported_profiles.end()) {
                        LogObjectList objlist(commandBuffer);
                        objlist.add(pBeginInfo->videoSession);
                        objlist.add(reference_resource.image_view_state->image_view());
                        objlist.add(reference_resource.image_state->image());
                        skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-pPictureResource-07240",
                                         "vkCmdBeginVideoCodingKHR(): pBeginInfo->pReferenceSlots[%u]."
                                         "imageViewBinding (%s created from %s) is not compatible with the "
                                         "video profile %s was created with",
                                         i, report_data->FormatHandle(reference_resource.image_view_state->image_view()).c_str(),
                                         report_data->FormatHandle(reference_resource.image_state->image()).c_str(),
                                         report_data->FormatHandle(pBeginInfo->videoSession).c_str());
                    }

                    if (reference_resource.image_view_state->create_info.format != vs_state->create_info.referencePictureFormat) {
                        LogObjectList objlist(commandBuffer);
                        objlist.add(pBeginInfo->videoSession);
                        objlist.add(reference_resource.image_view_state->image_view());
                        objlist.add(reference_resource.image_state->image());
                        skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-pPictureResource-07241",
                                         "vkCmdBeginVideoCodingKHR(): pBeginInfo->pReferenceSlots[%u]."
                                         "imageViewBinding (%s created from %s) format (%s) does not match "
                                         "the referencePictureFormat (%s) %s was created with",
                                         i, report_data->FormatHandle(reference_resource.image_view_state->image_view()).c_str(),
                                         report_data->FormatHandle(reference_resource.image_state->image()).c_str(),
                                         string_VkFormat(reference_resource.image_view_state->create_info.format),
                                         string_VkFormat(vs_state->create_info.referencePictureFormat),
                                         report_data->FormatHandle(pBeginInfo->videoSession).c_str());
                    }

                    auto supported_usage = reference_resource.image_view_state->inherited_usage;
                    if ((supported_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR) == 0) {
                        LogObjectList objlist(commandBuffer);
                        objlist.add(pBeginInfo->videoSession);
                        objlist.add(reference_resource.image_view_state->image_view());
                        objlist.add(reference_resource.image_state->image());
                        skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-slotIndex-07245",
                                         "vkCmdBeginVideoCodingKHR(): pBeginInfo->pReferenceSlots[%u].imageViewBinding "
                                         "(%s created from %s) was not created with VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR "
                                         "thus it cannot be used as a reference picture with %s that was created with a "
                                         "decode operation",
                                         i, report_data->FormatHandle(reference_resource.image_view_state->image_view()).c_str(),
                                         report_data->FormatHandle(reference_resource.image_state->image()).c_str(),
                                         report_data->FormatHandle(pBeginInfo->videoSession).c_str());
                    }

                    last_dpb_image = reference_resource.image_state.get();
                }
            }
        }

        if (!resources_unique) {
            LogObjectList objlist(commandBuffer);
            objlist.add(pBeginInfo->videoSession);
            skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-pPictureResource-07238",
                             "vkCmdBeginVideoCodingKHR(): more than one element of pBeginInfo->pReferenceSlots "
                             "refers to the same video picture resource");
        }

        auto supported_cap_flags = vs_state->profile->GetCapabilities().base.flags;
        if ((supported_cap_flags & VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR) == 0 && has_separate_images) {
            LogObjectList objlist(commandBuffer);
            objlist.add(pBeginInfo->videoSession);
            skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-flags-07244",
                             "vkCmdBeginVideoCodingKHR(): not all elements of pBeginInfo->pReferenceSlots refer "
                             "to the same image and the video profile %s was created with does not support "
                             "VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR",
                             report_data->FormatHandle(pBeginInfo->videoSession).c_str());
        }
    }

    if (vsp_state && vsp_state->vs_state->videoSession() != vs_state->videoSession()) {
        LogObjectList objlist(commandBuffer);
        objlist.add(pBeginInfo->videoSessionParameters);
        objlist.add(pBeginInfo->videoSession);
        skip |= LogError(objlist, "VUID-VkVideoBeginCodingInfoKHR-videoSessionParameters-04857",
                         "vkCmdBeginVideoCodingKHR(): %s was not created for %s",
                         report_data->FormatHandle(pBeginInfo->videoSessionParameters).c_str(),
                         report_data->FormatHandle(pBeginInfo->videoSession).c_str());
    }

    const char *codec_op_requires_params_vuid = nullptr;
    switch (vs_state->GetCodecOp()) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
            codec_op_requires_params_vuid = "VUID-VkVideoBeginCodingInfoKHR-videoSession-07247";
            break;

        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
            codec_op_requires_params_vuid = "VUID-VkVideoBeginCodingInfoKHR-videoSession-07248";
            break;

        default:
            break;
    }

    if ((codec_op_requires_params_vuid != nullptr) && (vsp_state == nullptr)) {
        LogObjectList objlist(commandBuffer);
        objlist.add(pBeginInfo->videoSession);
        skip |= LogError(objlist, codec_op_requires_params_vuid,
                         "vkCmdBeginVideoCodingKHR(): %s was created with %s but no video session parameters object was "
                         "specified in pBeginInfo->videoSessionParameters",
                         report_data->FormatHandle(pBeginInfo->videoSession).c_str(),
                         string_VkVideoCodecOperationFlagBitsKHR(vs_state->GetCodecOp()));
    }

    skip |= ValidateCmd(*cb_state, CMD_BEGINVIDEOCODINGKHR);
    return skip;
}

bool CoreChecks::PreCallValidateCmdEndVideoCodingKHR(VkCommandBuffer commandBuffer,
                                                     const VkVideoEndCodingInfoKHR *pEndCodingInfo) const {
    bool skip = false;
    auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
    if (!cb_state) return false;

    if (cb_state->activeQueries.size() > 0) {
        skip |= LogError(commandBuffer, "VUID-vkCmdEndVideoCodingKHR-None-07251", "vkCmdEndVideoCodingKHR(): %s has active queries",
                         report_data->FormatHandle(commandBuffer).c_str());
    }

    skip |= ValidateCmd(*cb_state, CMD_ENDVIDEOCODINGKHR);
    return skip;
}

bool CoreChecks::PreCallValidateCmdControlVideoCodingKHR(VkCommandBuffer commandBuffer,
                                                         const VkVideoCodingControlInfoKHR *pCodingControlInfo) const {
    bool skip = false;
    auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
    if (!cb_state) return false;

    skip |= ValidateCmd(*cb_state, CMD_CONTROLVIDEOCODINGKHR);
    return skip;
}

bool CoreChecks::PreCallValidateCmdDecodeVideoKHR(VkCommandBuffer commandBuffer, const VkVideoDecodeInfoKHR *pDecodeInfo) const {
    bool skip = false;
    auto cb_state = GetRead<CMD_BUFFER_STATE>(commandBuffer);
    if (!cb_state) return false;

    const auto vs_state = cb_state->bound_video_session.get();
    if (!vs_state) return false;

    const auto &bound_resources = cb_state->bound_video_picture_resources;

    bool hit_error = false;

    const auto &profile_caps = vs_state->profile->GetCapabilities();

    auto buffer_state = Get<BUFFER_STATE>(pDecodeInfo->srcBuffer);
    if (buffer_state) {
        const char *where = " Buffer referenced in pDecodeInfo->srcBuffer";
        skip |= ValidateProtectedBuffer(*cb_state, *buffer_state, "vkCmdDecodeVideoKHR()",
                                        "VUID-vkCmdDecodeVideoKHR-commandBuffer-07136", where);
        skip |= ValidateUnprotectedBuffer(*cb_state, *buffer_state, "vkCmdDecodeVideoKHR()",
                                          "VUID-vkCmdDecodeVideoKHR-commandBuffer-07137", where);
    }

    if ((buffer_state->createInfo.usage & VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR) == 0) {
        LogObjectList objlist(commandBuffer);
        objlist.add(vs_state->videoSession());
        objlist.add(pDecodeInfo->srcBuffer);
        skip |= LogError(objlist, "VUID-VkVideoDecodeInfoKHR-srcBuffer-07165",
                         "vkCmdDecodeVideoKHR(): pDecodeInfo->srcBuffer (%s) was not created with "
                         "VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR",
                         report_data->FormatHandle(pDecodeInfo->srcBuffer).c_str());
    }

    const auto &src_supported_profiles = buffer_state->supported_video_profiles;
    if (src_supported_profiles.find(vs_state->profile) == src_supported_profiles.end()) {
        LogObjectList objlist(commandBuffer);
        objlist.add(vs_state->videoSession());
        objlist.add(pDecodeInfo->srcBuffer);
        skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07135",
                         "vkCmdDecodeVideoKHR(): pDecodeInfo->srcBuffer (%s) is not compatible "
                         "with the video profile %s was created with",
                         report_data->FormatHandle(pDecodeInfo->srcBuffer).c_str(),
                         report_data->FormatHandle(vs_state->videoSession()).c_str());
    }

    if (pDecodeInfo->srcBufferOffset >= buffer_state->createInfo.size) {
        LogObjectList objlist(commandBuffer);
        objlist.add(vs_state->videoSession());
        objlist.add(pDecodeInfo->srcBuffer);
        skip |= LogError(objlist, "VUID-VkVideoDecodeInfoKHR-srcBufferOffset-07166",
                         "vkCmdDecodeVideoKHR(): pDecodeInfo->srcBufferOffset (%" PRIu64 ") must be less than the size (%" PRIu64
                         ") of pDecodeInfo->srcBuffer (%s)",
                         pDecodeInfo->srcBufferOffset, buffer_state->createInfo.size,
                         report_data->FormatHandle(pDecodeInfo->srcBuffer).c_str());
    }

    if (!IsIntegerMultipleOf(pDecodeInfo->srcBufferOffset, profile_caps.base.minBitstreamBufferOffsetAlignment)) {
        LogObjectList objlist(commandBuffer);
        objlist.add(vs_state->videoSession());
        skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07138",
                         "vkCmdDecodeVideoKHR(): pDecodeInfo->srcBufferOffset (%" PRIu64
                         ") is not an integer multiple of the minBitstreamBufferOffsetAlignment (%" PRIu64
                         ") required by the video profile %s was created with",
                         pDecodeInfo->srcBufferOffset, profile_caps.base.minBitstreamBufferOffsetAlignment,
                         report_data->FormatHandle(vs_state->videoSession()).c_str());
    }

    if (pDecodeInfo->srcBufferOffset + pDecodeInfo->srcBufferRange > buffer_state->createInfo.size) {
        LogObjectList objlist(commandBuffer);
        objlist.add(vs_state->videoSession());
        objlist.add(pDecodeInfo->srcBuffer);
        skip |=
            LogError(objlist, "VUID-VkVideoDecodeInfoKHR-srcBufferRange-07167",
                     "vkCmdDecodeVideoKHR(): pDecodeInfo->srcBufferOffset (%" PRIu64 ") plus pDecodeInfo->srcBufferRange (%" PRIu64
                     ") must be less than or equal to the size (%" PRIu64 ") of pDecodeInfo->srcBuffer (%s)",
                     pDecodeInfo->srcBufferOffset, pDecodeInfo->srcBufferRange, buffer_state->createInfo.size,
                     report_data->FormatHandle(pDecodeInfo->srcBuffer).c_str());
    }

    if (!IsIntegerMultipleOf(pDecodeInfo->srcBufferRange, profile_caps.base.minBitstreamBufferSizeAlignment)) {
        LogObjectList objlist(commandBuffer);
        objlist.add(vs_state->videoSession());
        skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07139",
                         "vkCmdDecodeVideoKHR(): pDecodeInfo->srcBufferRange (%" PRIu64
                         ") is not an integer multiple of the minBitstreamBufferSizeAlignment (%" PRIu64
                         ") required by the video profile %s was created with",
                         pDecodeInfo->srcBufferRange, profile_caps.base.minBitstreamBufferSizeAlignment,
                         report_data->FormatHandle(vs_state->videoSession()).c_str());
    }

    VideoPictureResource setup_resource;
    if (pDecodeInfo->pSetupReferenceSlot) {
        if (pDecodeInfo->pSetupReferenceSlot->slotIndex < 0) {
            skip |= LogError(commandBuffer, "VUID-VkVideoDecodeInfoKHR-pSetupReferenceSlot-07168",
                             "vkCmdDecodeVideoKHR(): pBeginInfo->pSetupReferenceSlot->slotIndex (%d) "
                             "must not be negative",
                             pDecodeInfo->pSetupReferenceSlot->slotIndex);
        } else if ((uint32_t)pDecodeInfo->pSetupReferenceSlot->slotIndex >= vs_state->create_info.maxDpbSlots) {
            skip |= LogError(commandBuffer, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07170",
                             "vkCmdDecodeVideoKHR(): pBeginInfo->pSetupReferenceSlot->slotIndex (%d) "
                             "must be smaller than the maxDpbSlots (%u) the bound video session %s "
                             "was created with",
                             pDecodeInfo->pSetupReferenceSlot->slotIndex, vs_state->create_info.maxDpbSlots,
                             report_data->FormatHandle(vs_state->videoSession()).c_str());
        }

        if (pDecodeInfo->pSetupReferenceSlot->pPictureResource != nullptr) {
            setup_resource = VideoPictureResource(this, *pDecodeInfo->pSetupReferenceSlot->pPictureResource);
            if (setup_resource) {
                skip |= ValidateVideoPictureResource(setup_resource, commandBuffer, *vs_state, "vkCmdDecodeVideoKHR()",
                                                     " Image referenced in pDecodeInfo->pSetupReferenceSlot",
                                                     "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07173");

                if (bound_resources.find(setup_resource) == bound_resources.end()) {
                    skip |= LogError(commandBuffer, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07149",
                                     "vkCmdDecodeVideoKHR(): the video picture resource specified in "
                                     "pDecodeInfo->pSetupReferenceSlot->pPictureResource is not one of the "
                                     "bound video picture resources");
                }

                skip |= VerifyImageLayout(*cb_state, *setup_resource.image_state, setup_resource.range,
                                          VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, "vkCmdDecodeVideoKHR()",
                                          "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07254", &hit_error);
            }
        } else {
            skip |= LogError(commandBuffer, "VUID-VkVideoDecodeInfoKHR-pSetupReferenceSlot-07169",
                             "vkCmdDecodeVideoKHR(): pBeginInfo->pSetupReferenceSlot->pPictureResource "
                             "must not be NULL");
        }
    }

    auto dst_resource = VideoPictureResource(this, pDecodeInfo->dstPictureResource);
    skip |= ValidateVideoPictureResource(
        dst_resource, commandBuffer, *vs_state, "vkCmdDecodeVideoKHR()", " Image referenced in pDecodeInfo->dstPictureResource",
        "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07144", "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07145");
    if (dst_resource) {
        const char *where = " Image referenced in pDecodeInfo->dstPictureResource";
        skip |= ValidateProtectedImage(*cb_state, *dst_resource.image_state, "vkCmdDecodeVideoKHR()",
                                       "VUID-vkCmdDecodeVideoKHR-commandBuffer-07147", where);
        skip |= ValidateUnprotectedImage(*cb_state, *dst_resource.image_state, "vkCmdDecodeVideoKHR()",
                                         "VUID-vkCmdDecodeVideoKHR-commandBuffer-07148", where);

        const auto &dst_supported_profiles = dst_resource.image_state->supported_video_profiles;
        if (dst_supported_profiles.find(vs_state->profile) == dst_supported_profiles.end()) {
            LogObjectList objlist(commandBuffer);
            objlist.add(vs_state->videoSession());
            objlist.add(dst_resource.image_view_state->image_view());
            objlist.add(dst_resource.image_state->image());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07142",
                             "vkCmdDecodeVideoKHR(): pDecodeInfo->dstPictureResource.imageViewBinding "
                             "(%s created from %s) is not compatible with the video profile the bound "
                             "video session %s was created with",
                             report_data->FormatHandle(pDecodeInfo->dstPictureResource.imageViewBinding).c_str(),
                             report_data->FormatHandle(dst_resource.image_state->image()).c_str(),
                             report_data->FormatHandle(vs_state->videoSession()).c_str());
        }

        if (dst_resource.image_view_state->create_info.format != vs_state->create_info.pictureFormat) {
            LogObjectList objlist(commandBuffer);
            objlist.add(vs_state->videoSession());
            objlist.add(dst_resource.image_view_state->image_view());
            objlist.add(dst_resource.image_state->image());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07143",
                             "vkCmdDecodeVideoKHR(): pDecodeInfo->dstPictureResource.imageViewBinding "
                             "(%s created from %s) format (%s) does not match the pictureFormat (%s) "
                             "the bound video session %s was created with",
                             report_data->FormatHandle(dst_resource.image_view_state->image_view()).c_str(),
                             report_data->FormatHandle(dst_resource.image_state->image()).c_str(),
                             string_VkFormat(dst_resource.image_view_state->create_info.format),
                             string_VkFormat(vs_state->create_info.pictureFormat),
                             report_data->FormatHandle(vs_state->videoSession()).c_str());
        }

        auto supported_usage = dst_resource.image_view_state->inherited_usage;
        if ((supported_usage & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) == 0) {
            LogObjectList objlist(commandBuffer);
            objlist.add(vs_state->videoSession());
            objlist.add(dst_resource.image_view_state->image_view());
            objlist.add(dst_resource.image_state->image());
            skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07146",
                             "vkCmdDecodeVideoKHR(): pDecodeInfo->dstPictureResource.imageViewBinding "
                             "(%s created from %s) was not created with VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR "
                             "thus it cannot be used as a decode output picture with the bound video session %s "
                             "that was created with a decode operation",
                             report_data->FormatHandle(dst_resource.image_view_state->image_view()).c_str(),
                             report_data->FormatHandle(dst_resource.image_state->image()).c_str(),
                             report_data->FormatHandle(vs_state->videoSession()).c_str());
        }

        bool dst_same_as_setup = (setup_resource == dst_resource);

        VkImageLayout expected_layout =
            dst_same_as_setup ? VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR : VK_IMAGE_LAYOUT_VIDEO_DECODE_DST_KHR;

        const char *vuid =
            dst_same_as_setup ? "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07253" : "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07252";

        skip |= VerifyImageLayout(*cb_state, *dst_resource.image_state, dst_resource.range, expected_layout,
                                  "vkCmdDecodeVideoKHR()", vuid, &hit_error);

        if (setup_resource) {
            if ((profile_caps.decode.flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR) == 0 &&
                dst_same_as_setup) {
                LogObjectList objlist(commandBuffer);
                objlist.add(vs_state->videoSession());
                skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07140",
                                 "vkCmdDecodeVideoKHR(): the video profile %s was created with does not support "
                                 "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR but "
                                 "pDecodeInfo->dstPictureResource and "
                                 "pDecodeInfo->pSetupReferenceSlot->pPictureResource match",
                                 report_data->FormatHandle(vs_state->videoSession()).c_str());
            }

            if ((profile_caps.decode.flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR) == 0 &&
                !dst_same_as_setup) {
                LogObjectList objlist(commandBuffer);
                objlist.add(vs_state->videoSession());
                skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07141",
                                 "vkCmdDecodeVideoKHR(): the video profile %s was created with does not support "
                                 "VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR but "
                                 "pDecodeInfo->dstPictureResource and pSetupReferenceSlot->pPictureResource "
                                 "do not match",
                                 report_data->FormatHandle(vs_state->videoSession()).c_str());
            }
        }
    }

    if (pDecodeInfo->pReferenceSlots) {
        VideoPictureResources unique_resources{};
        bool resources_unique = true;
        char where[64];

        skip |= ValidateActiveReferencePictureCount(*cb_state, *pDecodeInfo);
        skip |= ValidateReferencePictureUseCount(*cb_state, *pDecodeInfo);

        for (uint32_t i = 0; i < pDecodeInfo->referenceSlotCount; ++i) {
            if (pDecodeInfo->pReferenceSlots[i].slotIndex < 0) {
                skip |= LogError(commandBuffer, "VUID-VkVideoDecodeInfoKHR-slotIndex-07171",
                                 "vkCmdDecodeVideoKHR(): pDecodeInfo->pReferenceSlots[%u].slotIndex (%d) "
                                 "must not be negative",
                                 i, pDecodeInfo->pReferenceSlots[i].slotIndex);
            } else if ((uint32_t)pDecodeInfo->pReferenceSlots[i].slotIndex >= vs_state->create_info.maxDpbSlots) {
                LogObjectList objlist(commandBuffer);
                objlist.add(vs_state->videoSession());
                skip |= LogError(objlist, "VUID-vkCmdDecodeVideoKHR-slotIndex-07256",
                                 "vkCmdDecodeVideoKHR(): pDecodeInfo->pReferenceSlots[%u].slotIndex (%d) "
                                 "must be smaller than the maxDpbSlots (%u) the bound video session %s "
                                 "was created with",
                                 i, pDecodeInfo->pReferenceSlots[i].slotIndex, vs_state->create_info.maxDpbSlots,
                                 report_data->FormatHandle(vs_state->videoSession()).c_str());
            }

            if (pDecodeInfo->pReferenceSlots[i].pPictureResource != nullptr) {
                auto reference_resource = VideoPictureResource(this, *pDecodeInfo->pReferenceSlots[i].pPictureResource);
                if (reference_resource) {
                    if (!unique_resources.emplace(reference_resource).second) {
                        resources_unique = false;
                    }

                    const auto &it = bound_resources.find(reference_resource);
                    if (it == bound_resources.end()) {
                        skip |= LogError(commandBuffer, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07151",
                                         "vkCmdDecodeVideoKHR(): the video picture resource specified in "
                                         "pDecodeInfo->pReferenceSlots[%u].pPictureResource is not one of the "
                                         "bound video picture resources",
                                         i);
                    } else if (pDecodeInfo->pReferenceSlots[i].slotIndex >= 0 &&
                               pDecodeInfo->pReferenceSlots[i].slotIndex != it->second) {
                        skip |= LogError(commandBuffer, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07151",
                                         "vkCmdDecodeVideoKHR(): the bound video picture resource specified in "
                                         "pDecodeInfo->pReferenceSlots[%u].pPictureResource is not currently "
                                         "associated with the DPB slot index specifed in "
                                         "pDecodeInfo->pReferenceSlots[%u].slotIndex (%d)",
                                         i, i, pDecodeInfo->pReferenceSlots[i].slotIndex);
                    }

                    snprintf(where, sizeof(where), " Image referenced in pDecodeInfo->pReferenceSlots[%u]", i);
                    skip |= ValidateVideoPictureResource(reference_resource, commandBuffer, *vs_state, "vkCmdDecodeVideoKHR()",
                                                         where, "VUID-vkCmdDecodeVideoKHR-codedOffset-07257");

                    skip |= VerifyImageLayout(*cb_state, *reference_resource.image_state, reference_resource.range,
                                              VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR, "vkCmdDecodeVideoKHR()",
                                              "VUID-vkCmdDecodeVideoKHR-pPictureResource-07255", &hit_error);
                }
            } else {
                skip |= LogError(commandBuffer, "VUID-VkVideoDecodeInfoKHR-pPictureResource-07172",
                                 "vkCmdDecodeVideoKHR(): pDecodeInfo->pReferenceSlots[%u].pPictureResource "
                                 "must not be NULL",
                                 i);
            }
        }

        if (!resources_unique) {
            skip |= LogError(commandBuffer, "VUID-vkCmdDecodeVideoKHR-pDecodeInfo-07264",
                             "vkCmdDecodeVideoKHR(): more than one element of pDecodeInfo->pReferenceSlots "
                             "refers to the same video picture resource");
        }
    }

    for (const auto &query : cb_state->activeQueries) {
        uint32_t op_count = vs_state->GetVideoDecodeOperationCount(pDecodeInfo);
        if (query.active_query_index + op_count > query.last_activatable_query_index + 1) {
            auto query_pool_state = Get<QUERY_POOL_STATE>(query.pool);
            skip |= LogError(commandBuffer, "VUID-vkCmdDecodeVideoKHR-opCount-07134",
                             "vkCmdDecodeVideoKHR(): not enough activatable queries for query type %s "
                             "with opCount %u, active query index %u, and last activatable query index %u",
                             string_VkQueryType(query_pool_state->createInfo.queryType), op_count, query.active_query_index,
                             query.last_activatable_query_index);
        }
    }

    switch (vs_state->GetCodecOp()) {
        case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
            skip |= ValidateVideoDecodeInfoH264(*cb_state, *pDecodeInfo);
            break;

        case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
            skip |= ValidateVideoDecodeInfoH265(*cb_state, *pDecodeInfo);
            break;

        default:
            break;
    }

    skip |= ValidateCmd(*cb_state, CMD_DECODEVIDEOKHR);
    return skip;
}
