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

#include <algorithm>
#include <assert.h>
#include <sstream>
#include <string>

#include <vulkan/vk_enum_string_helper.h>
#include "generated/chassis.h"
#include "core_validation.h"
#include "cc_buffer_address.h"
#include "utils/ray_tracing_utils.h"

bool CoreChecks::ValidateInsertAccelerationStructureMemoryRange(VkAccelerationStructureNV as, const vvl::DeviceMemory *mem_info,
                                                                VkDeviceSize mem_offset, const Location &loc) const {
    return ValidateInsertMemoryRange(VulkanTypedHandle(as, kVulkanObjectTypeAccelerationStructureNV), mem_info, mem_offset, loc);
}

bool CoreChecks::PreCallValidateCreateAccelerationStructureNV(VkDevice device,
                                                              const VkAccelerationStructureCreateInfoNV *pCreateInfo,
                                                              const VkAllocationCallbacks *pAllocator,
                                                              VkAccelerationStructureNV *pAccelerationStructure,
                                                              const ErrorObject &error_obj) const {
    bool skip = false;
    if (pCreateInfo != nullptr && pCreateInfo->info.type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV) {
        for (uint32_t i = 0; i < pCreateInfo->info.geometryCount; i++) {
            skip |= ValidateGeometryNV(pCreateInfo->info.pGeometries[i],
                                       error_obj.location.dot(Field::pCreateInfo).dot(Field::info).dot(Field::pGeometries, i));
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateCreateAccelerationStructureKHR(VkDevice device,
                                                               const VkAccelerationStructureCreateInfoKHR *pCreateInfo,
                                                               const VkAllocationCallbacks *pAllocator,
                                                               VkAccelerationStructureKHR *pAccelerationStructure,
                                                               const ErrorObject &error_obj) const {
    bool skip = false;
    if (!pCreateInfo) {
        return false;
    }
    auto buffer_state = Get<vvl::Buffer>(pCreateInfo->buffer);
    if (!buffer_state) {
        return false;
    }

    if (!(buffer_state->usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR)) {
        skip |= LogError("VUID-VkAccelerationStructureCreateInfoKHR-buffer-03614", buffer_state->Handle(),
                         error_obj.location.dot(Field::pCreateInfo).dot(Field::buffer), "was created with %s.",
                         string_VkBufferUsageFlags2KHR(buffer_state->usage).c_str());
    }
    if (buffer_state->createInfo.flags & VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT) {
        skip |= LogError("VUID-VkAccelerationStructureCreateInfoKHR-buffer-03615", buffer_state->Handle(),
                         error_obj.location.dot(Field::pCreateInfo).dot(Field::buffer), "was created with %s.",
                         string_VkBufferCreateFlags(buffer_state->createInfo.flags).c_str());
    }
    if (pCreateInfo->offset + pCreateInfo->size > buffer_state->createInfo.size) {
        skip |= LogError("VUID-VkAccelerationStructureCreateInfoKHR-offset-03616", buffer_state->Handle(),
                         error_obj.location.dot(Field::pCreateInfo).dot(Field::offset),
                         "(%" PRIu64 ") plus size (%" PRIu64 ") must be less than the size of buffer (%" PRIu64 ").",
                         pCreateInfo->offset, pCreateInfo->size, buffer_state->createInfo.size);
    }
    return skip;
}

bool CoreChecks::PreCallValidateBindAccelerationStructureMemoryNV(VkDevice device, uint32_t bindInfoCount,
                                                                  const VkBindAccelerationStructureMemoryInfoNV *pBindInfos,
                                                                  const ErrorObject &error_obj) const {
    bool skip = false;
    for (uint32_t i = 0; i < bindInfoCount; i++) {
        const Location bind_info_loc = error_obj.location.dot(Field::pBindInfos, i);
        const VkBindAccelerationStructureMemoryInfoNV &info = pBindInfos[i];
        auto as_state = Get<vvl::AccelerationStructureNV>(info.accelerationStructure);
        if (!as_state) {
            continue;
        }
        if (as_state->HasFullRangeBound()) {
            skip |= LogError("VUID-VkBindAccelerationStructureMemoryInfoNV-accelerationStructure-03620", info.accelerationStructure,
                             bind_info_loc.dot(Field::accelerationStructure), "must not already be backed by a memory object.");
        }

        // Validate bound memory range information
        auto mem_info = Get<vvl::DeviceMemory>(info.memory);
        if (mem_info) {
            skip |= ValidateInsertAccelerationStructureMemoryRange(info.accelerationStructure, mem_info.get(), info.memoryOffset,
                                                                   bind_info_loc.dot(Field::memoryOffset));
            skip |= ValidateMemoryTypes(mem_info.get(), as_state->memory_requirements.memoryTypeBits,
                                        bind_info_loc.dot(Field::accelerationStructure),
                                        "VUID-VkBindAccelerationStructureMemoryInfoNV-memory-03622");
        }

        // Validate memory requirements alignment
        if (SafeModulo(info.memoryOffset, as_state->memory_requirements.alignment) != 0) {
            skip |= LogError("VUID-VkBindAccelerationStructureMemoryInfoNV-memoryOffset-03623", info.accelerationStructure,
                             bind_info_loc.dot(Field::memoryOffset),
                             "(%" PRIu64 ") must be a multiple of the alignment (%" PRIu64
                             ") member of the VkMemoryRequirements structure returned from "
                             "a call to vkGetAccelerationStructureMemoryRequirementsNV with accelerationStructure %s and type of "
                             "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV",
                             info.memoryOffset, as_state->memory_requirements.alignment,
                             FormatHandle(info.accelerationStructure).c_str());
        }

        if (mem_info) {
            // Validate memory requirements size
            if (as_state->memory_requirements.size > (mem_info->alloc_info.allocationSize - info.memoryOffset)) {
                skip |= LogError("VUID-VkBindAccelerationStructureMemoryInfoNV-size-03624", info.accelerationStructure,
                                 bind_info_loc.dot(Field::memory),
                                 "'s size (%" PRIu64 ") minus %s (%" PRIu64 ") is %" PRIu64
                                 ", but the size member of the VkMemoryRequirements structure returned from a call to "
                                 "vkGetAccelerationStructureMemoryRequirementsNV with accelerationStructure %s and type of "
                                 "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV is %" PRIu64 ".",
                                 as_state->memory_requirements.size, bind_info_loc.dot(Field::memoryOffset).Fields().c_str(),
                                 info.memoryOffset, mem_info->alloc_info.allocationSize - info.memoryOffset,
                                 FormatHandle(info.accelerationStructure).c_str(), as_state->memory_requirements.size);
            }
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateGetAccelerationStructureHandleNV(VkDevice device, VkAccelerationStructureNV accelerationStructure,
                                                                 size_t dataSize, void *pData, const ErrorObject &error_obj) const {
    bool skip = false;

    auto as_state = Get<vvl::AccelerationStructureNV>(accelerationStructure);
    if (as_state != nullptr) {
        skip = VerifyBoundMemoryIsValid(as_state->MemState(), LogObjectList(accelerationStructure), as_state->Handle(),
                                        error_obj.location.dot(Field::accelerationStructure),
                                        "VUID-vkGetAccelerationStructureHandleNV-accelerationStructure-02787");
    }

    return skip;
}

bool CoreChecks::ValidateAccelerationStructuresMemoryAlisasing(VkCommandBuffer commandBuffer, uint32_t infoCount,
                                                               const VkAccelerationStructureBuildGeometryInfoKHR *pInfos,
                                                               uint32_t info_i, const ErrorObject &error_obj) const {
    using sparse_container::range;

    bool skip = false;
    const VkAccelerationStructureBuildGeometryInfoKHR &info = pInfos[info_i];
    const Location info_i_loc = error_obj.location.dot(Field::pInfos, info_i);
    const auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure);
    const auto dst_as_state = Get<vvl::AccelerationStructureKHR>(info.dstAccelerationStructure);

    auto validate_no_as_buffer_memory_overlap =
        [this, commandBuffer, error_obj](const vvl::AccelerationStructureKHR &accel_struct_a, const Location &location_a,
                                         const vvl::Buffer &buffer_a, const sparse_container::range<VkDeviceSize> &range_a,

                                         const vvl::AccelerationStructureKHR &accel_struct_b, const Location &location_b,
                                         const vvl::Buffer &buffer_b, const sparse_container::range<VkDeviceSize> &range_b,

                                         const char *vuid) {
            bool skip = false;

            if (const auto [memory, overlap_range] = buffer_a.GetResourceMemoryOverlap(range_a, &buffer_b, range_b);
                memory != VK_NULL_HANDLE) {
                const LogObjectList objlist(commandBuffer, accel_struct_a.Handle(), buffer_a.Handle(), accel_struct_b.Handle(),
                                            buffer_b.Handle());
                skip |= LogError(
                    vuid, objlist, error_obj.location,
                    "memory backing buffer (%s) used as storage for %s (%s) overlaps memory backing buffer (%s) used as "
                    "storage for %s (%s). Overlapped memory is (%s) on range %s.",
                    FormatHandle(buffer_a).c_str(), location_a.Fields().c_str(), FormatHandle(accel_struct_a.Handle()).c_str(),
                    FormatHandle(buffer_b).c_str(), location_b.Fields().c_str(), FormatHandle(accel_struct_b.Handle()).c_str(),
                    FormatHandle(memory).c_str(), string_range(overlap_range).c_str());
            }

            return skip;
        };

    if (info.srcAccelerationStructure != info.dstAccelerationStructure && src_as_state && dst_as_state) {
        const std::shared_ptr<vvl::Buffer> src_as_buffer = src_as_state->buffer_state;
        const std::shared_ptr<vvl::Buffer> dst_as_buffer = dst_as_state->buffer_state;

        if (src_as_buffer && dst_as_buffer) {
            const range<VkDeviceSize> src_as_range(src_as_state->create_infoKHR.offset, src_as_state->create_infoKHR.size);
            const range<VkDeviceSize> dst_as_range(dst_as_state->create_infoKHR.offset, dst_as_state->create_infoKHR.size);

            skip |= validate_no_as_buffer_memory_overlap(*src_as_state, info_i_loc.dot(Field::srcAccelerationStructure),
                                                         *src_as_buffer, src_as_range, *dst_as_state,
                                                         info_i_loc.dot(Field::dstAccelerationStructure), *dst_as_buffer,
                                                         dst_as_range, "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03668");
        }
    }

    for (auto [other_info_j, other_info] : vvl::enumerate(pInfos + info_i, infoCount - info_i)) {
        // Validate that scratch buffer's memory does not overlap destination acceleration structure's memory, or source
        // acceleration structure's memory if build mode is update, or other scratch buffers' memory.
        // Here validation is pessimistic: if one buffer associated to pInfos[other_info_j].scratchData.deviceAddress has an
        // overlap, an error will be logged.

        other_info_j += info_i;
        const Location other_info_j_loc = error_obj.location.dot(Field::pInfos, other_info_j);
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/6040
#if 0
            if (auto other_scratches = GetBuffersByAddress(pInfos[other_info_j].scratchData.deviceAddress);
                !other_scratches.empty()) {
                using BUFFER_STATE_PTR = ValidationStateTracker::BUFFER_STATE_PTR;
                BufferAddressValidation<3> other_scratches_validator;

                // Validate that scratch buffer's memory does not overlap destination acceleration structure's memory
                if (dst_as_state && dst_as_state->buffer_state) {
                    const vvl::Buffer &dst_as_buffer = *dst_as_state->buffer_state;
                    const range<VkDeviceSize> dst_as_buffer_range(
                        dst_as_state->create_infoKHR.offset,
                        dst_as_state->create_infoKHR.offset + dst_as_state->create_infoKHR.size);

                    other_scratches_validator.AddVuidValidation(
                        {"VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03703",
                         LogObjectList(commandBuffer, dst_as_state->Handle(), dst_as_buffer.Handle()),
                         // clang-format off
                         [info_i,
                         other_info_j,
                         &no_as_buffer_memory_overlap_msg,
                         &dst_as_buffer,
                         dst_as_buffer_range,
                         // Since scratch buffer size is unknown, compute an assumed scratch buffer size the idiomatic way
                         assumed_other_scratch_size = rt::ComputeScratchSize(device, pInfos[other_info_j], ppBuildRangeInfos[other_info_j]),
                         other_scratch_address = pInfos[other_info_j].scratchData.deviceAddress]
                         // clang-format on
                         (const BUFFER_STATE_PTR &other_scratch, std::string *out_error_msg) -> bool {
                             assert(other_scratch->DeviceAddressRange().includes(other_scratch_address));

                             const VkDeviceSize other_scratch_offset = other_scratch_address - other_scratch->deviceAddress;
                             const range<VkDeviceSize> other_scratch_range(
                                 other_scratch_offset,
                                 std::min(other_scratch_offset + assumed_other_scratch_size, other_scratch->createInfo.size));
                             if (other_scratch_range.invalid()) {
                                 // Do not validate this VU if range is invalid
                                 return true;
                             }

                             if (const auto [memory, overlap_range] = dst_as_buffer.GetResourceMemoryOverlap(
                                     dst_as_buffer_range, other_scratch, other_scratch_range);
                                 memory != VK_NULL_HANDLE) {
                                 if (out_error_msg) {
                                     std::stringstream dst_as_var_name_ss;
                                     dst_as_var_name_ss << "pInfos[" << info_i << "].dstAccelerationStructure";
                                     std::stringstream other_scratch_var_name_ss;
                                     other_scratch_var_name_ss << "pInfos[" << other_info_j << "].scratchData";

                                     *out_error_msg += no_as_buffer_memory_overlap_msg(
                                         dst_as_var_name_ss.str().c_str(), dst_as_buffer.buffer(),
                                         other_scratch_var_name_ss.str().c_str(), other_scratch->buffer(), memory, overlap_range);
                                 }
                                 return false;
                             }
                             return true;
                         },
                         [info_i, other_info_j]() {
                             return std::string("Some buffers associated to pInfos[") + std::to_string(other_info_j) +
                                    "].scratchData.deviceAddress have their underlying memory overlapping with the memory "
                                    "backing pInfos[" +
                                    std::to_string(info_i) + "].dstAccelerationStructure.\n";
                         }});
                }

                // Validate that scratch buffer's memory does not overlap source acceleration structure's memory if build mode is
                // update
                if (info->mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR && src_as_state &&
                    src_as_state->buffer_state) {
                    const vvl::Buffer &src_as_buffer = *src_as_state->buffer_state;
                    const range<VkDeviceSize> src_as_buffer_range(
                        src_as_state->create_infoKHR.offset,
                        src_as_state->create_infoKHR.offset + src_as_state->create_infoKHR.size);

                    other_scratches_validator.AddVuidValidation(
                        {"VUID-vkCmdBuildAccelerationStructuresKHR-scratchData-03705",
                         LogObjectList(commandBuffer, src_as_state->Handle(), src_as_buffer.Handle()),
                         // clang-format off
                         [info_i,
                         other_info_j,
                         &no_as_buffer_memory_overlap_msg,
                         &src_as_buffer,
                         src_as_buffer_range,
                         // Since scratch buffer size is unknown, compute an assumed scratch buffer size the idiomatic way
                         assumed_other_scratch_size = rt::ComputeScratchSize(device, pInfos[other_info_j], ppBuildRangeInfos[other_info_j]),
                         other_scratch_address = pInfos[other_info_j].scratchData.deviceAddress]
                         // clang-format on
                         (const BUFFER_STATE_PTR &other_scratch, std::string *out_error_msg) -> bool {
                             assert(other_scratch->DeviceAddressRange().includes(other_scratch_address));

                             const VkDeviceSize other_scratch_offset = other_scratch_address - other_scratch->deviceAddress;
                             const range<VkDeviceSize> other_scratch_range(
                                 other_scratch_offset,
                                 std::min(other_scratch_offset + assumed_other_scratch_size, other_scratch->createInfo.size));
                             if (other_scratch_range.invalid()) {
                                 // Do not validate this VU if range is invalid
                                 return true;
                             }

                             if (const auto [memory, overlap_range] = src_as_buffer.GetResourceMemoryOverlap(
                                     src_as_buffer_range, other_scratch, other_scratch_range);
                                 memory != VK_NULL_HANDLE) {
                                 if (out_error_msg) {
                                     std::stringstream src_as_var_name_ss;
                                     src_as_var_name_ss << "pInfos[" << info_i << "].srcAccelerationStructure";
                                     std::stringstream other_scratch_var_name_ss;
                                     other_scratch_var_name_ss << "pInfos[" << other_info_j << "].scratchData";

                                     *out_error_msg += no_as_buffer_memory_overlap_msg(
                                         src_as_var_name_ss.str().c_str(), src_as_buffer.buffer(),
                                         other_scratch_var_name_ss.str().c_str(), other_scratch->buffer(), memory, overlap_range);
                                 }
                                 return false;
                             }
                             return true;
                         },
                         [info_i, other_info_j]() {
                             return std::string("Some buffers associated to pInfos[") + std::to_string(other_info_j) +
                                    "].scratchData.deviceAddress have their underlying memory overlapping with the memory "
                                    "backing pInfos[" +
                                    std::to_string(info_i) + "].srcAccelerationStructure:\n";
                         }});
                }

                // Validate that scratch buffers' memory do not overlap.
                // Since info->scratchData.deviceAddress can point to multiple buffers,
                // `other_scratch` needs to be validated against all of these buffers: if one pair has their respective memory
                // overlapping, validation failed
                auto scratches = GetBuffersByAddress(info->scratchData.deviceAddress);
                if (info_i != other_info_j) {
                    if (!scratches.empty()) {
                        other_scratches_validator.AddVuidValidation(
                            {"VUID-vkCmdBuildAccelerationStructuresKHR-scratchData-03704", LogObjectList(commandBuffer),
                             // clang-format off
                             [this,
                             commandBuffer,
                             info_i,
                             scratch_address = info->scratchData.deviceAddress,
                             assumed_scratch_size = rt::ComputeScratchSize(device, info-> ppBuildRangeInfos[info_i]),
                             &scratches,
                             other_scratch_address = pInfos[other_info_j].scratchData.deviceAddress,
                             assumed_other_scratch_size = rt::ComputeScratchSize(device, pInfos[other_info_j], ppBuildRangeInfos[other_info_j])]
                             // clang-format on
                             (const BUFFER_STATE_PTR &other_scratch, std::string *out_error_msg) -> bool {
                                 assert(other_scratch->DeviceAddressRange().includes(other_scratch_address));

                                 const VkDeviceSize other_scratch_offset = other_scratch_address - other_scratch->deviceAddress;
                                 const range<VkDeviceSize> other_scratch_range(
                                     other_scratch_offset,
                                     std::min(other_scratch_offset + assumed_other_scratch_size, other_scratch->createInfo.size));
                                 if (other_scratch_range.invalid()) {
                                     // Do not validate this VU if range is invalid
                                     return true;
                                 }

                                 // Create a nested BufferAddressValidation object, this time to loop over buffers associated to
                                 // info->scratchData.deviceAddress
                                 // If one does overlap "other_scratch", then validation of 03704 failed
                                 BufferAddressValidation<1> scratch_and_other_scratch_overlap_validator = {
                                     {{{"No-VUID", LogObjectList(commandBuffer),
                                        // clang-format off
                                        [this,
                                        scratch_address,
                                        assumed_scratch_size,
                                        &other_scratch,
                                        other_scratch_range,
                                        parent_out_error_msg = out_error_msg]
                                        // clang-format on
                                        (const BUFFER_STATE_PTR &scratch, std::string *) -> bool {
                                            const VkDeviceSize scratch_offset = scratch_address - scratch->deviceAddress;
                                            const range<VkDeviceSize> scratch_range(
                                                scratch_offset,
                                                std::min(scratch_offset + assumed_scratch_size, scratch->createInfo.size));

                                            // Do not validate this VU if range is invalid
                                            if (scratch_range.invalid()) {
                                                return true;
                                            }

                                            if (const auto [memory, overlap_range] = scratch->GetResourceMemoryOverlap(
                                                    scratch_range, other_scratch, other_scratch_range);
                                                memory != VK_NULL_HANDLE) {
                                                if (parent_out_error_msg) {
                                                    std::stringstream scratch_error_msg_ss;
                                                    scratch_error_msg_ss << " {" << FormatHandle(scratch->buffer())
                                                                         << ", backed by " << FormatHandle(memory)
                                                                         << " - overlap on VkDeviceMemory space range "
                                                                         << string_range(overlap_range) << "}";
                                                    *parent_out_error_msg += scratch_error_msg_ss.str();
                                                }
                                                return false;
                                            }
                                            return true;
                                        }}}}};

                                 if (!out_error_msg) {
                                     return !scratch_and_other_scratch_overlap_validator.HasInvalidBuffer(scratches);
                                 } else {
                                     const std::string address_name = [&]() {
                                         std::stringstream address_name_ss;
                                         address_name_ss << "pInfos[" << info_i << "].scratchData.deviceAddress";
                                         return address_name_ss.str();
                                     }();
                                     *out_error_msg +=
                                         "Memory backing this buffer is overlapped by memory backing the following buffer(s) "
                                         "associated to ";
                                     *out_error_msg += address_name;
                                     *out_error_msg += ':';
                                     // Buffer from the `scratches` list overlapping `other_scratch` will be
                                     // appended in out_error_msg in the LogInvalidBuffers call
                                     return scratch_and_other_scratch_overlap_validator.LogInvalidBuffers(
                                         *this, scratches, "vkCmdBuildAccelerationStructures", address_name.c_str(),
                                         scratch_address);
                                 }
                             },
                             [info_i]() {
                                 return std::string(
                                            "The following buffers have their underlying memory overlapping buffers "
                                            "associated to pInfos[") +
                                        std::to_string(info_i) + "].scratchData.deviceAddress:\n";
                             }});
                    }
                }

                std::stringstream address_name_ss;
                address_name_ss << "pInfos[" << other_info_j << "].scratchData.deviceAddress";

                skip |= other_scratches_validator.LogErrorsIfInvalidBufferFound(
                    *this, other_scratches, "vkCmdBuildAccelerationStructuresKHR()", address_name_ss.str(),
                    pInfos[other_info_j].scratchData.deviceAddress);
            }
#endif
        // skip comparing to self info
        if (other_info_j != info_i) {
            const auto other_dst_as_state = Get<vvl::AccelerationStructureKHR>(pInfos[other_info_j].dstAccelerationStructure);
            const auto other_src_as_state = Get<vvl::AccelerationStructureKHR>(pInfos[other_info_j].srcAccelerationStructure);

            // Validate destination acceleration structure's memory is not overlapped by another source acceleration structure's
            // memory that is going to be updated by this cmd
            if (dst_as_state && dst_as_state->buffer_state && other_src_as_state && other_src_as_state->buffer_state) {
                if (pInfos[other_info_j].mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
                    const vvl::Buffer &buffer_a = *dst_as_state->buffer_state;
                    const range<VkDeviceSize> range_a(dst_as_state->create_infoKHR.offset,
                                                      dst_as_state->create_infoKHR.offset + dst_as_state->create_infoKHR.size);

                    const vvl::Buffer &buffer_b = *other_src_as_state->buffer_state;
                    const range<VkDeviceSize> range_b(
                        other_src_as_state->create_infoKHR.offset,
                        other_src_as_state->create_infoKHR.offset + other_src_as_state->create_infoKHR.size);

                    skip |= validate_no_as_buffer_memory_overlap(
                        *dst_as_state, info_i_loc.dot(Field::dstAccelerationStructure), buffer_a, range_a, *other_src_as_state,
                        other_info_j_loc.dot(Field::srcAccelerationStructure), buffer_b, range_b,
                        "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03701");
                }
            }

            // Validate that there is no destination acceleration structures' memory overlaps
            if (dst_as_state && dst_as_state->buffer_state && other_dst_as_state && other_dst_as_state->buffer_state) {
                const vvl::Buffer &buffer_a = *dst_as_state->buffer_state;
                const range<VkDeviceSize> range_a(dst_as_state->create_infoKHR.offset,
                                                  dst_as_state->create_infoKHR.offset + dst_as_state->create_infoKHR.size);

                const vvl::Buffer &buffer_b = *other_dst_as_state->buffer_state;
                const range<VkDeviceSize> range_b(
                    other_dst_as_state->create_infoKHR.offset,
                    other_dst_as_state->create_infoKHR.offset + other_dst_as_state->create_infoKHR.size);

                skip |= validate_no_as_buffer_memory_overlap(
                    *dst_as_state, info_i_loc.dot(Field::dstAccelerationStructure), buffer_a, range_a, *other_dst_as_state,
                    other_info_j_loc.dot(Field::dstAccelerationStructure), buffer_b, range_b,
                    "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03702");
            }
        }
    }

    return skip;
}

bool CoreChecks::ValidateAccelerationBuffers(VkCommandBuffer cmd_buffer, uint32_t info_i,
                                             const VkAccelerationStructureBuildGeometryInfoKHR &info,
                                             const VkAccelerationStructureBuildRangeInfoKHR *geometry_build_ranges,
                                             const Location &loc) const {
    bool skip = false;
    const auto geometry_count = info.geometryCount;
    const auto *p_geometries = info.pGeometries;
    const auto *const *const pp_geometries = info.ppGeometries;

    auto buffer_check = [this](uint32_t gi, const VkDeviceOrHostAddressConstKHR address, const Location &geom_loc) -> bool {
        const auto buffer_states = GetBuffersByAddress(address.deviceAddress);
        const bool no_valid_buffer_found =
            !buffer_states.empty() &&
            std::none_of(buffer_states.begin(), buffer_states.end(),
                         [](const ValidationStateTracker::BUFFER_STATE_PTR &buffer_state) {
                             return buffer_state->usage & VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
                         });
        if (no_valid_buffer_found) {
            LogObjectList objlist(device);
            for (const auto &buffer_state : buffer_states) {
                objlist.add(buffer_state->Handle());
            }
            return LogError(
                "VUID-vkCmdBuildAccelerationStructuresKHR-geometry-03673", objlist, geom_loc,
                "has no buffer which created with VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR.");
        }

        return false;
    };

    // Parameter validation has already checked VUID-VkAccelerationStructureBuildGeometryInfoKHR-pGeometries-03788
    // !(pGeometries && ppGeometries)
    std::function<const VkAccelerationStructureGeometryKHR &(uint32_t)> geom_accessor;
    if (p_geometries) {
        geom_accessor = [p_geometries](uint32_t i) -> const VkAccelerationStructureGeometryKHR & { return p_geometries[i]; };
    } else if (pp_geometries) {
        geom_accessor = [pp_geometries](uint32_t i) -> const VkAccelerationStructureGeometryKHR & {
            // pp_geometries[i] is assumed to be a valid pointer
            return *pp_geometries[i];
        };
    }

    if (geom_accessor) {
        const Location pp_build_range_info_loc(loc.function, Field::ppBuildRangeInfos, info_i);
        for (uint32_t geom_i = 0; geom_i < geometry_count; ++geom_i) {
            const Location p_geom_loc = loc.dot(pp_geometries ? Field::pGeometries : Field::ppGeometries, geom_i);
            const Location p_geom_geom_loc = p_geom_loc.dot(Field::geometry);
            const Location p_geom_geom_triangles_loc = p_geom_geom_loc.dot(Field::triangles);
            const auto &geom_data = geom_accessor(geom_i);
            switch (geom_data.geometryType) {
                case VK_GEOMETRY_TYPE_TRIANGLES_KHR:  // == VK_GEOMETRY_TYPE_TRIANGLES_NV
                {
                    skip |= buffer_check(geom_i, geom_data.geometry.triangles.vertexData,
                                         p_geom_geom_triangles_loc.dot(Field::vertexData));
                    skip |= buffer_check(geom_i, geom_data.geometry.triangles.indexData,
                                         p_geom_geom_triangles_loc.dot(Field::indexData));
                    skip |= buffer_check(geom_i, geom_data.geometry.triangles.transformData,
                                         p_geom_geom_triangles_loc.dot(Field::transformData));

                    if (geom_data.geometry.triangles.indexType != VK_INDEX_TYPE_NONE_KHR) {
                        if (const auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure)) {
                            const uint32_t recorded_first_vertex = src_as_state->build_range_infos[geom_i].firstVertex;
                            if (recorded_first_vertex != geometry_build_ranges[geom_i].firstVertex) {
                                const LogObjectList objlist(info.srcAccelerationStructure);
                                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresIndirectKHR-firstVertex-03770", objlist,
                                                 p_geom_loc,
                                                 " has corresponding VkAccelerationStructureBuildRangeInfoKHR %s[%" PRIu32
                                                 "], but this build range has its firstVertex member set to (%" PRIu32
                                                 ") when it was last specified as (%" PRIu32 ").",
                                                 pp_build_range_info_loc.Fields().c_str(), geom_i,
                                                 geometry_build_ranges[geom_i].firstVertex, recorded_first_vertex);
                            }
                        }
                    }
                    break;
                }
                case VK_GEOMETRY_TYPE_INSTANCES_KHR: {
                    const Location instances_loc = p_geom_geom_loc.dot(Field::instances);
                    const Location instances_data_loc = instances_loc.dot(Field::data);

                    skip |= buffer_check(geom_i, geom_data.geometry.instances.data, instances_data_loc);
                    auto buffer_states = GetBuffersByAddress(geom_data.geometry.instances.data.deviceAddress);
                    if (buffer_states.empty()) {
                        skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03813", cmd_buffer,
                                         instances_data_loc.dot(Field::deviceAddress), "(%" PRIu64 ") is not a valid address.",
                                         geom_data.geometry.instances.data.deviceAddress);
                    } else {
                        using BUFFER_STATE_PTR = ValidationStateTracker::BUFFER_STATE_PTR;
                        BufferAddressValidation<1> buffer_address_validator = {
                            {{{"VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03814", LogObjectList(cmd_buffer),
                               [this, instances_data_loc, cmd_buffer](const BUFFER_STATE_PTR &buffer_state,
                                                                      std::string *out_error_msg) {
                                   if (!out_error_msg) {
                                       return !buffer_state->sparse && buffer_state->IsMemoryBound();
                                   } else {
                                       return ValidateMemoryIsBoundToBuffer(
                                           cmd_buffer, *buffer_state, instances_data_loc.dot(Field::deviceAddress),
                                           "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03814");
                                   }
                               }}}}};

                        skip |= buffer_address_validator.LogErrorsIfNoValidBuffer(*this, buffer_states,
                                                                                  instances_data_loc.dot(Field::deviceAddress),
                                                                                  geom_data.geometry.instances.data.deviceAddress);
                    }

                    break;
                }
                case VK_GEOMETRY_TYPE_AABBS_KHR:  // == VK_GEOMETRY_TYPE_AABBS_NV
                    skip |= buffer_check(geom_i, geom_data.geometry.aabbs.data, p_geom_geom_loc.dot(Field::aabbs).dot(Field::data));
                    break;
                default:
                    // no-op
                    break;
            }
        }
    }

    const auto buffer_states = GetBuffersByAddress(info.scratchData.deviceAddress);
    if (buffer_states.empty()) {
        skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03802", device,
                         loc.dot(Field::scratchData).dot(Field::deviceAddress),
                         "(0x%" PRIx64 ") has no buffer is associated with it.", info.scratchData.deviceAddress);
    } else {
        const bool no_valid_buffer_found = std::none_of(buffer_states.begin(), buffer_states.end(),
                                                        [](const ValidationStateTracker::BUFFER_STATE_PTR &buffer_state) {
                                                            return buffer_state->usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
                                                        });
        if (no_valid_buffer_found) {
            LogObjectList objlist(device);
            for (const auto &buffer_state : buffer_states) {
                objlist.add(buffer_state->Handle());
            }
            skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03674", objlist,
                             loc.dot(Field::scratchData).dot(Field::deviceAddress),
                             "(0x%" PRIx64
                             ") has no buffer is associated with it that was created with VK_BUFFER_USAGE_STORAGE_BUFFER_BIT bit.",
                             info.scratchData.deviceAddress);
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateCmdBuildAccelerationStructuresKHR(
    VkCommandBuffer commandBuffer, uint32_t infoCount, const VkAccelerationStructureBuildGeometryInfoKHR *pInfos,
    const VkAccelerationStructureBuildRangeInfoKHR *const *ppBuildRangeInfos, const ErrorObject &error_obj) const {
    using sparse_container::range;
    bool skip = false;
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    assert(cb_state);
    skip |= ValidateCmd(*cb_state, error_obj.location);

    if (!pInfos || !ppBuildRangeInfos) {
        return skip;
    }

    for (const auto [info_i, info] : vvl::enumerate(pInfos, infoCount)) {
        const Location info_loc = error_obj.location.dot(Field::pInfos, info_i);

        const auto src_as_state = Get<vvl::AccelerationStructureKHR>(info->srcAccelerationStructure);
        const auto dst_as_state = Get<vvl::AccelerationStructureKHR>(info->dstAccelerationStructure);

        if (dst_as_state != nullptr) {
            skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_as_state->buffer_state,
                                                  info_loc.dot(Field::dstAccelerationStructure),
                                                  "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03707");
        }

        if (info->mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
            if (src_as_state->built &&
                !(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR)) {
                const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03667", objlist, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, srcAccelerationStructure has been previously "
                                 "constructed with flags %s.",
                                 string_VkBuildAccelerationStructureFlagsKHR(src_as_state->build_info_khr.flags).c_str());
            }
            if (src_as_state != nullptr) {
                if (!src_as_state->buffer_state) {
                    const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                    skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03708", objlist, info_loc.dot(Field::mode),
                                     "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR but the buffer associated with "
                                     "srcAccelerationStructure is not valid.");
                } else {
                    skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *src_as_state->buffer_state,
                                                          info_loc.dot(Field::srcAccelerationStructure),
                                                          "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03708");
                }
                if (info->flags != src_as_state->build_info_khr.flags) {
                    const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                    skip |=
                        LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03759", objlist, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, but %s (%s) must have the same value which"
                                 " was specified when srcAccelerationStructure was last built (%s).",
                                 info_loc.dot(Field::flags).Fields().c_str(),
                                 string_VkBuildAccelerationStructureFlagsKHR(info->flags).c_str(),
                                 string_VkBuildAccelerationStructureFlagsKHR(src_as_state->build_info_khr.flags).c_str());
                }
                if (info->type != src_as_state->build_info_khr.type) {
                    const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                    skip |=
                        LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03760", objlist, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, but type (%s) must have the same value which"
                                 " was specified when srcAccelerationStructure was last built (%s).",
                                 string_VkAccelerationStructureTypeKHR(info->type),
                                 string_VkAccelerationStructureTypeKHR(src_as_state->build_info_khr.type));
                }
                if (info->geometryCount != src_as_state->build_info_khr.geometryCount) {
                    const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                    skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03758", objlist, info_loc.dot(Field::mode),
                                     "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                     " but geometryCount (%" PRIu32
                                     ")  must have the same value which was specified when "
                                     "srcAccelerationStructure was last built (%" PRIu32 ").",
                                     info->geometryCount, src_as_state->build_info_khr.geometryCount);
                } else if (info->pGeometries || info->ppGeometries) {
                    for (uint32_t geom_i = 0; geom_i < info->geometryCount; ++geom_i) {
                        const VkAccelerationStructureGeometryKHR &updated_geometry =
                            info->pGeometries ? info->pGeometries[geom_i] : *info->ppGeometries[geom_i];

                        const safe_VkAccelerationStructureGeometryKHR &last_geometry =
                            src_as_state->build_info_khr.pGeometries ? src_as_state->build_info_khr.pGeometries[geom_i]
                                                                     : *src_as_state->build_info_khr.ppGeometries[geom_i];

                        const Location geom_loc =
                            info_loc.dot(info->pGeometries ? Field::pGeometries : Field::ppGeometries, geom_i);

                        if (updated_geometry.geometryType != last_geometry.geometryType) {
                            const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                            skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03761", objlist,
                                             geom_loc.dot(Field::geometryType), "is %s but was last specified as %s.",
                                             string_VkGeometryTypeKHR(updated_geometry.geometryType),
                                             string_VkGeometryTypeKHR(last_geometry.geometryType));
                        }

                        if (updated_geometry.flags != last_geometry.flags) {
                            const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                            skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03762", objlist,
                                             geom_loc.dot(Field::flags), "is %s but was last specified as %s.",
                                             string_VkGeometryFlagsKHR(updated_geometry.flags).c_str(),
                                             string_VkGeometryFlagsKHR(last_geometry.geometryType).c_str());
                        }

                        if (updated_geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR) {
                            if (updated_geometry.geometry.triangles.vertexFormat != last_geometry.geometry.triangles.vertexFormat) {
                                const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03763", objlist,
                                                 geom_loc.dot(Field::geometry).dot(Field::triangles).dot(Field::vertexFormat),
                                                 "is %s but was last specified as %s.",
                                                 string_VkFormat(updated_geometry.geometry.triangles.vertexFormat),
                                                 string_VkFormat(last_geometry.geometry.triangles.vertexFormat));
                            }
                        }

                        if (updated_geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR) {
                            if (updated_geometry.geometry.triangles.maxVertex != last_geometry.geometry.triangles.maxVertex) {
                                const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03764", objlist,
                                                 geom_loc.dot(Field::geometry).dot(Field::triangles).dot(Field::maxVertex),
                                                 "is %" PRIu32 " but was last specified as %" PRIu32 ".",
                                                 updated_geometry.geometry.triangles.maxVertex,
                                                 last_geometry.geometry.triangles.maxVertex);
                            }
                        }

                        if (updated_geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR) {
                            if (updated_geometry.geometry.triangles.indexType != last_geometry.geometry.triangles.indexType) {
                                const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03765", objlist,
                                                 geom_loc.dot(Field::geometry).dot(Field::triangles).dot(Field::indexType),
                                                 "is %s but was last specified as %s.",
                                                 string_VkIndexType(updated_geometry.geometry.triangles.indexType),
                                                 string_VkIndexType(last_geometry.geometry.triangles.indexType));
                            }
                        }

                        if (updated_geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR) {
                            if (last_geometry.geometry.triangles.transformData.deviceAddress == 0 &&
                                updated_geometry.geometry.triangles.transformData.deviceAddress != 0) {
                                const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03766", objlist,
                                                 geom_loc.dot(Field::geometry).dot(Field::triangles).dot(Field::transformData),
                                                 "is 0x%" PRIx64 " but was last specified as NULL.",
                                                 updated_geometry.geometry.triangles.transformData.deviceAddress);
                            }
                        }

                        if (updated_geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR) {
                            if (last_geometry.geometry.triangles.transformData.deviceAddress != 0 &&
                                updated_geometry.geometry.triangles.transformData.deviceAddress == 0) {
                                const LogObjectList objlist(device, commandBuffer, info->srcAccelerationStructure);
                                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03767", objlist,
                                                 geom_loc.dot(Field::geometry).dot(Field::triangles).dot(Field::transformData),
                                                 "is NULL but was last specified as 0x%" PRIx64 ".",
                                                 last_geometry.geometry.triangles.transformData.deviceAddress);
                            }
                        }
                    }
                }
            }
        }
        if (info->type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) {
            if (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR &&
                dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR) {
                const LogObjectList objlist(device, commandBuffer);
                skip |= LogError(
                    "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03700", objlist, info_loc.dot(Field::type),
                    "is VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, but its dstAccelerationStructure was created with %s.",
                    string_VkAccelerationStructureTypeKHR(dst_as_state->create_infoKHR.type));
            }
        }
        if (info->type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) {
            if (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR &&
                dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR) {
                const LogObjectList objlist(device, commandBuffer);
                skip |= LogError(
                    "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03699", objlist, info_loc.dot(Field::type),
                    "is VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, but its dstAccelerationStructure was created with %s.",
                    string_VkAccelerationStructureTypeKHR(dst_as_state->create_infoKHR.type));
            }
        }

        skip |= ValidateAccelerationBuffers(commandBuffer, info_i, *info, ppBuildRangeInfos[info_i], info_loc);

        skip |= ValidateAccelerationStructuresMemoryAlisasing(commandBuffer, infoCount, pInfos, info_i, error_obj);
    }

    return skip;
}

bool CoreChecks::PreCallValidateBuildAccelerationStructuresKHR(
    VkDevice device, VkDeferredOperationKHR deferredOperation, uint32_t infoCount,
    const VkAccelerationStructureBuildGeometryInfoKHR *pInfos,
    const VkAccelerationStructureBuildRangeInfoKHR *const *ppBuildRangeInfos, const ErrorObject &error_obj) const {
    bool skip = false;
    skip |= ValidateDeferredOperation(device, deferredOperation, error_obj.location.dot(Field::deferredOperation),
                                      "VUID-vkBuildAccelerationStructuresKHR-deferredOperation-03678");

    for (uint32_t i = 0; i < infoCount; ++i) {
        const Location info_loc = error_obj.location.dot(Field::pInfos, i);
        auto src_as_state = Get<vvl::AccelerationStructureKHR>(pInfos[i].srcAccelerationStructure);
        auto dst_as_state = Get<vvl::AccelerationStructureKHR>(pInfos[i].dstAccelerationStructure);
        if (dst_as_state) {
            skip |=
                ValidateHostVisibleMemoryIsBoundToBuffer(*dst_as_state->buffer_state, info_loc.dot(Field::dstAccelerationStructure),
                                                         "VUID-vkBuildAccelerationStructuresKHR-pInfos-03722");
        }
        if (pInfos[i].mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
            if (src_as_state == nullptr || !src_as_state->built ||
                !(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR)) {
                skip |= LogError(
                    "VUID-vkBuildAccelerationStructuresKHR-pInfos-03667", pInfos[i].srcAccelerationStructure,
                    info_loc.dot(Field::mode),
                    "is "
                    "VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, but its srcAccelerationStructure was built before with %s.",
                    string_VkBuildAccelerationStructureFlagsKHR(src_as_state->build_info_khr.flags).c_str());
            }
            if (src_as_state) {
                skip |= ValidateHostVisibleMemoryIsBoundToBuffer(*src_as_state->buffer_state,
                                                                 info_loc.dot(Field::srcAccelerationStructure),
                                                                 "VUID-vkBuildAccelerationStructuresKHR-pInfos-03723");
                if (pInfos[i].geometryCount != src_as_state->build_info_khr.geometryCount) {
                    skip |=
                        LogError("VUID-vkBuildAccelerationStructuresKHR-pInfos-03758", pInfos[i].srcAccelerationStructure,
                                 info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                 " but geometryCount (%" PRIu32 ") is different then srcAccelerationStructure value (%" PRIu32 ").",
                                 pInfos[i].geometryCount, src_as_state->build_info_khr.geometryCount);
                }
                if (pInfos[i].flags != src_as_state->build_info_khr.flags) {
                    skip |= LogError("VUID-vkBuildAccelerationStructuresKHR-pInfos-03759", pInfos[i].srcAccelerationStructure,
                                     info_loc.dot(Field::mode),
                                     "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                     " but %s (%s) is different then srcAccelerationStructure value (%s).",
                                     info_loc.dot(Field::flags).Fields().c_str(),
                                     string_VkBuildAccelerationStructureFlagsKHR(pInfos[i].flags).c_str(),
                                     string_VkBuildAccelerationStructureFlagsKHR(src_as_state->build_info_khr.flags).c_str());
                }
                if (pInfos[i].type != src_as_state->build_info_khr.type) {
                    skip |= LogError("VUID-vkBuildAccelerationStructuresKHR-pInfos-03760", pInfos[i].srcAccelerationStructure,
                                     info_loc.dot(Field::mode),
                                     "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                     " but type (%s) is different then srcAccelerationStructure value (%s).",
                                     string_VkAccelerationStructureTypeKHR(pInfos[i].type),
                                     string_VkAccelerationStructureTypeKHR(src_as_state->build_info_khr.type));
                }
            }
        }
        if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) {
            if (!dst_as_state ||
                (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR &&
                 dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) {
                skip |= LogError(
                    "VUID-vkBuildAccelerationStructuresKHR-pInfos-03700", pInfos[i].dstAccelerationStructure,
                    info_loc.dot(Field::type),
                    "is VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, but its dstAccelerationStructure was built with type %s.",
                    string_VkAccelerationStructureTypeKHR(dst_as_state->create_infoKHR.type));
            }
        }
        if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) {
            if (!dst_as_state ||
                (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR &&
                 dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) {
                skip |= LogError(
                    "VUID-vkBuildAccelerationStructuresKHR-pInfos-03699", pInfos[i].dstAccelerationStructure,
                    info_loc.dot(Field::type),
                    "is VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, but its dstAccelerationStructure was built with type %s.",
                    string_VkAccelerationStructureTypeKHR(dst_as_state->create_infoKHR.type));
            }
        }
    }
    return skip;
}
bool CoreChecks::PreCallValidateCmdBuildAccelerationStructureNV(VkCommandBuffer commandBuffer,
                                                                const VkAccelerationStructureInfoNV *pInfo, VkBuffer instanceData,
                                                                VkDeviceSize instanceOffset, VkBool32 update,
                                                                VkAccelerationStructureNV dst, VkAccelerationStructureNV src,
                                                                VkBuffer scratch, VkDeviceSize scratchOffset,
                                                                const ErrorObject &error_obj) const {
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    assert(cb_state);
    bool skip = false;

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

    if (pInfo != nullptr && pInfo->type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV) {
        for (uint32_t i = 0; i < pInfo->geometryCount; i++) {
            skip |= ValidateGeometryNV(pInfo->pGeometries[i], error_obj.location.dot(Field::pInfo).dot(Field::pGeometries, i));
        }
    }

    if (pInfo != nullptr && pInfo->geometryCount > phys_dev_ext_props.ray_tracing_props_nv.maxGeometryCount) {
        skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-geometryCount-02241", commandBuffer, error_obj.location,
                         "geometryCount [%" PRIu32
                         "] must be less than or equal to "
                         "VkPhysicalDeviceRayTracingPropertiesNV::maxGeometryCount.",
                         pInfo->geometryCount);
    }

    auto dst_as_state = Get<vvl::AccelerationStructureNV>(dst);
    auto src_as_state = Get<vvl::AccelerationStructureNV>(src);
    auto scratch_buffer_state = Get<vvl::Buffer>(scratch);

    if (dst_as_state != nullptr && pInfo != nullptr) {
        if (dst_as_state->create_infoNV.info.type != pInfo->type) {
            skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-dst-02488", commandBuffer, error_obj.location,
                             "create info VkAccelerationStructureInfoNV::type"
                             "[%s] must be identical to build info VkAccelerationStructureInfoNV::type [%s].",
                             string_VkAccelerationStructureTypeKHR(dst_as_state->create_infoNV.info.type),
                             string_VkAccelerationStructureTypeKHR(pInfo->type));
        }
        if (dst_as_state->create_infoNV.info.flags != pInfo->flags) {
            skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-dst-02488", commandBuffer, error_obj.location,
                             "create info VkAccelerationStructureInfoNV::flags"
                             "[%s] must be identical to build info VkAccelerationStructureInfoNV::flags [%s].",
                             string_VkBuildAccelerationStructureFlagsKHR(dst_as_state->create_infoNV.info.flags).c_str(),
                             string_VkBuildAccelerationStructureFlagsKHR(pInfo->flags).c_str());
        }
        if (dst_as_state->create_infoNV.info.instanceCount < pInfo->instanceCount) {
            skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-dst-02488", commandBuffer, error_obj.location,
                             "create info VkAccelerationStructureInfoNV::instanceCount "
                             "[%" PRIu32
                             "] must be greater than or equal to build info VkAccelerationStructureInfoNV::instanceCount [%" PRIu32
                             "].",
                             dst_as_state->create_infoNV.info.instanceCount, pInfo->instanceCount);
        }
        if (dst_as_state->create_infoNV.info.geometryCount < pInfo->geometryCount) {
            skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-dst-02488", commandBuffer, error_obj.location,
                             "create info VkAccelerationStructureInfoNV::geometryCount"
                             "[%" PRIu32
                             "] must be greater than or equal to build info VkAccelerationStructureInfoNV::geometryCount [%" PRIu32
                             "].",
                             dst_as_state->create_infoNV.info.geometryCount, pInfo->geometryCount);
        } else {
            for (uint32_t i = 0; i < pInfo->geometryCount; i++) {
                const VkGeometryDataNV &create_geometry_data = dst_as_state->create_infoNV.info.pGeometries[i].geometry;
                const VkGeometryDataNV &build_geometry_data = pInfo->pGeometries[i].geometry;
                if (create_geometry_data.triangles.vertexCount < build_geometry_data.triangles.vertexCount) {
                    skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-dst-02488", commandBuffer, error_obj.location,
                                     "create info pGeometries[%" PRIu32 "].geometry.triangles.vertexCount [%" PRIu32
                                     "]"
                                     "must be greater than or equal to build info pGeometries[%" PRIu32
                                     "].geometry.triangles.vertexCount [%" PRIu32 "].",
                                     i, create_geometry_data.triangles.vertexCount, i, build_geometry_data.triangles.vertexCount);
                    break;
                }
                if (create_geometry_data.triangles.indexCount < build_geometry_data.triangles.indexCount) {
                    skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-dst-02488", commandBuffer, error_obj.location,
                                     "create info pGeometries[%" PRIu32 "].geometry.triangles.indexCount [%" PRIu32
                                     "]"
                                     "must be greater than or equal to build info pGeometries[%" PRIu32
                                     "].geometry.triangles.indexCount [%" PRIu32 "].",
                                     i, create_geometry_data.triangles.indexCount, i, build_geometry_data.triangles.indexCount);
                    break;
                }
                if (create_geometry_data.aabbs.numAABBs < build_geometry_data.aabbs.numAABBs) {
                    skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-dst-02488", commandBuffer, error_obj.location,
                                     "create info pGeometries[%" PRIu32 "].geometry.aabbs.numAABBs [%" PRIu32
                                     "]"
                                     "must be greater than or equal to build info pGeometries[%" PRIu32
                                     "].geometry.aabbs.numAABBs [%" PRIu32 "].",
                                     i, create_geometry_data.aabbs.numAABBs, i, build_geometry_data.aabbs.numAABBs);
                    break;
                }
            }
        }
    }

    if (dst_as_state != nullptr) {
        skip |= VerifyBoundMemoryIsValid(dst_as_state->MemState(), LogObjectList(commandBuffer, dst), dst_as_state->Handle(),
                                         error_obj.location.dot(Field::dst), "VUID-vkCmdBuildAccelerationStructureNV-dst-07787");
    }

    if (update == VK_TRUE) {
        if (src == VK_NULL_HANDLE) {
            skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-update-02489", commandBuffer, error_obj.location,
                             "If update is VK_TRUE, src must not be VK_NULL_HANDLE.");
        } else {
            if (src_as_state == nullptr || !src_as_state->built ||
                !(src_as_state->build_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV)) {
                skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-update-02490", commandBuffer, error_obj.location,
                                 "If update is VK_TRUE, src must have been built before "
                                 "with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV set in "
                                 "VkAccelerationStructureInfoNV::flags.");
            }
        }
        if (dst_as_state != nullptr && !dst_as_state->update_scratch_memory_requirements_checked) {
            skip |= LogWarning("WARNING-vkCmdBuildAccelerationStructureNV-update-requirements", dst, error_obj.location,
                               "Updating %s but vkGetAccelerationStructureMemoryRequirementsNV() "
                               "has not been called for update scratch memory.",
                               FormatHandle(dst_as_state->acceleration_structure()).c_str());
            // Use requirements fetched at create time
        }
        if (scratch_buffer_state != nullptr && dst_as_state != nullptr &&
            dst_as_state->update_scratch_memory_requirements.size > (scratch_buffer_state->createInfo.size - scratchOffset)) {
            skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-update-02492", commandBuffer, error_obj.location,
                             "If update is VK_TRUE, The size member of the "
                             "VkMemoryRequirements structure returned from a call to "
                             "vkGetAccelerationStructureMemoryRequirementsNV with "
                             "VkAccelerationStructureMemoryRequirementsInfoNV::accelerationStructure set to dst and "
                             "VkAccelerationStructureMemoryRequirementsInfoNV::type set to "
                             "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV must be less than "
                             "or equal to the size of scratch minus scratchOffset");
        }
    } else {
        if (dst_as_state != nullptr && !dst_as_state->build_scratch_memory_requirements_checked) {
            skip |= LogWarning("WARNING-vkCmdBuildAccelerationStructureNV-scratch-requirements", dst, error_obj.location,
                               "Assigning scratch buffer to %s but "
                               "vkGetAccelerationStructureMemoryRequirementsNV() has not been called for scratch memory.",
                               FormatHandle(dst_as_state->acceleration_structure()).c_str());
            // Use requirements fetched at create time
        }
        if (scratch_buffer_state != nullptr && dst_as_state != nullptr &&
            dst_as_state->build_scratch_memory_requirements.size > (scratch_buffer_state->createInfo.size - scratchOffset)) {
            skip |= LogError("VUID-vkCmdBuildAccelerationStructureNV-update-02491", commandBuffer, error_obj.location,
                             "If update is VK_FALSE, The size member of the "
                             "VkMemoryRequirements structure returned from a call to "
                             "vkGetAccelerationStructureMemoryRequirementsNV with "
                             "VkAccelerationStructureMemoryRequirementsInfoNV::accelerationStructure set to dst and "
                             "VkAccelerationStructureMemoryRequirementsInfoNV::type set to "
                             "VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV must be less than "
                             "or equal to the size of scratch minus scratchOffset");
        }
    }
    if (instanceData != VK_NULL_HANDLE) {
        auto buffer_state = Get<vvl::Buffer>(instanceData);
        if (buffer_state) {
            skip |= ValidateBufferUsageFlags(
                LogObjectList(commandBuffer, instanceData), *buffer_state, VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, true,
                "VUID-VkAccelerationStructureInfoNV-instanceData-02782", error_obj.location.dot(Field::instanceData));
        }
    }
    if (scratch_buffer_state) {
        skip |= ValidateBufferUsageFlags(
            LogObjectList(commandBuffer, scratch), *scratch_buffer_state, VK_BUFFER_USAGE_RAY_TRACING_BIT_NV, true,
            "VUID-VkAccelerationStructureInfoNV-scratch-02781", error_obj.location.dot(Field::scratch));
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdCopyAccelerationStructureNV(VkCommandBuffer commandBuffer, VkAccelerationStructureNV dst,
                                                               VkAccelerationStructureNV src,
                                                               VkCopyAccelerationStructureModeNV mode,
                                                               const ErrorObject &error_obj) const {
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    assert(cb_state);
    bool skip = false;

    skip |= ValidateCmd(*cb_state, error_obj.location);
    auto dst_as_state = Get<vvl::AccelerationStructureNV>(dst);
    auto src_as_state = Get<vvl::AccelerationStructureNV>(src);

    if (dst_as_state != nullptr) {
        const LogObjectList objlist(commandBuffer, dst);
        skip |= VerifyBoundMemoryIsValid(dst_as_state->MemState(), objlist, dst_as_state->Handle(),
                                         error_obj.location.dot(Field::dst), "VUID-vkCmdCopyAccelerationStructureNV-dst-07792");
        skip |= VerifyBoundMemoryIsDeviceVisible(dst_as_state->MemState(), objlist, dst_as_state->Handle(),
                                                 error_obj.location.dot(Field::dst),
                                                 "VUID-vkCmdCopyAccelerationStructureNV-buffer-03719");
    }
    if (src_as_state != nullptr) {
        const LogObjectList objlist(commandBuffer, src);
        skip |= VerifyBoundMemoryIsDeviceVisible(src_as_state->MemState(), objlist, src_as_state->Handle(),
                                                 error_obj.location.dot(Field::src),
                                                 "VUID-vkCmdCopyAccelerationStructureNV-buffer-03718");
        if (!src_as_state->built) {
            skip |= LogError("VUID-vkCmdCopyAccelerationStructureNV-src-04963", commandBuffer, error_obj.location,
                             "The source acceleration structure src has not yet been built.");
        }
    }

    if (mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV) {
        if (src_as_state != nullptr &&
            (!src_as_state->built || !(src_as_state->build_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV))) {
            skip |= LogError("VUID-vkCmdCopyAccelerationStructureNV-src-03411", commandBuffer, error_obj.location,
                             "src must have been built with "
                             "VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV if mode is "
                             "VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV.");
        }
    }
    if (!(mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV || mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR)) {
        skip |= LogError("VUID-vkCmdCopyAccelerationStructureNV-mode-03410", commandBuffer, error_obj.location,
                         "mode must be VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR"
                         "or VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_KHR.");
    }
    return skip;
}

bool CoreChecks::PreCallValidateDestroyAccelerationStructureNV(VkDevice device, VkAccelerationStructureNV accelerationStructure,
                                                               const VkAllocationCallbacks *pAllocator,
                                                               const ErrorObject &error_obj) const {
    auto as_state = Get<vvl::AccelerationStructureNV>(accelerationStructure);
    bool skip = false;
    if (as_state) {
        skip |= ValidateObjectNotInUse(as_state.get(), error_obj.location,
                                       "VUID-vkDestroyAccelerationStructureNV-accelerationStructure-03752");
    }
    return skip;
}

bool CoreChecks::PreCallValidateDestroyAccelerationStructureKHR(VkDevice device, VkAccelerationStructureKHR accelerationStructure,
                                                                const VkAllocationCallbacks *pAllocator,
                                                                const ErrorObject &error_obj) const {
    auto as_state = Get<vvl::AccelerationStructureKHR>(accelerationStructure);
    bool skip = false;
    if (as_state) {
        skip |= ValidateObjectNotInUse(as_state.get(), error_obj.location,
                                       "VUID-vkDestroyAccelerationStructureKHR-accelerationStructure-02442");
    }
    return skip;
}

void CoreChecks::PreCallRecordCmdWriteAccelerationStructuresPropertiesKHR(VkCommandBuffer commandBuffer,
                                                                          uint32_t accelerationStructureCount,
                                                                          const VkAccelerationStructureKHR *pAccelerationStructures,
                                                                          VkQueryType queryType, VkQueryPool queryPool,
                                                                          uint32_t firstQuery, const RecordObject &record_obj) {
    if (disabled[query_validation]) return;
    // Enqueue the submit time validation check here, before the submit time state update in StateTracker::PostCall...
    auto cb_state = GetWrite<vvl::CommandBuffer>(commandBuffer);
    cb_state->queryUpdates.emplace_back([accelerationStructureCount, firstQuery, queryPool](
                                            vvl::CommandBuffer &cb_state_arg, bool do_validate, VkQueryPool &firstPerfQueryPool,
                                            uint32_t perfPass, QueryMap *localQueryToStateMap) {
        if (!do_validate) return false;
        bool skip = false;
        for (uint32_t i = 0; i < accelerationStructureCount; i++) {
            QueryObject query_obj = {queryPool, firstQuery + i, perfPass};
            skip |= VerifyQueryIsReset(cb_state_arg, query_obj, Func::vkCmdWriteAccelerationStructuresPropertiesKHR,
                                       firstPerfQueryPool, perfPass, localQueryToStateMap);
        }
        return skip;
    });
}

bool CoreChecks::PreCallValidateWriteAccelerationStructuresPropertiesKHR(VkDevice device, uint32_t accelerationStructureCount,
                                                                         const VkAccelerationStructureKHR *pAccelerationStructures,
                                                                         VkQueryType queryType, size_t dataSize, void *pData,
                                                                         size_t stride, const ErrorObject &error_obj) const {
    bool skip = false;
    for (uint32_t i = 0; i < accelerationStructureCount; ++i) {
        const Location as_loc = error_obj.location.dot(Field::pAccelerationStructures, i);
        auto as_state = Get<vvl::AccelerationStructureKHR>(pAccelerationStructures[i]);
        const auto &as_info = as_state->build_info_khr;
        if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR) {
            if (!(as_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) {
                const LogObjectList objlist(device, pAccelerationStructures[i]);
                skip |= LogError("VUID-vkWriteAccelerationStructuresPropertiesKHR-accelerationStructures-03431", objlist, as_loc,
                                 "has flags %s.", string_VkBuildAccelerationStructureFlagsKHR(as_info.flags).c_str());
            }
        }
        if (as_state) {
            skip |= ValidateHostVisibleMemoryIsBoundToBuffer(*as_state->buffer_state, as_loc,
                                                             "VUID-vkWriteAccelerationStructuresPropertiesKHR-buffer-03733");
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdWriteAccelerationStructuresPropertiesKHR(
    VkCommandBuffer commandBuffer, uint32_t accelerationStructureCount, const VkAccelerationStructureKHR *pAccelerationStructures,
    VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery, const ErrorObject &error_obj) const {
    bool skip = false;
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    skip |= ValidateCmd(*cb_state, error_obj.location);
    auto query_pool_state = Get<vvl::QueryPool>(queryPool);
    const auto &query_pool_ci = query_pool_state->createInfo;
    if (query_pool_ci.queryType != queryType) {
        skip |= LogError("VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-queryPool-02493", commandBuffer,
                         error_obj.location.dot(Field::queryType),
                         "was created with %s which is differnent from the type queryPool was created with %s.",
                         string_VkQueryType(queryType), string_VkQueryType(query_pool_ci.queryType));
    }
    for (uint32_t i = 0; i < accelerationStructureCount; ++i) {
        if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR) {
            auto as_state = Get<vvl::AccelerationStructureKHR>(pAccelerationStructures[i]);
            if (!(as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) {
                skip |= LogError("VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-accelerationStructures-03431", commandBuffer,
                                 error_obj.location.dot(Field::pAccelerationStructures, i),
                                 "was built with %s, but queryType is VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR.",
                                 string_VkBuildAccelerationStructureFlagsKHR(as_state->build_info_khr.flags).c_str());
            }
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdWriteAccelerationStructuresPropertiesNV(
    VkCommandBuffer commandBuffer, uint32_t accelerationStructureCount, const VkAccelerationStructureNV *pAccelerationStructures,
    VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery, const ErrorObject &error_obj) const {
    bool skip = false;
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    skip |= ValidateCmd(*cb_state, error_obj.location);
    auto query_pool_state = Get<vvl::QueryPool>(queryPool);
    const auto &query_pool_ci = query_pool_state->createInfo;
    if (query_pool_ci.queryType != queryType) {
        skip |= LogError("VUID-vkCmdWriteAccelerationStructuresPropertiesNV-queryPool-03755", commandBuffer,
                         error_obj.location.dot(Field::queryType),
                         "was created with %s which is differnent from the type queryPool was created with %s.",
                         string_VkQueryType(queryType), string_VkQueryType(query_pool_ci.queryType));
    }
    for (uint32_t i = 0; i < accelerationStructureCount; ++i) {
        if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV) {
            auto as_state = Get<vvl::AccelerationStructureNV>(pAccelerationStructures[i]);
            if (!(as_state->build_info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) {
                skip |= LogError("VUID-vkCmdWriteAccelerationStructuresPropertiesNV-pAccelerationStructures-06215", commandBuffer,
                                 error_obj.location.dot(Field::pAccelerationStructures, i),
                                 "was built with %s, but queryType is VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR.",
                                 string_VkBuildAccelerationStructureFlagsKHR(as_state->build_info.flags).c_str());
            }
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdBuildAccelerationStructuresIndirectKHR(VkCommandBuffer commandBuffer, uint32_t infoCount,
                                                                          const VkAccelerationStructureBuildGeometryInfoKHR *pInfos,
                                                                          const VkDeviceAddress *pIndirectDeviceAddresses,
                                                                          const uint32_t *pIndirectStrides,
                                                                          const uint32_t *const *ppMaxPrimitiveCounts,
                                                                          const ErrorObject &error_obj) const {
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    assert(cb_state);
    bool skip = false;
    skip |= ValidateCmd(*cb_state, error_obj.location);
    // TODO - This is not called in vkCmdBuildAccelerationStructuresKHR and only seems used for ValidateActionState
    skip |= ValidateCmdRayQueryState(*cb_state, VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, error_obj.location);
    for (uint32_t i = 0; i < infoCount; ++i) {
        const Location info_loc = error_obj.location.dot(Field::pInfos, i);
        auto src_as_state = Get<vvl::AccelerationStructureKHR>(pInfos[i].srcAccelerationStructure);
        auto dst_as_state = Get<vvl::AccelerationStructureKHR>(pInfos[i].dstAccelerationStructure);
        skip |=
            ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_as_state->buffer_state, info_loc.dot(Field::dstAccelerationStructure),
                                          "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03707");
        if (pInfos[i].mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
            skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *src_as_state->buffer_state,
                                                  info_loc.dot(Field::srcAccelerationStructure),
                                                  "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03708");
            if (src_as_state == nullptr || !src_as_state->built ||
                !(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR)) {
                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03667",
                                 pInfos[i].srcAccelerationStructure, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                 " but srcAccelerationStructure was created with (%s)",
                                 string_VkBuildAccelerationStructureFlagsKHR(src_as_state->build_info_khr.flags).c_str());
            }
            if (pInfos[i].geometryCount != src_as_state->build_info_khr.geometryCount) {
                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03758",
                                 pInfos[i].srcAccelerationStructure, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                 " but geometryCount (%" PRIu32 ") is different then srcAccelerationStructure value (%" PRIu32 ").",
                                 pInfos[i].geometryCount, src_as_state->build_info_khr.geometryCount);
            }
            if (pInfos[i].flags != src_as_state->build_info_khr.flags) {
                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03759",
                                 pInfos[i].srcAccelerationStructure, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                 " but %s (%s) is different than srcAccelerationStructure value (%s).",
                                 info_loc.dot(Field::flags).Fields().c_str(),
                                 string_VkBuildAccelerationStructureFlagsKHR(pInfos[i].flags).c_str(),
                                 string_VkBuildAccelerationStructureFlagsKHR(src_as_state->build_info_khr.flags).c_str());
            }
            if (pInfos[i].type != src_as_state->build_info_khr.type) {
                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03760",
                                 pInfos[i].srcAccelerationStructure, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                 " but type (%s) is different than srcAccelerationStructure value (%s).",
                                 string_VkAccelerationStructureTypeKHR(pInfos[i].type),
                                 string_VkAccelerationStructureTypeKHR(src_as_state->build_info_khr.type));
            }
        }
        if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) {
            if (!dst_as_state ||
                (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR &&
                 dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) {
                skip |= LogError(
                    "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03700", pInfos[i].dstAccelerationStructure,
                    info_loc.dot(Field::type),
                    "is VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR, but its dstAccelerationStructure was built with type %s.",
                    string_VkAccelerationStructureTypeKHR(dst_as_state->create_infoKHR.type));
            }
        }
        if (pInfos[i].type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) {
            if (!dst_as_state ||
                (dst_as_state && dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR &&
                 dst_as_state->create_infoKHR.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR)) {
                skip |= LogError(
                    "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03699", pInfos[i].dstAccelerationStructure,
                    info_loc.dot(Field::type),
                    "is VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, but its dstAccelerationStructure was built with type %s.",
                    string_VkAccelerationStructureTypeKHR(dst_as_state->create_infoKHR.type));
            }
        }
    }
    return skip;
}

bool CoreChecks::ValidateCopyAccelerationStructureInfoKHR(const VkCopyAccelerationStructureInfoKHR *pInfo,
                                                          const VulkanTypedHandle &handle, const Location &info_loc) const {
    bool skip = false;
    if (pInfo->mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR) {
        auto src_as_state = Get<vvl::AccelerationStructureKHR>(pInfo->src);
        if (!(src_as_state->build_info_khr.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) {
            const LogObjectList objlist(handle, pInfo->src);
            skip |= LogError("VUID-VkCopyAccelerationStructureInfoKHR-src-03411", objlist, info_loc.dot(Field::src),
                             "(%s) must have been built with VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR"
                             "if mode is VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR.",
                             FormatHandle(pInfo->src).c_str());
        }
    }
    auto src_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->src);
    if (src_accel_state) {
        auto buffer_state = Get<vvl::Buffer>(src_accel_state->create_infoKHR.buffer);
        skip |= ValidateMemoryIsBoundToBuffer(device, *buffer_state, info_loc.dot(Field::src),
                                              "VUID-VkCopyAccelerationStructureInfoKHR-buffer-03718");
    }
    auto dst_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->dst);
    if (dst_accel_state) {
        auto buffer_state = Get<vvl::Buffer>(dst_accel_state->create_infoKHR.buffer);
        skip |= ValidateMemoryIsBoundToBuffer(device, *buffer_state, info_loc.dot(Field::dst),
                                              "VUID-VkCopyAccelerationStructureInfoKHR-buffer-03719");
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdCopyAccelerationStructureKHR(VkCommandBuffer commandBuffer,
                                                                const VkCopyAccelerationStructureInfoKHR *pInfo,
                                                                const ErrorObject &error_obj) const {
    bool skip = false;
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    assert(cb_state);
    skip |= ValidateCmd(*cb_state, error_obj.location);
    if (pInfo) {
        const Location info_loc = error_obj.location.dot(Field::pInfo);
        skip |= ValidateCopyAccelerationStructureInfoKHR(pInfo, error_obj.handle, info_loc);
        auto src_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->src);
        if (src_accel_state) {
            skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *src_accel_state->buffer_state, info_loc.dot(Field::src),
                                                  "VUID-vkCmdCopyAccelerationStructureKHR-buffer-03737");
        }
        auto dst_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->dst);
        if (dst_accel_state) {
            skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_accel_state->buffer_state, info_loc.dot(Field::dst),
                                                  "VUID-vkCmdCopyAccelerationStructureKHR-buffer-03738");
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateDestroyDeferredOperationKHR(VkDevice device, VkDeferredOperationKHR operation,
                                                            const VkAllocationCallbacks *pAllocator,
                                                            const ErrorObject &error_obj) const {
    bool skip = false;
    skip |= ValidateDeferredOperation(device, operation, error_obj.location.dot(Field::operation),
                                      "VUID-vkDestroyDeferredOperationKHR-operation-03436");
    return skip;
}

bool CoreChecks::PreCallValidateCopyAccelerationStructureKHR(VkDevice device, VkDeferredOperationKHR deferredOperation,
                                                             const VkCopyAccelerationStructureInfoKHR *pInfo,
                                                             const ErrorObject &error_obj) const {
    bool skip = false;
    skip |= ValidateDeferredOperation(device, deferredOperation, error_obj.location.dot(Field::deferredOperation),
                                      "VUID-vkCopyAccelerationStructureKHR-deferredOperation-03678");

    if (pInfo) {
        const Location info_loc = error_obj.location.dot(Field::pInfo);
        skip |= ValidateCopyAccelerationStructureInfoKHR(pInfo, error_obj.handle, error_obj.location.dot(Field::pInfo));
        auto src_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->src);
        if (src_accel_state) {
            skip |= ValidateHostVisibleMemoryIsBoundToBuffer(*src_accel_state->buffer_state, info_loc.dot(Field::src),
                                                             "VUID-vkCopyAccelerationStructureKHR-buffer-03727");
        }
        auto dst_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->dst);
        if (dst_accel_state) {
            skip |= ValidateHostVisibleMemoryIsBoundToBuffer(*dst_accel_state->buffer_state, info_loc.dot(Field::dst),
                                                             "VUID-vkCopyAccelerationStructureKHR-buffer-03728");
        }
    }
    return skip;
}

bool CoreChecks::PreCallValidateCopyAccelerationStructureToMemoryKHR(VkDevice device, VkDeferredOperationKHR deferredOperation,
                                                                     const VkCopyAccelerationStructureToMemoryInfoKHR *pInfo,
                                                                     const ErrorObject &error_obj) const {
    bool skip = false;
    skip |= ValidateDeferredOperation(device, deferredOperation, error_obj.location.dot(Field::deferredOperation),
                                      "VUID-vkCopyAccelerationStructureToMemoryKHR-deferredOperation-03678");
    return skip;
}

bool CoreChecks::PreCallValidateCmdCopyAccelerationStructureToMemoryKHR(VkCommandBuffer commandBuffer,
                                                                        const VkCopyAccelerationStructureToMemoryInfoKHR *pInfo,
                                                                        const ErrorObject &error_obj) const {
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    assert(cb_state);
    bool skip = false;
    skip |= ValidateCmd(*cb_state, error_obj.location);

    auto accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->src);
    if (accel_state) {
        auto buffer_state = Get<vvl::Buffer>(accel_state->create_infoKHR.buffer);
        skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *buffer_state, error_obj.location.dot(Field::pInfo).dot(Field::src),
                                              "VUID-vkCmdCopyAccelerationStructureToMemoryKHR-None-03559");
    }
    return skip;
}

bool CoreChecks::PreCallValidateCopyMemoryToAccelerationStructureKHR(VkDevice device, VkDeferredOperationKHR deferredOperation,
                                                                     const VkCopyMemoryToAccelerationStructureInfoKHR *pInfo,
                                                                     const ErrorObject &error_obj) const {
    bool skip = false;
    skip |= ValidateDeferredOperation(device, deferredOperation, error_obj.location.dot(Field::deferredOperation),
                                      "VUID-vkCopyMemoryToAccelerationStructureKHR-deferredOperation-03678");

    auto accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->dst);
    if (accel_state) {
        skip |= ValidateHostVisibleMemoryIsBoundToBuffer(*accel_state->buffer_state,
                                                         error_obj.location.dot(Field::pInfo).dot(Field::dst),
                                                         "VUID-vkCopyMemoryToAccelerationStructureKHR-buffer-03730");
    }

    return skip;
}

bool CoreChecks::PreCallValidateCmdCopyMemoryToAccelerationStructureKHR(VkCommandBuffer commandBuffer,
                                                                        const VkCopyMemoryToAccelerationStructureInfoKHR *pInfo,
                                                                        const ErrorObject &error_obj) const {
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    assert(cb_state);
    bool skip = false;
    skip |= ValidateCmd(*cb_state, error_obj.location);

    auto accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->dst);
    if (accel_state) {
        skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *accel_state->buffer_state,
                                              error_obj.location.dot(Field::pInfo).dot(Field::dst),
                                              "VUID-vkCmdCopyMemoryToAccelerationStructureKHR-buffer-03745");
    }
    return skip;
}

bool CoreChecks::ValidateCmdRayQueryState(const vvl::CommandBuffer &cb_state, const VkPipelineBindPoint bind_point,
                                          const Location &loc) const {
    bool skip = false;
    const vvl::DrawDispatchVuid &vuid = vvl::GetDrawDispatchVuid(loc.function);
    const auto lv_bind_point = ConvertToLvlBindPoint(bind_point);
    const auto &last_bound = cb_state.lastBound[lv_bind_point];
    const auto *pipe = last_bound.pipeline_state;

    bool ray_query_shader = false;
    if (nullptr != pipe) {
        if (bind_point == VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) {
            ray_query_shader = true;
        } else {
            // TODO - Loop through shader for RayQueryKHR for draw/dispatch commands
        }
    }

    if (cb_state.unprotected == false && ray_query_shader) {
        skip |= LogError(vuid.ray_query_protected_cb_03635, cb_state.commandBuffer(), loc,
                         "can't use in protected command buffers for RayQuery operations.");
    }

    return skip;
}

uint32_t CoreChecks::CalcTotalShaderGroupCount(const vvl::Pipeline &pipeline) const {
    uint32_t total = 0;
    if (pipeline.GetCreateInfoSType() == VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR) {
        const auto &create_info = pipeline.GetCreateInfo<VkRayTracingPipelineCreateInfoKHR>();
        total = create_info.groupCount;

        if (create_info.pLibraryInfo) {
            for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) {
                auto library_pipeline_state = Get<vvl::Pipeline>(create_info.pLibraryInfo->pLibraries[i]);
                total += CalcTotalShaderGroupCount(*library_pipeline_state.get());
            }
        }
    } else if (pipeline.GetCreateInfoSType() == VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV) {
        const auto &create_info = pipeline.GetCreateInfo<VkRayTracingPipelineCreateInfoNV>();
        total = create_info.groupCount;

        if (create_info.pLibraryInfo) {
            for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) {
                auto library_pipeline_state = Get<vvl::Pipeline>(create_info.pLibraryInfo->pLibraries[i]);
                total += CalcTotalShaderGroupCount(*library_pipeline_state.get());
            }
        }
    }

    return total;
}

bool CoreChecks::PreCallValidateGetRayTracingShaderGroupHandlesKHR(VkDevice device, VkPipeline pipeline, uint32_t firstGroup,
                                                                   uint32_t groupCount, size_t dataSize, void *pData,
                                                                   const ErrorObject &error_obj) const {
    bool skip = false;
    auto pPipeline = Get<vvl::Pipeline>(pipeline);
    if (!pPipeline) {
        return skip;
    } else if (pPipeline->pipeline_type != VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) {
        skip |=
            LogError("VUID-vkGetRayTracingShaderGroupHandlesKHR-pipeline-04619", pipeline, error_obj.location.dot(Field::pipeline),
                     "is a %s pipeline.", string_VkPipelineBindPoint(pPipeline->pipeline_type));
        return skip;
    }

    const vvl::Pipeline &pipeline_state = *pPipeline;
    if (pipeline_state.create_flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) {
        if (!enabled_features.pipelineLibraryGroupHandles) {
            skip |= LogError("VUID-vkGetRayTracingShaderGroupHandlesKHR-pipeline-07828", pipeline,
                             error_obj.location.dot(Field::pipeline),
                             "was created with %s, but the pipelineLibraryGroupHandles feature was not enabled.",
                             string_VkPipelineCreateFlags2KHR(pipeline_state.create_flags).c_str());
        }
    }
    if (dataSize < (phys_dev_ext_props.ray_tracing_props_khr.shaderGroupHandleSize * groupCount)) {
        skip |=
            LogError("VUID-vkGetRayTracingShaderGroupHandlesKHR-dataSize-02420", device, error_obj.location.dot(Field::dataSize),
                     "(%zu) must be at least "
                     "shaderGroupHandleSize (%" PRIu32 ") * groupCount (%" PRIu32 ").",
                     dataSize, phys_dev_ext_props.ray_tracing_props_khr.shaderGroupHandleSize, groupCount);
    }

    const uint32_t total_group_count = CalcTotalShaderGroupCount(pipeline_state);

    if (firstGroup >= total_group_count) {
        skip |= LogError("VUID-vkGetRayTracingShaderGroupHandlesKHR-firstGroup-04050", device,
                         error_obj.location.dot(Field::firstGroup),
                         "(%" PRIu32 ") must be less than the number of shader groups in pipeline (%" PRIu32 ").", firstGroup,
                         total_group_count);
    }
    if ((firstGroup + groupCount) > total_group_count) {
        skip |= LogError("VUID-vkGetRayTracingShaderGroupHandlesKHR-firstGroup-02419", device,
                         error_obj.location.dot(Field::firstGroup),
                         "(%" PRIu32 ") plus groupCount (%" PRIu32
                         ") must be less than or equal to the number "
                         "of shader groups in pipeline (%" PRIu32 ").",
                         firstGroup, groupCount, total_group_count);
    }
    return skip;
}

bool CoreChecks::PreCallValidateGetRayTracingCaptureReplayShaderGroupHandlesKHR(VkDevice device, VkPipeline pipeline,
                                                                                uint32_t firstGroup, uint32_t groupCount,
                                                                                size_t dataSize, void *pData,
                                                                                const ErrorObject &error_obj) const {
    bool skip = false;
    if (dataSize < (phys_dev_ext_props.ray_tracing_props_khr.shaderGroupHandleCaptureReplaySize * groupCount)) {
        skip |= LogError("VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-dataSize-03484", device,
                         error_obj.location.dot(Field::dataSize),
                         "(%zu) must be at least "
                         "shaderGroupHandleCaptureReplaySize (%" PRIu32 ") * groupCount (%" PRIu32 ").",
                         dataSize, phys_dev_ext_props.ray_tracing_props_khr.shaderGroupHandleCaptureReplaySize, groupCount);
    }
    auto pipeline_state = Get<vvl::Pipeline>(pipeline);
    if (!pipeline_state) {
        return skip;
    } else if (pipeline_state->pipeline_type != VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) {
        skip |= LogError("VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-pipeline-04620", pipeline,
                         error_obj.location.dot(Field::pipeline), "is a %s pipeline.",
                         string_VkPipelineBindPoint(pipeline_state->pipeline_type));
        return skip;
    }

    const auto &create_info = pipeline_state->GetCreateInfo<VkRayTracingPipelineCreateInfoKHR>();
    if (create_info.flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) {
        if (!enabled_features.pipelineLibraryGroupHandles) {
            skip |= LogError("VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-pipeline-07829", pipeline,
                             error_obj.location.dot(Field::pipeline),
                             "was created with %s, but the pipelineLibraryGroupHandles feature was not enabled.",
                             string_VkPipelineCreateFlags(create_info.flags).c_str());
        }
    }

    const uint32_t total_group_count = CalcTotalShaderGroupCount(*pipeline_state);

    if (firstGroup >= total_group_count) {
        skip |= LogError("VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-firstGroup-04051", device,
                         error_obj.location.dot(Field::firstGroup),
                         "(%" PRIu32
                         ") must be less than the number of shader "
                         "groups in pipeline (%" PRIu32 ").",
                         firstGroup, total_group_count);
    }
    if ((firstGroup + groupCount) > total_group_count) {
        skip |= LogError("VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-firstGroup-03483", device,
                         error_obj.location.dot(Field::firstGroup),
                         "(%" PRIu32 ") plus groupCount (%" PRIu32
                         ") must be less than or equal to the number of shader groups in pipeline (%" PRIu32 ").",
                         firstGroup, groupCount, total_group_count);
    }
    if (!(create_info.flags & VK_PIPELINE_CREATE_RAY_TRACING_SHADER_GROUP_HANDLE_CAPTURE_REPLAY_BIT_KHR)) {
        skip |= LogError("VUID-vkGetRayTracingCaptureReplayShaderGroupHandlesKHR-pipeline-03607", pipeline,
                         error_obj.location.dot(Field::pipeline), "was created with %s.",
                         string_VkPipelineCreateFlags(create_info.flags).c_str());
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdSetRayTracingPipelineStackSizeKHR(VkCommandBuffer commandBuffer, uint32_t pipelineStackSize,
                                                                     const ErrorObject &error_obj) const {
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    return ValidateExtendedDynamicState(*cb_state, error_obj.location, VK_TRUE, nullptr, nullptr);
}

bool CoreChecks::PreCallValidateGetRayTracingShaderGroupStackSizeKHR(VkDevice device, VkPipeline pipeline, uint32_t group,
                                                                     VkShaderGroupShaderKHR groupShader,
                                                                     const ErrorObject &error_obj) const {
    bool skip = false;
    auto pipeline_state = Get<vvl::Pipeline>(pipeline);
    if (pipeline_state) {
        if (pipeline_state->pipeline_type != VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR) {
            skip |= LogError("VUID-vkGetRayTracingShaderGroupStackSizeKHR-pipeline-04622", pipeline,
                             error_obj.location.dot(Field::pipeline), "is a %s pipeline.",
                             string_VkPipelineBindPoint(pipeline_state->pipeline_type));
        } else if (group >= pipeline_state->GetCreateInfo<VkRayTracingPipelineCreateInfoKHR>().groupCount) {
            skip |=
                LogError("VUID-vkGetRayTracingShaderGroupStackSizeKHR-group-03608", pipeline, error_obj.location.dot(Field::group),
                         "(%" PRIu32 ") must be less than the number of shader groups in pipeline (%" PRIu32 ").", group,
                         pipeline_state->GetCreateInfo<VkRayTracingPipelineCreateInfoKHR>().groupCount);
        }
    }
    return skip;
}

bool CoreChecks::ValidateGeometryTrianglesNV(const VkGeometryTrianglesNV &triangles, const Location &loc) const {
    bool skip = false;

    auto vb_state = Get<vvl::Buffer>(triangles.vertexData);
    if (vb_state != nullptr && vb_state->createInfo.size <= triangles.vertexOffset) {
        skip |= LogError("VUID-VkGeometryTrianglesNV-vertexOffset-02428", device, loc, "is invalid.");
    }

    auto ib_state = Get<vvl::Buffer>(triangles.indexData);
    if (ib_state != nullptr && ib_state->createInfo.size <= triangles.indexOffset) {
        skip |= LogError("VUID-VkGeometryTrianglesNV-indexOffset-02431", device, loc, "is invalid.");
    }

    auto td_state = Get<vvl::Buffer>(triangles.transformData);
    if (td_state != nullptr && td_state->createInfo.size <= triangles.transformOffset) {
        skip |= LogError("VUID-VkGeometryTrianglesNV-transformOffset-02437", device, loc, "is invalid.");
    }

    return skip;
}

bool CoreChecks::ValidateGeometryAABBNV(const VkGeometryAABBNV &aabbs, const Location &loc) const {
    bool skip = false;

    auto aabb_state = Get<vvl::Buffer>(aabbs.aabbData);
    if (aabb_state != nullptr && aabb_state->createInfo.size > 0 && aabb_state->createInfo.size <= aabbs.offset) {
        skip |= LogError("VUID-VkGeometryAABBNV-offset-02439", device, loc, "is invalid.");
    }

    return skip;
}

bool CoreChecks::ValidateGeometryNV(const VkGeometryNV &geometry, const Location &loc) const {
    bool skip = false;
    if (geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_NV) {
        skip = ValidateGeometryTrianglesNV(geometry.geometry.triangles, loc);
    } else if (geometry.geometryType == VK_GEOMETRY_TYPE_AABBS_NV) {
        skip = ValidateGeometryAABBNV(geometry.geometry.aabbs, loc);
    }
    return skip;
}

bool CoreChecks::ValidateRaytracingShaderBindingTable(VkCommandBuffer commandBuffer, const Location &table_loc,
                                                      const char *vuid_single_device_memory, const char *vuid_binding_table_flag,
                                                      const VkStridedDeviceAddressRegionKHR &binding_table) const {
    bool skip = false;

    if (binding_table.deviceAddress == 0 || binding_table.size == 0) {
        return skip;
    }

    const auto buffer_states = GetBuffersByAddress(binding_table.deviceAddress);
    if (buffer_states.empty()) {
        skip |= LogError("VUID-VkStridedDeviceAddressRegionKHR-size-04631", commandBuffer, table_loc.dot(Field::deviceAddress),
                         "(0x%" PRIx64 ") has no buffer associated with it.", binding_table.deviceAddress);
    } else {
        const sparse_container::range<VkDeviceSize> requested_range(binding_table.deviceAddress,
                                                                    binding_table.deviceAddress + binding_table.size - 1);
        using BUFFER_STATE_PTR = ValidationStateTracker::BUFFER_STATE_PTR;
        BufferAddressValidation<4> buffer_address_validator = {{{
            {vuid_single_device_memory, LogObjectList(commandBuffer),
             [this, commandBuffer, table_loc, vuid_single_device_memory](const BUFFER_STATE_PTR &buffer_state,
                                                                         std::string *out_error_msg) {
                 if (!out_error_msg) {
                     return !buffer_state->sparse && buffer_state->IsMemoryBound();
                 } else {
                     return ValidateMemoryIsBoundToBuffer(commandBuffer, *buffer_state, table_loc.dot(Field::deviceAddress),
                                                          vuid_single_device_memory);
                 }
             }},

            {vuid_binding_table_flag, LogObjectList(commandBuffer),
             [](const BUFFER_STATE_PTR &buffer_state, std::string *out_error_msg) {
                 if (!(static_cast<uint32_t>(buffer_state->usage) & VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR)) {
                     if (out_error_msg) {
                         *out_error_msg += "buffer has usage " + string_VkBufferUsageFlags2KHR(buffer_state->usage) + '\n';
                     }
                     return false;
                 }
                 return true;
             },
             []() {
                 return "The following buffers have not been created with the VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR "
                        "usage flag:\n";
             }},

            {"VUID-VkStridedDeviceAddressRegionKHR-size-04631", LogObjectList(commandBuffer),
             [&requested_range](const BUFFER_STATE_PTR &buffer_state, std::string *out_error_msg) {
                 const auto buffer_address_range = buffer_state->DeviceAddressRange();
                 if (!buffer_address_range.includes(requested_range)) {
                     if (out_error_msg) {
                         const std::string buffer_address_range_string = string_range_hex(buffer_address_range);
                         *out_error_msg += "buffer device address range is " + buffer_address_range_string + '\n';
                     }
                     return false;
                 }

                 return true;
             },
             [table_loc, requested_range_string = string_range_hex(requested_range)]() {
                 return "The following buffers do not include " + table_loc.Fields() + " buffer device address range " +
                        requested_range_string + ":\n";
             }},

            {"VUID-VkStridedDeviceAddressRegionKHR-size-04632", LogObjectList(commandBuffer),
             [&binding_table](const BUFFER_STATE_PTR &buffer_state, std::string *out_error_msg) {
                 if (binding_table.stride > buffer_state->createInfo.size) {
                     if (out_error_msg) {
                         *out_error_msg += "buffer size is " + std::to_string(buffer_state->createInfo.size) + '\n';
                     }
                     return false;
                 }
                 return true;
             },
             [table_loc, &binding_table]() {
                 return "The following buffers have a size inferior to " + table_loc.Fields() + "->stride (" +
                        std::to_string(binding_table.stride) + "):\n";
                 ;
             }},
        }}};

        skip |= buffer_address_validator.LogErrorsIfNoValidBuffer(*this, buffer_states, table_loc.dot(Field::deviceAddress),
                                                                  binding_table.deviceAddress);
    }

    return skip;
}

bool CoreChecks::ValidateDeferredOperation(VkDevice device, VkDeferredOperationKHR deferred_operation, const Location &loc,
                                           const char *vuid) const {
    // validate in core check because need to make sure it is a valid VkDeferredOperationKHR object
    bool skip = false;
    if (deferred_operation != VK_NULL_HANDLE) {
        VkResult result = DispatchGetDeferredOperationResultKHR(device, deferred_operation);
        if (result == VK_NOT_READY) {
            skip |= LogError(vuid, deferred_operation, loc.dot(Field::deferredOperation), "%s is not completed.",
                             FormatHandle(deferred_operation).c_str());
        }
    }
    return skip;
}