/* Copyright (c) 2015-2026 The Khronos Group Inc.
 * Copyright (c) 2015-2026 Valve Corporation
 * Copyright (c) 2015-2026 LunarG, Inc.
 * Copyright (C) 2015-2025 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 <string>

#include <vulkan/vk_enum_string_helper.h>
#include <vulkan/utility/vk_format_utils.h>
#include "containers/limits.h"
#include "core_validation.h"
#include "core_checks/cc_state_tracker.h"
#include "cc_buffer_address.h"
#include "error_message/logging.h"
#include "generated/error_location_helper.h"
#include "utils/ray_tracing_utils.h"
#include "utils/math_utils.h"
#include "state_tracker/ray_tracing_state.h"
#include "state_tracker/cmd_buffer_state.h"
#include "state_tracker/pipeline_state.h"
#include "error_message/error_strings.h"
#include "containers/container_utils.h"

#include <algorithm>

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;
    auto buffer_state = Get<vvl::Buffer>(pCreateInfo->buffer);
    ASSERT_AND_RETURN_SKIP(buffer_state);

    if (!(buffer_state->usage & VK_BUFFER_USAGE_2_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_VkBufferUsageFlags2(buffer_state->usage).c_str());
    }
    if (buffer_state->create_info.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->create_info.flags).c_str());
    }
    if (pCreateInfo->offset + pCreateInfo->size > buffer_state->create_info.size) {
        skip |= LogError("VUID-VkAccelerationStructureCreateInfoKHR-offset-03616", buffer_state->Handle(),
                         error_obj.location.dot(Field::pCreateInfo).dot(Field::offset),
                         "(%" PRIu64 ") + size (%" PRIu64 ") must be less than the size of buffer (%" PRIu64 ").",
                         pCreateInfo->offset, pCreateInfo->size, buffer_state->create_info.size);
    }

    if (device_state->physical_device_count > 1 && !enabled_features.bufferDeviceAddressMultiDevice &&
        !enabled_features.bufferDeviceAddressMultiDeviceEXT) {
        skip |= LogError("VUID-vkCreateAccelerationStructureKHR-device-03489", device, error_obj.location,
                         "device was created with multiple physical devices (%" PRIu32
                         "), but the "
                         "bufferDeviceAddressMultiDevice feature was not enabled.",
                         device_state->physical_device_count);
    }
    return skip;
}

bool CoreChecks::PreCallValidateGetAccelerationStructureBuildSizesKHR(
    VkDevice device, VkAccelerationStructureBuildTypeKHR buildType, const VkAccelerationStructureBuildGeometryInfoKHR *pBuildInfo,
    const uint32_t *pMaxPrimitiveCounts, VkAccelerationStructureBuildSizesInfoKHR *pSizeInfo, const ErrorObject &error_obj) const {
    bool skip = false;
    if (device_state->physical_device_count > 1 && !enabled_features.bufferDeviceAddressMultiDevice &&
        !enabled_features.bufferDeviceAddressMultiDeviceEXT) {
        skip |= LogError("VUID-vkGetAccelerationStructureBuildSizesKHR-device-03618", device, error_obj.location,
                         "device was created with multiple physical devices (%" PRIu32
                         "), but the "
                         "bufferDeviceAddressMultiDevice feature was not enabled.",
                         device_state->physical_device_count);
    }
    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);
        ASSERT_AND_CONTINUE(as_state);

        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 |=
                ValidateInsertMemoryRange(VulkanTypedHandle(info.accelerationStructure, kVulkanObjectTypeAccelerationStructureNV),
                                          *mem_info, info.memoryOffset, bind_info_loc);
            skip |= ValidateMemoryTypes(*mem_info, as_state->memory_requirements.memoryTypeBits,
                                        bind_info_loc.dot(Field::accelerationStructure),
                                        "VUID-VkBindAccelerationStructureMemoryInfoNV-memory-03622");
        }

        // Validate memory requirements alignment
        if (!IsIntegerMultipleOf(info.memoryOffset, as_state->memory_requirements.alignment)) {
            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->allocate_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->allocate_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;

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

    return skip;
}

bool CoreChecks::ValidateAccelerationStructuresMemoryAlisasing(const LogObjectList &objlist, uint32_t infoCount,
                                                               const VkAccelerationStructureBuildGeometryInfoKHR *pInfos,
                                                               uint32_t info_i, const ErrorObject &error_obj) const {
    using vvl::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);

    const bool info_in_mode_update = info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR;

    if (info_in_mode_update && info.srcAccelerationStructure != info.dstAccelerationStructure && src_as_state && dst_as_state) {
        const char *vuid = error_obj.location.function == Func::vkCmdBuildAccelerationStructuresKHR
                               ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03668"
                           : error_obj.location.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                               ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03668"
                               : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03668";
        skip |= ValidateAccelStructsMemoryDoNotOverlap(error_obj.location, objlist, *src_as_state,
                                                       info_i_loc.dot(Field::srcAccelerationStructure), *dst_as_state,
                                                       info_i_loc.dot(Field::dstAccelerationStructure), vuid);
    }

    // Loop on other acceleration structure builds info.
    // Given that comparisons are commutative, only need to consider elements after info_i
    assert(infoCount > info_i);
    for (auto [other_info_j, other_info] : vvl::enumerate(pInfos + info_i + 1, infoCount - (info_i + 1))) {
        // 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.

        const Location other_info_j_loc = error_obj.location.dot(Field::pInfos, other_info_j);

        const auto other_dst_as_state = Get<vvl::AccelerationStructureKHR>(other_info.dstAccelerationStructure);
        const auto other_src_as_state = Get<vvl::AccelerationStructureKHR>(other_info.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 && other_src_as_state) {
            if (other_info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
                const char *vuid = error_obj.location.function == Func::vkCmdBuildAccelerationStructuresKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03701"
                                   : error_obj.location.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-dstAccelerationStructure-03701"
                                       : "VUID-vkBuildAccelerationStructuresKHR-dstAccelerationStructure-03701";

                skip |= ValidateAccelStructsMemoryDoNotOverlap(error_obj.location, objlist, *dst_as_state,
                                                               info_i_loc.dot(Field::dstAccelerationStructure), *other_src_as_state,
                                                               other_info_j_loc.dot(Field::srcAccelerationStructure), vuid);
            }
        }

        // Validate that there is no destination acceleration structures' memory overlaps
        if (dst_as_state && other_dst_as_state) {
            const char *vuid = error_obj.location.function == Func::vkCmdBuildAccelerationStructuresKHR
                                   ? "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03702"
                               : error_obj.location.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                   ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-dstAccelerationStructure-03702"
                                   : "VUID-vkBuildAccelerationStructuresKHR-dstAccelerationStructure-03702";

            skip |= ValidateAccelStructsMemoryDoNotOverlap(error_obj.location, objlist, *dst_as_state,
                                                           info_i_loc.dot(Field::dstAccelerationStructure), *other_dst_as_state,
                                                           other_info_j_loc.dot(Field::dstAccelerationStructure), vuid);
        }
    }

    return skip;
}

bool CoreChecks::ValidateAccelerationStructuresDeviceScratchBufferMemoryAliasing(
    VkCommandBuffer cmd_buffer, uint32_t info_count, const VkAccelerationStructureBuildGeometryInfoKHR *p_infos,
    const VkAccelerationStructureBuildRangeInfoKHR *const *pp_range_infos, const Location &loc) const {
    assert(loc.function != Func::vkBuildAccelerationStructuresKHR);

    bool skip = false;

    enum class AddressRangeOrigin : uint8_t { Undefined, SrcAccelStruct, DstAccelStruct, Scratch };
    struct AddressRange {
        vvl::range<VkDeviceAddress> range = {};
        uint32_t info_i = 0;
        AddressRangeOrigin origin = AddressRangeOrigin::Undefined;
    };
    // Gather all address ranges  from source acceleration structtures, destination acceleration structures
    // and scratch buffers in a single sorted vector, to more rapidly lookup overlaps
    std::vector<AddressRange> address_ranges;
    address_ranges.reserve(3 * info_count);

    const auto insert_address = [](std::vector<AddressRange> &address_ranges, const AddressRange address_range) {
        std::optional<AddressRange> overlapped_range = std::nullopt;
        auto insert_pos =
            std::lower_bound(address_ranges.begin(), address_ranges.end(), address_range,
                             [&overlapped_range](const AddressRange iter, const AddressRange new_elt) -> bool {
                                 const vvl::range<VkDeviceAddress> intersection = iter.range & new_elt.range;
                                 if (intersection.non_empty()) {
                                     const bool both_src_as_ranges = iter.origin == AddressRangeOrigin::SrcAccelStruct &&
                                                                     new_elt.origin == AddressRangeOrigin::SrcAccelStruct;
                                     const bool as_update =
                                         iter.info_i == new_elt.info_i && ((iter.origin == AddressRangeOrigin::SrcAccelStruct &&
                                                                            new_elt.origin == AddressRangeOrigin::DstAccelStruct) ||
                                                                           (new_elt.origin == AddressRangeOrigin::SrcAccelStruct &&
                                                                            iter.origin == AddressRangeOrigin::DstAccelStruct));

                                     if (!both_src_as_ranges && !as_update) {
                                         overlapped_range = iter;
                                     }
                                 }

                                 return iter.range < new_elt.range;
                             });

        address_ranges.insert(insert_pos, address_range);
        return overlapped_range;
    };

    for (const auto [info_i, info] : vvl::enumerate(p_infos, info_count)) {
        const Location info_i_loc = loc.dot(Field::pInfos, info_i);

        const auto dst_as_state = Get<vvl::AccelerationStructureKHR>(info.dstAccelerationStructure);

        if (const auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure);
            src_as_state && info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
            if (src_as_state->buffer_state && !src_as_state->buffer_state->sparse) {
                const vvl::range<VkDeviceAddress> src_as_range = src_as_state->GetDeviceAddressRange();

                if (dst_as_state && dst_as_state->VkHandle() != src_as_state->VkHandle()) {
                    const vvl::range<VkDeviceAddress> dst_as_range = dst_as_state->GetDeviceAddressRange();

                    if (const vvl::range<VkDeviceAddress> dst_as_src_as_intersection = dst_as_range & src_as_range;
                        dst_as_src_as_intersection.non_empty()) {
                        skip |=
                            LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03668",
                                     LogObjectList(cmd_buffer, src_as_state->VkHandle(), dst_as_state->VkHandle()),
                                     info_i_loc.dot(Field::dstAccelerationStructure),
                                     "overlaps with srcAccelerationStructure, which is in update mode, on device address range %s.",
                                     string_range_hex(dst_as_src_as_intersection).c_str());
                    }
                }

                const AddressRange src_as_address_range = {src_as_range, info_i, AddressRangeOrigin::SrcAccelStruct};
                const std::optional<AddressRange> overlapped_address_range = insert_address(address_ranges, src_as_address_range);
                if (overlapped_address_range.has_value()) {
                    switch (overlapped_address_range->origin) {
                        case AddressRangeOrigin::SrcAccelStruct: {
                            // Valid overlap, source acceleration structures being read only
                            break;
                        }
                        case AddressRangeOrigin::DstAccelStruct: {
                            if (const auto other_dst_as_state = Get<vvl::AccelerationStructureKHR>(
                                    p_infos[overlapped_address_range->info_i].dstAccelerationStructure)) {
                                const vvl::range<VkDeviceAddress> src_as_other_dst_as_intersection =
                                    src_as_address_range.range & overlapped_address_range->range;

                                skip |= LogError(
                                    "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03701",
                                    LogObjectList(cmd_buffer, src_as_state->VkHandle(), other_dst_as_state->VkHandle()),
                                    info_i_loc.dot(Field::srcAccelerationStructure),
                                    "is in update mode, but overlaps with dstAccelerationStructure of pInfos[%" PRIu32
                                    "] on device address range %s.",
                                    overlapped_address_range->info_i, string_range_hex(src_as_other_dst_as_intersection).c_str());
                            }
                            break;
                        }
                        case AddressRangeOrigin::Scratch: {
                            const vvl::range<VkDeviceAddress> src_as_other_scratch_intersection =
                                src_as_address_range.range & overlapped_address_range->range;
                            skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-scratchData-03705",
                                             LogObjectList(cmd_buffer, src_as_state->VkHandle()),
                                             info_i_loc.dot(Field::srcAccelerationStructure),
                                             "overlaps with scratch buffer of pInfos[%" PRIu32 "] on device address range %s.",
                                             overlapped_address_range->info_i,
                                             string_range_hex(src_as_other_scratch_intersection).c_str());
                            break;
                        }
                        case AddressRangeOrigin::Undefined:
                            assert(false);
                            break;
                    }
                }
            }
        }

        if (dst_as_state) {
            if (dst_as_state->buffer_state && !dst_as_state->buffer_state->sparse) {
                const AddressRange dst_as_address_range = {dst_as_state->GetDeviceAddressRange(), info_i,
                                                           AddressRangeOrigin::DstAccelStruct};
                const std::optional<AddressRange> overlapped_address_range = insert_address(address_ranges, dst_as_address_range);
                if (overlapped_address_range.has_value()) {
                    switch (overlapped_address_range->origin) {
                        case AddressRangeOrigin::SrcAccelStruct: {
                            if (const auto other_src_as_state = Get<vvl::AccelerationStructureKHR>(
                                    p_infos[overlapped_address_range->info_i].srcAccelerationStructure)) {
                                const vvl::range<VkDeviceAddress> dst_as_other_src_as_intersection =
                                    dst_as_address_range.range & overlapped_address_range->range;

                                skip |= LogError(
                                    "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03701",
                                    LogObjectList(cmd_buffer, dst_as_state->VkHandle(), other_src_as_state->VkHandle()),
                                    info_i_loc.dot(Field::dstAccelerationStructure),
                                    "overlaps with srcAccelerationStructure of pInfos[%" PRIu32
                                    "], which is in update mode, on device address range %s.",
                                    overlapped_address_range->info_i, string_range_hex(dst_as_other_src_as_intersection).c_str());
                            }
                            break;
                        }
                        case AddressRangeOrigin::DstAccelStruct: {
                            if (const auto other_dst_as_state = Get<vvl::AccelerationStructureKHR>(
                                    p_infos[overlapped_address_range->info_i].dstAccelerationStructure)) {
                                const vvl::range<VkDeviceAddress> dst_as_other_dst_as_intersection =
                                    dst_as_address_range.range & overlapped_address_range->range;

                                skip |= LogError(
                                    "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03702",
                                    LogObjectList(cmd_buffer, dst_as_state->VkHandle(), other_dst_as_state->VkHandle()),
                                    info_i_loc.dot(Field::dstAccelerationStructure),
                                    "overlaps with dstAccelerationStructure of pInfos[%" PRIu32 "], on device address range %s.",
                                    overlapped_address_range->info_i, string_range_hex(dst_as_other_dst_as_intersection).c_str());
                            }
                            break;
                        }
                        case AddressRangeOrigin::Scratch: {
                            const vvl::range<VkDeviceAddress> src_as_other_scratch_intersection =
                                dst_as_address_range.range & overlapped_address_range->range;
                            skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03703",
                                             LogObjectList(cmd_buffer, dst_as_state->VkHandle()),
                                             info_i_loc.dot(Field::dstAccelerationStructure),
                                             "overlaps with scratch buffer of pInfos[%" PRIu32 "] on device address range %s.",
                                             overlapped_address_range->info_i,
                                             string_range_hex(src_as_other_scratch_intersection).c_str());
                            break;
                        }
                        case AddressRangeOrigin::Undefined:
                            assert(false);
                            break;
                    }
                }
            }
        }

        // Do not attempt looking for memory overlaps here if any buffer associated to scratch address is sparse
        const auto scratch_buffers = GetBuffersByAddress(info.scratchData.deviceAddress);
        const bool any_scratch_sparse = std::any_of(scratch_buffers.begin(), scratch_buffers.end(),
                                                    [](const vvl::Buffer *buffer) { return buffer && buffer->sparse; });
        if (!any_scratch_sparse) {
            const VkDeviceSize assumed_scratch_size =
                rt::ComputeScratchSize(rt::BuildType::Device, device, info, pp_range_infos[info_i]);

            const vvl::range<VkDeviceAddress> scratch_range = {info.scratchData.deviceAddress,
                                                               info.scratchData.deviceAddress + assumed_scratch_size};

            const AddressRange scratch_address_range = {scratch_range, info_i, AddressRangeOrigin::Scratch};
            const std::optional<AddressRange> overlapped_address_range = insert_address(address_ranges, scratch_address_range);
            if (overlapped_address_range.has_value()) {
                switch (overlapped_address_range->origin) {
                    case AddressRangeOrigin::SrcAccelStruct: {
                        if (const auto other_src_as_state = Get<vvl::AccelerationStructureKHR>(
                                p_infos[overlapped_address_range->info_i].srcAccelerationStructure)) {
                            const vvl::range<VkDeviceAddress> scratch_other_src_as_intersection =
                                scratch_address_range.range & overlapped_address_range->range;

                            skip |= LogError(
                                "VUID-vkCmdBuildAccelerationStructuresKHR-scratchData-03705",
                                LogObjectList(cmd_buffer, other_src_as_state->VkHandle()), info_i_loc.dot(Field::scratchData),
                                "overlaps with srcAccelerationStructure of pInfos[%" PRIu32
                                "], which is in update mode, on device address range %s.",
                                overlapped_address_range->info_i, string_range_hex(scratch_other_src_as_intersection).c_str());
                        }
                        break;
                    }
                    case AddressRangeOrigin::DstAccelStruct: {
                        if (const auto other_dst_as_state = Get<vvl::AccelerationStructureKHR>(
                                p_infos[overlapped_address_range->info_i].dstAccelerationStructure)) {
                            const vvl::range<VkDeviceAddress> dst_as_other_dst_as_intersection =
                                scratch_address_range.range & overlapped_address_range->range;

                            skip |= LogError(
                                "VUID-vkCmdBuildAccelerationStructuresKHR-dstAccelerationStructure-03703",
                                LogObjectList(cmd_buffer, other_dst_as_state->VkHandle()), info_i_loc.dot(Field::scratchData),
                                "overlaps with dstAccelerationStructure of pInfos[%" PRIu32 "], on device address range %s.",
                                overlapped_address_range->info_i, string_range_hex(dst_as_other_dst_as_intersection).c_str());
                        }
                        break;
                    }
                    case AddressRangeOrigin::Scratch: {
                        const vvl::range<VkDeviceAddress> src_as_other_scratch_intersection =
                            scratch_address_range.range & overlapped_address_range->range;
                        skip |=
                            LogError("VUID-vkCmdBuildAccelerationStructuresKHR-scratchData-03704", LogObjectList(cmd_buffer),
                                     info_i_loc.dot(Field::scratchData),
                                     "overlaps with scratch buffer of pInfos[%" PRIu32 "] on device address range %s.",
                                     overlapped_address_range->info_i, string_range_hex(src_as_other_scratch_intersection).c_str());
                        break;
                    }
                    case AddressRangeOrigin::Undefined:
                        assert(false);
                        break;
                }
            }
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateGetAccelerationStructureDeviceAddressKHR(VkDevice device,
                                                                         const VkAccelerationStructureDeviceAddressInfoKHR *pInfo,
                                                                         const ErrorObject &error_obj) const {
    bool skip = false;
    if (!enabled_features.accelerationStructure) {
        skip |= LogError("VUID-vkGetAccelerationStructureDeviceAddressKHR-accelerationStructure-08935", device, error_obj.location,
                         "accelerationStructure feature was not enabled.");
    }
    if (device_state->physical_device_count > 1 && !enabled_features.bufferDeviceAddressMultiDevice &&
        !enabled_features.bufferDeviceAddressMultiDeviceEXT) {
        skip |= LogError("VUID-vkGetAccelerationStructureDeviceAddressKHR-device-03504", device, error_obj.location,
                         "device was created with multiple physical devices (%" PRIu32
                         "), but the "
                         "bufferDeviceAddressMultiDevice feature was not enabled.",
                         device_state->physical_device_count);
    }

    if (const auto accel_struct = Get<vvl::AccelerationStructureKHR>(pInfo->accelerationStructure)) {
        const Location info_loc = error_obj.location.dot(Field::pInfo);
        skip |= ValidateMemoryIsBoundToBuffer(device, *accel_struct->buffer_state,
                                              info_loc.dot(Field::accelerationStructure).dot(Field::buffer),
                                              "VUID-vkGetAccelerationStructureDeviceAddressKHR-pInfo-09541");

        if (!(accel_struct->buffer_state->usage & VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT)) {
            skip |= LogError("VUID-vkGetAccelerationStructureDeviceAddressKHR-pInfo-09542", LogObjectList(device),
                             info_loc.dot(Field::accelerationStructure).dot(Field::buffer), "was created with usage flag(s) %s.",
                             string_VkBufferUsageFlags2(accel_struct->buffer_state->usage).c_str());
        }
    }

    return skip;
}

bool CoreChecks::ValidateAccelerationVertex(VkFormat vertex_format, VkDeviceOrHostAddressConstKHR vertex_data,
                                            VkDeviceSize vertex_stride, const LogObjectList& objlist, const Location& loc) const {
    bool skip = false;

    uint32_t format_alignment = 0;
    const bool is_packed = vkuFormatIsPacked(vertex_format);
    if (is_packed) {
        format_alignment = vkuFormatTexelBlockSize(vertex_format);
    } else {
        uint32_t min_component_bits_size = vvl::kU32Max;
        const VKU_FORMAT_INFO format_info = vkuGetFormatInfo(vertex_format);
        for (uint32_t component_i = 0; component_i < format_info.component_count; ++component_i) {
            min_component_bits_size = std::min(format_info.components[component_i].size, min_component_bits_size);
        }
        format_alignment = min_component_bits_size / 8;
    }

    if (!IsPointerAligned(vertex_data.deviceAddress, format_alignment)) {
        const char* vuid = loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                               ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03711"
                               : "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03711";
        skip |= LogError(vuid, objlist, loc.dot(Field::vertexData).dot(Field::deviceAddress),
                         "(0x%" PRIx64 ") is not aligned to the %s (%" PRIu32 ") of its corresponding vertexFormat (%s).",
                         vertex_data.deviceAddress, is_packed ? "texel block size" : "minimum component byte size",
                         format_alignment, string_VkFormat(vertex_format));
    }
    if (!IsIntegerMultipleOf(vertex_stride, format_alignment)) {
        const char* vuid = loc.structure == Struct::VkAccelerationStructureGeometrySpheresDataNV
                               ? "VUID-VkAccelerationStructureGeometrySpheresDataNV-vertexStride-10431"
                           : loc.structure == Struct::VkAccelerationStructureGeometryLinearSweptSpheresDataNV
                               ? "VUID-VkAccelerationStructureGeometryLinearSweptSpheresDataNV-vertexStride-10421"
                               : "VUID-VkAccelerationStructureGeometryTrianglesDataKHR-vertexStride-03735";
        skip |= LogError(vuid, objlist, loc.dot(Field::vertexStride),
                         "(%" PRIu64 ") is not a multiple to the %s (%" PRIu32 ") of its corresponding vertexFormat (%s).",
                         vertex_stride, is_packed ? "texel block size" : "minimum component byte size", format_alignment,
                         string_VkFormat(vertex_format));
    }

    return skip;
}

bool CoreChecks::ValidateAccelerationBuffers(VkCommandBuffer cmd_buffer, uint32_t info_i,
                                             const VkAccelerationStructureBuildGeometryInfoKHR &info,
                                             const VkAccelerationStructureBuildRangeInfoKHR *geometry_build_ranges,
                                             const Location &info_loc) const {
    bool skip = false;

    const auto pick_vuid = [&info_loc](const char *direct_build_vu, const char *indirect_build_vu) -> const char * {
        return info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR ? direct_build_vu : indirect_build_vu;
    };

    auto buffer_check = [this, &pick_vuid, cmd_buffer](const VkDeviceOrHostAddressConstKHR address, const Location& loc) -> bool {
        BufferAddressValidation<1> buffer_address_validator = {
            {{{pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-geometry-03673",
                         "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-geometry-03673"),
               [](const vvl::Buffer& buffer_state) {
                   return (buffer_state.usage & VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR) == 0;
               },
               []() {
                   return "The following buffers are missing "
                          "VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR";
               },
               kUsageErrorMsgBuffer}}}};

        return buffer_address_validator.ValidateDeviceAddress(*this, loc.dot(Field::deviceAddress), LogObjectList(cmd_buffer),
                                                              address.deviceAddress);
    };

    const LogObjectList cb_objlist(cmd_buffer);
    const Location pp_build_range_info_loc(info_loc.function, Field::ppBuildRangeInfos, info_i);
    for (uint32_t geom_i = 0; geom_i < info.geometryCount; ++geom_i) {
        const Location p_geom_loc = info_loc.dot(info.pGeometries ? Field::pGeometries : Field::ppGeometries, geom_i);
        const Location p_geom_geom_loc = p_geom_loc.dot(Field::geometry);
        const VkAccelerationStructureGeometryKHR &geom_data = rt::GetGeometry(info, geom_i);
        const uint32_t geometry_build_range_primitive_count =
            geometry_build_ranges ? geometry_build_ranges[geom_i].primitiveCount : 0;

        if (geom_data.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR) {
            const Location p_geom_geom_triangles_loc = p_geom_geom_loc.dot(Field::triangles);
            const auto& triangles = geom_data.geometry.triangles;

            if (geometry_build_range_primitive_count > 0) {
                if (triangles.vertexData.deviceAddress == 0) {
                    skip |=
                        LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03804",
                                           "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03804"),
                                 cmd_buffer, p_geom_geom_triangles_loc.dot(Field::vertexData).dot(Field::deviceAddress), "is zero");
                }
                skip |= buffer_check(triangles.vertexData, p_geom_geom_triangles_loc.dot(Field::vertexData));
            }

            if (triangles.indexType != VK_INDEX_TYPE_NONE_KHR) {
                if (geometry_build_range_primitive_count > 0) {
                    if (triangles.indexData.deviceAddress == 0) {
                        skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03806",
                                                   "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03806"),
                                         cmd_buffer, p_geom_geom_triangles_loc.dot(Field::indexData).dot(Field::deviceAddress),
                                         "is zero");
                    }

                    skip |= buffer_check(triangles.indexData, p_geom_geom_triangles_loc.dot(Field::indexData));
                }

                if (info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR &&
                    info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
                    const auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure);
                    if (src_as_state && src_as_state->build_info_khr.has_value()) {
                        if (geom_i < src_as_state->build_range_infos.size()) {
                            if (const uint32_t recorded_primitive_count = src_as_state->build_range_infos[geom_i].primitiveCount;
                                recorded_primitive_count != geometry_build_range_primitive_count) {
                                const LogObjectList objlist(cmd_buffer, info.srcAccelerationStructure);
                                skip |=
                                    LogError("VUID-vkCmdBuildAccelerationStructuresKHR-primitiveCount-03769", objlist, p_geom_loc,
                                             " has corresponding VkAccelerationStructureBuildRangeInfoKHR %s[%" PRIu32
                                             "], but this build range has its primitiveCount member set to (%" PRIu32
                                             ") when it was last specified as (%" PRIu32 ").",
                                             pp_build_range_info_loc.Fields().c_str(), geom_i, geometry_build_range_primitive_count,
                                             recorded_primitive_count);
                            }
                        }
                    }
                }
            }

            const VkFormatProperties3 format_properties = GetPDFormatProperties(triangles.vertexFormat);
            if (!(format_properties.bufferFeatures & VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR)) {
                skip |= LogError("VUID-VkAccelerationStructureGeometryTrianglesDataKHR-vertexFormat-03797", cmd_buffer,
                                 p_geom_geom_triangles_loc.dot(Field::vertexFormat),
                                 "is %s which doesn't support VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR.\n"
                                 "(supported bufferFeatures: %s)",
                                 string_VkFormat(triangles.vertexFormat),
                                 string_VkFormatFeatureFlags2(format_properties.bufferFeatures).c_str());
            } else {
                // Only try to get format info if vertex format is valid
                skip |= ValidateAccelerationVertex(triangles.vertexFormat, triangles.vertexData, triangles.vertexStride, cb_objlist,
                                                   p_geom_geom_triangles_loc);
            }

            if (triangles.transformData.deviceAddress != 0 && geometry_build_range_primitive_count > 0) {
                skip |= buffer_check(triangles.transformData, p_geom_geom_triangles_loc.dot(Field::transformData));
            }

            if (const auto *micromap =
                    vku::FindStructInPNextChain<VkAccelerationStructureTrianglesOpacityMicromapEXT>(triangles.pNext)) {
                if (micromap->indexType == VK_INDEX_TYPE_NONE_KHR) {
                    if (!micromap->indexBuffer.deviceAddress) {
                        skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-10904", device,
                                         p_geom_geom_triangles_loc
                                             .pNext(Struct::VkAccelerationStructureTrianglesOpacityMicromapEXT, Field::indexBuffer)
                                             .dot(Field::deviceAddress),
                                         "is 0x%" PRIx64 " but indexType is VK_INDEX_TYPE_NONE_KHR.",
                                         micromap->indexBuffer.deviceAddress);
                    }
                } else {
                    skip |= ValidateDeviceAddress(
                        p_geom_geom_triangles_loc
                            .pNext(Struct::VkAccelerationStructureTrianglesOpacityMicromapEXT, Field::indexBuffer)
                            .dot(Field::deviceAddress),
                        cb_objlist, micromap->indexBuffer.deviceAddress);
                }
            }
        } else if (geom_data.geometryType == VK_GEOMETRY_TYPE_INSTANCES_KHR) {
            if (geometry_build_range_primitive_count > 0) {
                const Location instances_loc = p_geom_geom_loc.dot(Field::instances);
                const Location instances_data_loc = instances_loc.dot(Field::data);
                const auto& instances = geom_data.geometry.instances;
                if (instances.data.deviceAddress == 0) {
                    skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03813",
                                               "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03813"),
                                     cmd_buffer, instances_data_loc.dot(Field::deviceAddress), "is zero");
                }

                skip |= buffer_check(instances.data, instances_data_loc);
            }
        } else if (geom_data.geometryType == VK_GEOMETRY_TYPE_AABBS_KHR) {
            if (geometry_build_range_primitive_count > 0) {
                const Location aabbs_loc = p_geom_geom_loc.dot(Field::aabbs);
                const Location aabbs_data_loc = aabbs_loc.dot(Field::data);
                const auto& aabbs = geom_data.geometry.aabbs;

                if (aabbs.data.deviceAddress == 0) {
                    skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03811",
                                               "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03811"),
                                     cmd_buffer, aabbs_data_loc.dot(Field::deviceAddress), "is zero");
                }

                skip |= buffer_check(aabbs.data, aabbs_data_loc);
            }
        } else if (geom_data.geometryType == VK_GEOMETRY_TYPE_SPHERES_NV) {
            const Location p_geom_geom_spheres_loc = p_geom_geom_loc.pNext(Struct::VkAccelerationStructureGeometrySpheresDataNV);
            auto sphere_struct = reinterpret_cast<VkAccelerationStructureGeometrySpheresDataNV const *>(geom_data.pNext);
            ASSERT_AND_RETURN_SKIP(sphere_struct);

            if (geometry_build_range_primitive_count > 0) {
                if (sphere_struct->indexType == VK_INDEX_TYPE_NONE_KHR) {
                    if (sphere_struct->indexData.deviceAddress != 0) {
                        skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11846",
                                                   "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11846"),
                                         cmd_buffer, p_geom_geom_spheres_loc.dot(Field::indexData).dot(Field::deviceAddress),
                                         "(0x%" PRIx64 ") is not 0 when indexType is VK_INDEX_TYPE_NONE_KHR.",
                                         sphere_struct->indexData.deviceAddress);
                    }
                } else {
                    if (sphere_struct->indexData.deviceAddress == 0) {
                        skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11847",
                                                   "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11847"),
                                         cmd_buffer, p_geom_geom_spheres_loc.dot(Field::indexData).dot(Field::deviceAddress),
                                         "is zero");
                    }
                    skip |= buffer_check(sphere_struct->indexData, p_geom_geom_spheres_loc.dot(Field::indexData));
                }
                if (sphere_struct->vertexData.deviceAddress == 0) {
                    skip |=
                        LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11848",
                                           "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11848"),
                                 cmd_buffer, p_geom_geom_spheres_loc.dot(Field::vertexData).dot(Field::deviceAddress), "is zero");
                }
                skip |= buffer_check(sphere_struct->vertexData, p_geom_geom_spheres_loc.dot(Field::vertexData));

                if (sphere_struct->radiusData.deviceAddress == 0) {
                    skip |=
                        LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11849",
                                           "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11849"),
                                 cmd_buffer, p_geom_geom_spheres_loc.dot(Field::radiusData).dot(Field::deviceAddress), "is zero");
                }
                skip |= buffer_check(sphere_struct->radiusData, p_geom_geom_spheres_loc.dot(Field::radiusData));
            }

            const VkFormatProperties3KHR vertex_properties = GetPDFormatProperties(sphere_struct->vertexFormat);
            if (!(vertex_properties.bufferFeatures & VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR)) {
                skip |= LogError("VUID-VkAccelerationStructureGeometrySpheresDataNV-vertexFormat-10434", cmd_buffer,
                                 p_geom_geom_spheres_loc.dot(Field::vertexFormat),
                                 "is %s which doesn't support VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR.\n"
                                 "(supported bufferFeatures: %s)",
                                 string_VkFormat(sphere_struct->vertexFormat),
                                 string_VkFormatFeatureFlags2(vertex_properties.bufferFeatures).c_str());
            } else {
                // Only try to get format info if vertex format is valid
                skip |= ValidateAccelerationVertex(sphere_struct->vertexFormat, sphere_struct->vertexData,
                                                   sphere_struct->vertexStride, cb_objlist, p_geom_geom_spheres_loc);
            }

            const VkFormatProperties3KHR radius_properties = GetPDFormatProperties(sphere_struct->radiusFormat);
            if (!(radius_properties.bufferFeatures & VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_RADIUS_BUFFER_BIT_NV)) {
                skip |= LogError("VUID-VkAccelerationStructureGeometrySpheresDataNV-radiusFormat-10435", cmd_buffer,
                                 p_geom_geom_spheres_loc.dot(Field::radiusFormat),
                                 "is %s which doesn't support VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_RADIUS_BUFFER_BIT_NV.\n"
                                 "(supported bufferFeatures: %s)",
                                 string_VkFormat(sphere_struct->radiusFormat),
                                 string_VkFormatFeatureFlags2(radius_properties.bufferFeatures).c_str());
            }
        } else if (geom_data.geometryType == VK_GEOMETRY_TYPE_LINEAR_SWEPT_SPHERES_NV) {
            const Location p_geom_geom_linear_spheres_loc =
                p_geom_geom_loc.pNext(Struct::VkAccelerationStructureGeometryLinearSweptSpheresDataNV);
            auto sphere_linear_struct =
                reinterpret_cast<VkAccelerationStructureGeometryLinearSweptSpheresDataNV const *>(geom_data.pNext);
            ASSERT_AND_RETURN_SKIP(sphere_linear_struct);

            if (geometry_build_range_primitive_count > 0) {
                if (sphere_linear_struct->indexType == VK_INDEX_TYPE_NONE_KHR) {
                    if (sphere_linear_struct->indexData.deviceAddress != 0) {
                        skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11850",
                                                   "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11850"),
                                         cmd_buffer, p_geom_geom_linear_spheres_loc.dot(Field::indexData).dot(Field::deviceAddress),
                                         "(0x%" PRIx64 ") is not 0 when indexType is VK_INDEX_TYPE_NONE_KHR.",
                                         sphere_linear_struct->indexData.deviceAddress);
                    }
                } else {
                    if (sphere_linear_struct->indexData.deviceAddress == 0) {
                        skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11851",
                                                   "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11851"),
                                         cmd_buffer, p_geom_geom_linear_spheres_loc.dot(Field::indexData).dot(Field::deviceAddress),
                                         "is zero");
                    }
                    skip |= buffer_check(sphere_linear_struct->indexData, p_geom_geom_linear_spheres_loc.dot(Field::indexData));
                }
                if (sphere_linear_struct->vertexData.deviceAddress == 0) {
                    skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11852",
                                               "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11852"),
                                     cmd_buffer, p_geom_geom_linear_spheres_loc.dot(Field::vertexData).dot(Field::deviceAddress),
                                     "is zero");
                }
                skip |= buffer_check(sphere_linear_struct->vertexData, p_geom_geom_linear_spheres_loc.dot(Field::vertexData));

                if (sphere_linear_struct->radiusData.deviceAddress == 0) {
                    skip |= LogError(pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-11853",
                                               "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-11853"),
                                     cmd_buffer, p_geom_geom_linear_spheres_loc.dot(Field::radiusData).dot(Field::deviceAddress),
                                     "is zero");
                }
                skip |= buffer_check(sphere_linear_struct->radiusData, p_geom_geom_linear_spheres_loc.dot(Field::radiusData));
            }
            const VkFormatProperties3KHR vertex_properties = GetPDFormatProperties(sphere_linear_struct->vertexFormat);
            if (!(vertex_properties.bufferFeatures & VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR)) {
                skip |= LogError("VUID-VkAccelerationStructureGeometryLinearSweptSpheresDataNV-vertexFormat-10423", cmd_buffer,
                                 p_geom_geom_linear_spheres_loc.dot(Field::vertexFormat),
                                 "is %s which doesn't support VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR.\n"
                                 "(supported bufferFeatures: %s)",
                                 string_VkFormat(sphere_linear_struct->vertexFormat),
                                 string_VkFormatFeatureFlags2(vertex_properties.bufferFeatures).c_str());
            } else {
                // Only try to get format info if vertex format is valid
                skip |= ValidateAccelerationVertex(sphere_linear_struct->vertexFormat, sphere_linear_struct->vertexData,
                                                   sphere_linear_struct->vertexStride, cb_objlist, p_geom_geom_linear_spheres_loc);
            }

            const VkFormatProperties3KHR radius_properties = GetPDFormatProperties(sphere_linear_struct->radiusFormat);
            if (!(radius_properties.bufferFeatures & VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_RADIUS_BUFFER_BIT_NV)) {
                skip |= LogError("VUID-VkAccelerationStructureGeometryLinearSweptSpheresDataNV-radiusFormat-10424", cmd_buffer,
                                 p_geom_geom_linear_spheres_loc.dot(Field::radiusFormat),
                                 "is %s which doesn't support VK_FORMAT_FEATURE_2_ACCELERATION_STRUCTURE_RADIUS_BUFFER_BIT_NV.\n"
                                 "(supported bufferFeatures: %s)",
                                 string_VkFormat(sphere_linear_struct->radiusFormat),
                                 string_VkFormatFeatureFlags2(radius_properties.bufferFeatures).c_str());
            }

            if (sphere_linear_struct->indexingMode == VK_RAY_TRACING_LSS_INDEXING_MODE_SUCCESSIVE_NV) {
                if (!sphere_linear_struct->indexData.deviceAddress && !sphere_linear_struct->indexData.hostAddress) {
                    skip |= LogError("VUID-VkAccelerationStructureGeometryLinearSweptSpheresDataNV-indexingMode-10427", cmd_buffer,
                                     p_geom_geom_linear_spheres_loc.dot(Field::indexData),
                                     "shouldn't be NUll if indexing mode is VK_RAY_TRACING_LSS_INDEXING_MODE_SUCCESSIVE_NV.");
                }
            }
        }
    }

    if (info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR &&
        !(info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR &&
          (info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR))) {
        if (const auto dst_as_state = Get<vvl::AccelerationStructureKHR>(info.dstAccelerationStructure)) {
            const VkDeviceSize as_minimum_size =
                rt::ComputeAccelerationStructureSize(rt::BuildType::Device, device, info, geometry_build_ranges);
            if (dst_as_state->create_info.size < as_minimum_size) {
                const LogObjectList objlist(cmd_buffer, info.dstAccelerationStructure);
                skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-10126", objlist,
                                 info_loc.dot(Field::dstAccelerationStructure),
                                 " was created with size (%" PRIu64
                                 "), but an acceleration structure build with corresponding ppBuildRangeInfos[%" PRIu32
                                 "] requires a minimum size of (%" PRIu64 ").",
                                 dst_as_state->create_info.size, info_i, as_minimum_size);
            }
        }
    }
    const VkDeviceSize scratch_size = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                          ? rt::ComputeScratchSize(rt::BuildType::Device, device, info, geometry_build_ranges)
                                          : 1;
    if (scratch_size > 0) {
        if (info.scratchData.deviceAddress == 0) {
            const char *scratch_address_range_vuid =
                info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR
                    ? pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-12261",
                                "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-12261")
                    : pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-12260",
                                "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-12260");
            skip |=
                LogError(scratch_address_range_vuid, device, info_loc.dot(Field::scratchData).dot(Field::deviceAddress), "is zero");
        } else {
            // Hardcoded value of 1 for indirect calls because scratch size cannot be computed on the CPU in this case
            // (need to access build ranges). Easier to hardcode than to add the logic to not perform scratch buffer size
            // validation for indirect calls.

            const vvl::range<VkDeviceSize> scratch_address_range(info.scratchData.deviceAddress,
                                                                 info.scratchData.deviceAddress + scratch_size);
            const char *scratch_address_range_vuid =
                info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR
                    ? pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-12258",
                                "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-12258")
                    : pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-12259",
                                "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-12259");

            const char *scratch_buffer_has_storage_flag_vuid =
                info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR
                    ? pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-12260",
                                "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-12260")
                    : pick_vuid("VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-12261",
                                "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-12261");

            BufferAddressValidation<2> buffer_address_validator = {
                {{{scratch_buffer_has_storage_flag_vuid,
                   [](const vvl::Buffer &buffer_state) { return (buffer_state.usage & VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT) == 0; },
                   []() { return "The following buffers are missing VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT"; }, kUsageErrorMsgBuffer},

                  {scratch_address_range_vuid,
                   [scratch_address_range](const vvl::Buffer &buffer_state) {
                       const vvl::range<VkDeviceSize> buffer_address_range = buffer_state.DeviceAddressRange();
                       return !buffer_address_range.includes(scratch_address_range);
                   },
                   [scratch_size]() {
                       return "The scratch size (" + std::to_string(scratch_size) + ") does not fit in any buffer";
                   },
                   kEmptyErrorMsgBuffer}}}};

            skip |=
                buffer_address_validator.ValidateDeviceAddress(*this, info_loc.dot(Field::scratchData).dot(Field::deviceAddress),
                                                               cb_objlist, info.scratchData.deviceAddress, scratch_size);
        }
    }

    return skip;
}

bool CoreChecks::CommonBuildAccelerationStructureValidation(const VkAccelerationStructureBuildGeometryInfoKHR &info,
                                                            const Location &info_loc, LogObjectList object_list) const {
    bool skip = false;

    const auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure);
    if (!src_as_state) return skip;

    if (info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
        if (!src_as_state->is_built) {
            const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                   ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03667"
                               : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                   ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03667"
                                   : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03667";
            LogObjectList objlist = object_list;
            objlist.add(info.srcAccelerationStructure);
            skip |= LogError(
                vuid, objlist, info_loc.dot(Field::mode),
                "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, srcAccelerationStructure must have been previously built");
        } else if (src_as_state->build_info_khr.has_value()) {
            if (!(src_as_state->build_info_khr->flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_KHR)) {
                const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03667"
                                   : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03667"
                                       : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03667";
                LogObjectList objlist = object_list;
                objlist.add(info.srcAccelerationStructure);
                skip |= LogError(vuid, 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 (info.flags != src_as_state->build_info_khr->flags) {
                const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03759"
                                   : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03759"
                                       : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03759";
                LogObjectList objlist = object_list;
                objlist.add(info.srcAccelerationStructure);
                skip |= LogError(vuid, objlist, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, but %s (%s) must have the same value as "
                                 "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 char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03760"
                                   : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03760"
                                       : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03760";

                LogObjectList objlist = object_list;
                objlist.add(info.srcAccelerationStructure);
                skip |= LogError(vuid, objlist, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR, but type (%s) must have the same value as "
                                 "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 char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03758"
                                   : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                       ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03758"
                                       : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03758";

                LogObjectList objlist = object_list;
                objlist.add(info.srcAccelerationStructure);
                skip |= LogError(vuid, objlist, info_loc.dot(Field::mode),
                                 "is VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR,"
                                 " but geometryCount (%" PRIu32
                                 ") must have the same value as 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 vku::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 char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                               ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03761"
                                           : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                               ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03761"
                                               : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03761";

                        LogObjectList objlist = object_list;
                        objlist.add(info.srcAccelerationStructure);
                        skip |= LogError(vuid, 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 char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                               ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03762"
                                           : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                               ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03762"
                                               : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03762";

                        LogObjectList objlist = object_list;
                        objlist.add(info.srcAccelerationStructure);
                        skip |= LogError(vuid, 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.flags).c_str());
                    }

                    if (updated_geometry.geometryType == VK_GEOMETRY_TYPE_TRIANGLES_KHR) {
                        if (updated_geometry.geometry.triangles.vertexFormat != last_geometry.geometry.triangles.vertexFormat) {
                            const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03763"
                                               : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03763"
                                                   : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03763";

                            LogObjectList objlist = object_list;
                            objlist.add(info.srcAccelerationStructure);
                            skip |= LogError(vuid, 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.geometry.triangles.maxVertex != last_geometry.geometry.triangles.maxVertex) {
                            const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03764"
                                               : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03764"
                                                   : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03764";

                            LogObjectList objlist = object_list;
                            objlist.add(info.srcAccelerationStructure);
                            skip |=
                                LogError(vuid, 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.geometry.triangles.indexType != last_geometry.geometry.triangles.indexType) {
                            const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03765"
                                               : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03765"
                                                   : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03765";

                            LogObjectList objlist = object_list;
                            objlist.add(info.srcAccelerationStructure);
                            skip |=
                                LogError(vuid, 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 (last_geometry.geometry.triangles.transformData.deviceAddress == 0 &&
                            updated_geometry.geometry.triangles.transformData.deviceAddress != 0) {
                            const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03766"
                                               : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03766"
                                                   : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03766";

                            LogObjectList objlist = object_list;
                            objlist.add(info.srcAccelerationStructure);
                            skip |= LogError(vuid, 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 (last_geometry.geometry.triangles.transformData.deviceAddress != 0 &&
                            updated_geometry.geometry.triangles.transformData.deviceAddress == 0) {
                            const char *vuid = info_loc.function == Func::vkCmdBuildAccelerationStructuresKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03767"
                                               : info_loc.function == Func::vkCmdBuildAccelerationStructuresIndirectKHR
                                                   ? "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03767"
                                                   : "VUID-vkBuildAccelerationStructuresKHR-pInfos-03767";

                            LogObjectList objlist = object_list;
                            objlist.add(info.srcAccelerationStructure);
                            skip |= LogError(vuid, 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);
                        }
                    }
                }
            }
        }
    }

    return skip;
}

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

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

    if (!cb_state->unprotected) {
        skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-commandBuffer-09547", commandBuffer, error_obj.location,
                         "command can't be used in protected command buffers.");
    }

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

        if (const auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure)) {
            if (info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
                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 (const auto dst_as_state = Get<vvl::AccelerationStructureKHR>(info.dstAccelerationStructure)) {
            skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_as_state->buffer_state,
                                                  info_loc.dot(Field::dstAccelerationStructure),
                                                  "VUID-vkCmdBuildAccelerationStructuresKHR-pInfos-03707");
            if (info.type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) {
                if (dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR &&
                    dst_as_state->create_info.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_info.type));
                }
            }
            if (info.type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) {
                if (dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR &&
                    dst_as_state->create_info.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_info.type));
                }
            }
        }

        skip |= CommonBuildAccelerationStructureValidation(info, info_loc, commandBuffer);

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

    skip |= ValidateAccelerationStructuresDeviceScratchBufferMemoryAliasing(commandBuffer, infoCount, pInfos, ppBuildRangeInfos,
                                                                            error_obj.location);

    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 (const auto [info_i, info] : vvl::enumerate(pInfos, infoCount)) {
        const Location info_loc = error_obj.location.dot(Field::pInfos, info_i);
        auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure);
        auto dst_as_state = Get<vvl::AccelerationStructureKHR>(info.dstAccelerationStructure);

        if (src_as_state) {
            if (info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR) {
                skip |= ValidateAccelStructBufferMemoryIsHostVisible(*src_as_state, info_loc.dot(Field::srcAccelerationStructure),
                                                                     "VUID-vkBuildAccelerationStructuresKHR-pInfos-03723");
                skip |=
                    ValidateAccelStructBufferMemoryIsNotMultiInstance(*src_as_state, info_loc.dot(Field::srcAccelerationStructure),
                                                                      "VUID-vkBuildAccelerationStructuresKHR-pInfos-03776");
            }
        }

        if (dst_as_state) {
            skip |= ValidateAccelStructBufferMemoryIsHostVisible(*dst_as_state, info_loc.dot(Field::dstAccelerationStructure),
                                                                 "VUID-vkBuildAccelerationStructuresKHR-pInfos-03722");

            skip |= ValidateAccelStructBufferMemoryIsNotMultiInstance(*dst_as_state, info_loc.dot(Field::dstAccelerationStructure),
                                                                      "VUID-vkBuildAccelerationStructuresKHR-pInfos-03775");

            if (!(info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR &&
                  (info.flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR))) {
                const VkDeviceSize as_minimum_size =
                    rt::ComputeAccelerationStructureSize(rt::BuildType::Host, device, info, ppBuildRangeInfos[info_i]);
                if (dst_as_state->create_info.size < as_minimum_size) {
                    const LogObjectList objlist(info.dstAccelerationStructure);
                    skip |= LogError("VUID-vkBuildAccelerationStructuresKHR-pInfos-10126", objlist,
                                     info_loc.dot(Field::dstAccelerationStructure),
                                     " was created with size (%" PRIu64
                                     "), but an acceleration structure build with corresponding ppBuildRangeInfos[%" PRIu32
                                     "] requires a minimum size of (%" PRIu64 ").",
                                     dst_as_state->create_info.size, info_i, as_minimum_size);
                }
            }

            if (info.type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) {
                if (dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR &&
                    dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR) {
                    skip |= LogError("VUID-vkBuildAccelerationStructuresKHR-pInfos-03700", info.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_info.type));
                }
            }

            if (info.type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) {
                if (dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR &&
                    dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR) {
                    skip |= LogError(
                        "VUID-vkBuildAccelerationStructuresKHR-pInfos-03699", info.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_info.type));
                }
            }
        }

        skip |= ValidateAccelerationStructuresMemoryAlisasing(LogObjectList(), infoCount, pInfos, info_i, error_obj);
        const VkDeviceSize scratch_i_size = rt::ComputeScratchSize(rt::BuildType::Host, device, info, ppBuildRangeInfos[info_i]);
        auto scratch_i_host_addr = reinterpret_cast<uint64_t>(info.scratchData.hostAddress);
        const vvl::range<uint64_t> scratch_addr_range(scratch_i_host_addr, scratch_i_host_addr + scratch_i_size);

        assert(infoCount > info_i);
        for (auto [other_info_j, other_info] : vvl::enumerate(pInfos + info_i + 1, infoCount - (info_i + 1))) {
            const Location other_info_j_loc = error_obj.location.dot(Field::pInfos, other_info_j + info_i + 1);
            const VkDeviceSize other_scratch_size =
                rt::ComputeScratchSize(rt::BuildType::Host, device, other_info, ppBuildRangeInfos[other_info_j]);
            auto other_scratch_host_addr = reinterpret_cast<uint64_t>(other_info.scratchData.hostAddress);
            const vvl::range<uint64_t> other_scratch_addr_range(other_scratch_host_addr,
                                                                other_scratch_host_addr + other_scratch_size);

            if (scratch_addr_range.intersects(other_scratch_addr_range)) {
                std::string info_i_scratch_str = info_loc.dot(Field::scratchData).Fields();
                std::string other_info_j_scratch_str = other_info_j_loc.dot(Field::scratchData).Fields();
                skip |= LogError(
                    "VUID-vkBuildAccelerationStructuresKHR-scratchData-03704", device, info_loc.dot(Field::scratchData),
                    "overlaps with %s on host address range %s.\n"
                    "%s.hostAddress is %p and assumed scratch size is %" PRIu64
                    ".\n"
                    "%s.hostAddress is %p and assumed scratch size is %" PRIu64 ".",
                    other_info_j_scratch_str.c_str(), string_range_hex(scratch_addr_range & other_scratch_addr_range).c_str(),
                    info_i_scratch_str.c_str(), info.scratchData.hostAddress, scratch_i_size, other_info_j_scratch_str.c_str(),
                    other_info.scratchData.hostAddress, other_scratch_size);
            }
        }

        skip |= CommonBuildAccelerationStructureValidation(info, info_loc, LogObjectList());

        for (uint32_t geom_i = 0; geom_i < info.geometryCount; ++geom_i) {
            const VkAccelerationStructureGeometryKHR &geom = rt::GetGeometry(info, geom_i);

            if (geom.geometryType != VK_GEOMETRY_TYPE_INSTANCES_KHR) {
                continue;
            }

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

            if (info.mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR && src_as_state &&
                src_as_state->build_info_khr.has_value()) {
                if (geom_i < src_as_state->build_range_infos.size()) {
                    if (const uint32_t recorded_primitive_count = src_as_state->build_range_infos[geom_i].primitiveCount;
                        recorded_primitive_count != ppBuildRangeInfos[info_i][geom_i].primitiveCount) {
                        const LogObjectList objlist(info.srcAccelerationStructure);
                        skip |= LogError("VUID-vkCmdBuildAccelerationStructuresKHR-primitiveCount-03769", objlist, geometry_loc,
                                         " has corresponding VkAccelerationStructureBuildRangeInfoKHR %s[%" PRIu32
                                         "], but this build range has its primitiveCount member set to (%" PRIu32
                                         ") when it was last specified as (%" PRIu32 ").",
                                         error_obj.location.dot(Field::ppBuildRangeInfos, info_i).Fields().c_str(), geom_i,
                                         ppBuildRangeInfos[info_i][geom_i].primitiveCount, recorded_primitive_count);
                    }
                }
            }

            for (uint32_t instance_i = 0; instance_i < ppBuildRangeInfos[info_i][geom_i].primitiveCount; ++instance_i) {
                const VkAccelerationStructureInstanceKHR *instance = nullptr;
                if (geom.geometry.instances.data.hostAddress) {
                    if (geom.geometry.instances.arrayOfPointers) {
                        auto instance_pointers_array = reinterpret_cast<VkAccelerationStructureInstanceKHR const *const *>(
                            geom.geometry.instances.data.hostAddress);
                        instance = instance_pointers_array[instance_i];
                    } else {
                        auto instances_array =
                            reinterpret_cast<VkAccelerationStructureInstanceKHR const *>(geom.geometry.instances.data.hostAddress);
                        instance = instances_array + instance_i;
                    }

                    // Can only get here if geom.geometry.instances.arrayOfPointers is true
                    if (!instance) {
                        skip |= LogError(
                            "VUID-vkBuildAccelerationStructuresKHR-pInfos-03779", device,
                            geometry_loc.dot(Field::geometry)
                                .dot(Field::instances)
                                .dot(Field::data)
                                .dot(Field::hostAddress, instance_i),
                            "(0x%p) does not reference a valid VkAccelerationStructureKHR object. %s is %s.", instance,
                            geometry_loc.dot(Field::geometry).dot(Field::instances).dot(Field::arrayOfPointers).Fields().c_str(),
                            string_VkBool32(geom.geometry.instances.arrayOfPointers).c_str());

                        // Following checks rely on instance not being null
                        continue;
                    }

                    const VkAccelerationStructureKHR accel_struct =
                        CastFromUint64<VkAccelerationStructureKHR>(instance->accelerationStructureReference);
                    auto accel_struct_state = Get<vvl::AccelerationStructureKHR>(accel_struct);

                    if (!accel_struct_state) {
                        skip |= LogError(
                            "VUID-vkBuildAccelerationStructuresKHR-pInfos-03779", device,
                            geometry_loc.dot(Field::geometry)
                                .dot(Field::instances)
                                .dot(Field::data)
                                .dot(Field::hostAddress, instance_i)
                                .dot(Field::accelerationStructureReference),
                            "(%" PRIu64 ") does not reference a valid VkAccelerationStructureKHR object. %s is %s.",
                            instance->accelerationStructureReference,
                            geometry_loc.dot(Field::geometry).dot(Field::instances).dot(Field::arrayOfPointers).Fields().c_str(),
                            string_VkBool32(geom.geometry.instances.arrayOfPointers).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);
    bool skip = false;
    skip |= ValidateCmd(*cb_state, error_obj.location);
    if (!cb_state->unprotected) {
        skip |= LogError("VUID-vkCmdBuildAccelerationStructuresIndirectKHR-commandBuffer-09547", commandBuffer, error_obj.location,
                         "called in a protected command buffer.");
    }

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

        if (auto src_as_state = Get<vvl::AccelerationStructureKHR>(info.srcAccelerationStructure)) {
            if (info.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 (auto dst_as_state = Get<vvl::AccelerationStructureKHR>(info.dstAccelerationStructure)) {
            skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *dst_as_state->buffer_state,
                                                  info_loc.dot(Field::dstAccelerationStructure),
                                                  "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03707");
            if (info.type == VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR) {
                if (dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_KHR &&
                    dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR) {
                    skip |= LogError("VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03700", info.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_info.type));
                }
            }

            if (info.type == VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR) {
                if (dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR &&
                    dst_as_state->create_info.type != VK_ACCELERATION_STRUCTURE_TYPE_GENERIC_KHR) {
                    skip |= LogError(
                        "VUID-vkCmdBuildAccelerationStructuresIndirectKHR-pInfos-03699", info.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_info.type));
                }
            }
        }

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

        skip |= CommonBuildAccelerationStructureValidation(info, info_loc, commandBuffer);

        skip |= ValidateAccelerationBuffers(commandBuffer, info_i, info, nullptr, info_loc);
    }
    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);
    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);

    if (dst_as_state && pInfo) {
        if (dst_as_state->create_info.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_info.info.type),
                             string_VkAccelerationStructureTypeKHR(pInfo->type));
        }
        if (dst_as_state->create_info.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_info.info.flags).c_str(),
                             string_VkBuildAccelerationStructureFlagsKHR(pInfo->flags).c_str());
        }
        if (dst_as_state->create_info.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_info.info.instanceCount, pInfo->instanceCount);
        }
        if (dst_as_state->create_info.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_info.info.geometryCount, pInfo->geometryCount);
        } else {
            for (uint32_t i = 0; i < pInfo->geometryCount; i++) {
                const VkGeometryDataNV &create_geometry_data = dst_as_state->create_info.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) {
        skip |= VerifyBoundMemoryIsValid(dst_as_state->MemoryState(), LogObjectList(commandBuffer, dst), dst_as_state->Handle(),
                                         error_obj.location.dot(Field::dst), "VUID-vkCmdBuildAccelerationStructureNV-dst-07787");
    }

    auto scratch_buffer_state = Get<vvl::Buffer>(scratch);
    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 || !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 (scratch_buffer_state && dst_as_state &&
            dst_as_state->update_scratch_memory_requirements.size > (scratch_buffer_state->create_info.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 (scratch_buffer_state && dst_as_state &&
            dst_as_state->build_scratch_memory_requirements.size > (scratch_buffer_state->create_info.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) {
        if (auto buffer_state = Get<vvl::Buffer>(instanceData)) {
            skip |= ValidateBufferUsageFlags(
                LogObjectList(commandBuffer, instanceData), *buffer_state, VK_BUFFER_USAGE_2_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_2_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);
    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) {
        const LogObjectList objlist(commandBuffer, dst);
        skip |= VerifyBoundMemoryIsValid(dst_as_state->MemoryState(), objlist, dst_as_state->Handle(),
                                         error_obj.location.dot(Field::dst), "VUID-vkCmdCopyAccelerationStructureNV-dst-07792");
        skip |= VerifyBoundMemoryIsDeviceVisible(dst_as_state->MemoryState(), objlist, dst_as_state->Handle(),
                                                 error_obj.location.dot(Field::dst),
                                                 "VUID-vkCmdCopyAccelerationStructureNV-buffer-03719");
    }
    if (src_as_state) {
        const LogObjectList objlist(commandBuffer, src);
        skip |= VerifyBoundMemoryIsDeviceVisible(src_as_state->MemoryState(), 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 &&
            (!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 {
    bool skip = false;
    if (auto as_state = Get<vvl::AccelerationStructureNV>(accelerationStructure)) {
        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 {
    bool skip = false;
    if (auto as_state = Get<vvl::AccelerationStructureKHR>(accelerationStructure)) {
        skip |= ValidateObjectNotInUse(as_state.get(), error_obj.location,
                                       "VUID-vkDestroyAccelerationStructureKHR-accelerationStructure-02442");
    }
    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]);
        ASSERT_AND_CONTINUE(as_state);

        skip |= ValidateAccelStructBufferMemoryIsHostVisible(*as_state, as_loc,
                                                             "VUID-vkWriteAccelerationStructuresPropertiesKHR-buffer-03733");

        skip |= ValidateAccelStructBufferMemoryIsNotMultiInstance(*as_state, as_loc,
                                                                  "VUID-vkWriteAccelerationStructuresPropertiesKHR-buffer-03784");

        if (!as_state->is_built) {
            skip |= LogError("VUID-vkWriteAccelerationStructuresPropertiesKHR-pAccelerationStructures-04964", device, as_loc,
                             "has not been built.");
        } else if (as_state->build_info_khr.has_value()) {
            if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR) {
                if (!(as_state->build_info_khr->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_state->build_info_khr->flags).c_str());
                }
            }
        }
    }
    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);
    ASSERT_AND_RETURN_SKIP(query_pool_state);
    const auto &query_pool_ci = query_pool_state->create_info;
    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 different from the type queryPool was created with (%s).",
                         string_VkQueryType(queryType), string_VkQueryType(query_pool_ci.queryType));
    }
    if (firstQuery + accelerationStructureCount > query_pool_state->create_info.queryCount) {
        skip |= LogError("VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-query-04880", commandBuffer,
                         error_obj.location.dot(Field::firstQuery),
                         "(%" PRIu32 ") + accelerationStructureCount (%" PRIu32 "), or %" PRIu32
                         ", is superior to the number of queries in queryPool (%" PRIu32 ").",
                         firstQuery, accelerationStructureCount, firstQuery + accelerationStructureCount,
                         query_pool_state->create_info.queryCount);
    }

    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]);
        ASSERT_AND_CONTINUE(as_state);

        skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *as_state->buffer_state, as_loc.dot(Field::buffer),
                                              "VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-buffer-03736");

        if (!as_state->is_built) {
            skip |= LogError("VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-pAccelerationStructures-04964", commandBuffer,
                             as_loc, "has not been built.");
        } else {
            if (queryType == VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_KHR && as_state->build_info_khr.has_value()) {
                if (!(as_state->build_info_khr->flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) {
                    skip |= LogError("VUID-vkCmdWriteAccelerationStructuresPropertiesKHR-accelerationStructures-03431",
                                     commandBuffer, as_loc,
                                     "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);
    ASSERT_AND_RETURN_SKIP(query_pool_state);
    const auto &query_pool_ci = query_pool_state->create_info;
    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]);
            ASSERT_AND_CONTINUE(as_state);

            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::ValidateCopyAccelerationStructureInfoKHR(const VkCopyAccelerationStructureInfoKHR &as_info,
                                                          const VulkanTypedHandle &handle, const Location &info_loc) const {
    bool skip = false;

    auto src_as_state = Get<vvl::AccelerationStructureKHR>(as_info.src);
    if (src_as_state) {
        if (!src_as_state->is_built) {
            skip |= LogError("VUID-VkCopyAccelerationStructureInfoKHR-src-04963", device, info_loc.dot(Field::src),
                             "has not been built.");
        }

        if (auto buffer_state = Get<vvl::Buffer>(src_as_state->create_info.buffer)) {
            skip |= ValidateMemoryIsBoundToBuffer(device, *buffer_state, info_loc.dot(Field::src),
                                                  "VUID-VkCopyAccelerationStructureInfoKHR-buffer-03718");
        }

        if (as_info.mode == VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_KHR && src_as_state->build_info_khr.has_value()) {
            if (!(src_as_state->build_info_khr->flags & VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_KHR)) {
                const LogObjectList objlist(handle, as_info.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(as_info.src).c_str());
            }
        }
    }
    auto dst_as_state = Get<vvl::AccelerationStructureKHR>(as_info.dst);
    if (dst_as_state) {
        if (auto buffer_state = Get<vvl::Buffer>(dst_as_state->create_info.buffer)) {
            skip |= ValidateMemoryIsBoundToBuffer(device, *buffer_state, info_loc.dot(Field::dst),
                                                  "VUID-VkCopyAccelerationStructureInfoKHR-buffer-03719");
        }
    }

    if (src_as_state && dst_as_state) {
        skip |= ValidateAccelStructsMemoryDoNotOverlap(info_loc.function, LogObjectList(), *src_as_state, info_loc.dot(Field::src),
                                                       *dst_as_state, info_loc.dot(Field::dst),
                                                       "VUID-VkCopyAccelerationStructureInfoKHR-dst-07791");
    }

    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);
    skip |= ValidateCmd(*cb_state, error_obj.location);

    const Location info_loc = error_obj.location.dot(Field::pInfo);
    skip |= ValidateCopyAccelerationStructureInfoKHR(*pInfo, error_obj.handle, info_loc);
    if (auto src_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->src)) {
        skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *src_accel_state->buffer_state, info_loc.dot(Field::src),
                                              "VUID-vkCmdCopyAccelerationStructureKHR-buffer-03737");
    }
    if (auto dst_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->dst)) {
        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");

    const Location info_loc = error_obj.location.dot(Field::pInfo);
    skip |= ValidateCopyAccelerationStructureInfoKHR(*pInfo, error_obj.handle, info_loc);

    if (auto src_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->src)) {
        skip |= ValidateAccelStructBufferMemoryIsHostVisible(*src_accel_state, info_loc.dot(Field::src),
                                                             "VUID-vkCopyAccelerationStructureKHR-buffer-03727");

        skip |= ValidateAccelStructBufferMemoryIsNotMultiInstance(*src_accel_state, info_loc.dot(Field::src),
                                                                  "VUID-vkCopyAccelerationStructureKHR-buffer-03780");
    }

    if (auto dst_accel_state = Get<vvl::AccelerationStructureKHR>(pInfo->dst)) {
        skip |= ValidateAccelStructBufferMemoryIsHostVisible(*dst_accel_state, info_loc.dot(Field::dst),
                                                             "VUID-vkCopyAccelerationStructureKHR-buffer-03728");

        skip |= ValidateAccelStructBufferMemoryIsNotMultiInstance(*dst_accel_state, info_loc.dot(Field::dst),
                                                                  "VUID-vkCopyAccelerationStructureKHR-buffer-03781");
    }
    return skip;
}

bool CoreChecks::ValidateVkCopyAccelerationStructureToMemoryInfoKHR(const vvl::AccelerationStructureKHR &src_accel_struct,
                                                                    LogObjectList objlist, const Location &loc) const {
    bool skip = false;
    if (!src_accel_struct.is_built) {
        objlist.add(src_accel_struct.Handle());
        skip |= LogError("VUID-VkCopyAccelerationStructureToMemoryInfoKHR-src-04959", objlist, loc.dot(Field::src),
                         "has not been built.");
    }

    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");

    if (const auto src_accel_struct = Get<vvl::AccelerationStructureKHR>(pInfo->src)) {
        const Location info_loc = error_obj.location.dot(Field::pInfo);
        skip |= ValidateVkCopyAccelerationStructureToMemoryInfoKHR(*src_accel_struct, LogObjectList(device), info_loc);

        if (auto buffer_state = Get<vvl::Buffer>(src_accel_struct->create_info.buffer)) {
            skip |= ValidateAccelStructBufferMemoryIsHostVisible(*src_accel_struct, info_loc.dot(Field::src),
                                                                 "VUID-vkCopyAccelerationStructureToMemoryKHR-buffer-03731");

            skip |= ValidateAccelStructBufferMemoryIsNotMultiInstance(*src_accel_struct, info_loc.dot(Field::src),
                                                                      "VUID-vkCopyAccelerationStructureToMemoryKHR-buffer-03783");
        }
    }

    return skip;
}

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

    if (auto src_accel_struct = Get<vvl::AccelerationStructureKHR>(pInfo->src)) {
        skip |= ValidateVkCopyAccelerationStructureToMemoryInfoKHR(*src_accel_struct, LogObjectList(commandBuffer), info_loc);

        if (auto buffer_state = Get<vvl::Buffer>(src_accel_struct->create_info.buffer)) {
            skip |= ValidateMemoryIsBoundToBuffer(commandBuffer, *buffer_state, info_loc.dot(Field::src),
                                                  "VUID-vkCmdCopyAccelerationStructureToMemoryKHR-None-03559");
        }
    }

    const VkDeviceAddress dst_address = pInfo->dst.deviceAddress;
    if (dst_address == 0) {
        skip |= LogError("VUID-vkCmdCopyAccelerationStructureToMemoryKHR-pInfo-03739", commandBuffer,
                         info_loc.dot(Field::dst).dot(Field::deviceAddress), "is zero");
    }
    skip |= ValidateDeviceAddress(info_loc.dot(Field::dst).dot(Field::deviceAddress), LogObjectList(commandBuffer), dst_address);

    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");

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

        skip |= ValidateAccelStructBufferMemoryIsNotMultiInstance(*accel_state, info_loc.dot(Field::dst),
                                                                  "VUID-vkCopyMemoryToAccelerationStructureKHR-buffer-03782");
    }

    return skip;
}

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

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

    const VkDeviceAddress src_address = pInfo->src.deviceAddress;
    if (src_address == 0) {
        skip |= LogError("VUID-vkCmdCopyMemoryToAccelerationStructureKHR-pInfo-03742", commandBuffer,
                         info_loc.dot(Field::src).dot(Field::deviceAddress), "is zero");
    }
    skip |= ValidateDeviceAddress(info_loc.dot(Field::src).dot(Field::deviceAddress), LogObjectList(commandBuffer), src_address);

    return skip;
}

// Calculates the total number of shader groups taking libraries into account.
static uint32_t CalcTotalShaderGroupCount(const CoreChecks &validator, const vvl::Pipeline &pipeline) {
    uint32_t total = 0;
    const vku::safe_VkRayTracingPipelineCreateInfoCommon &create_info = pipeline.RayTracingCreateInfo();
    total = create_info.groupCount;

    if (create_info.pLibraryInfo) {
        for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) {
            auto library_pipeline_state = validator.Get<vvl::Pipeline>(create_info.pLibraryInfo->pLibraries[i]);
            if (!library_pipeline_state) continue;
            total += CalcTotalShaderGroupCount(validator, *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 pipeline_ptr = Get<vvl::Pipeline>(pipeline);
    ASSERT_AND_RETURN_SKIP(pipeline_ptr);
    if (pipeline_ptr->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(pipeline_ptr->pipeline_type));
        return skip;
    }

    const vvl::Pipeline &pipeline_state = *pipeline_ptr;
    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_VkPipelineCreateFlags2(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(*this, 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 ") + 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);
    ASSERT_AND_RETURN_SKIP(pipeline_state);
    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;
    }

    if (pipeline_state->create_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_VkPipelineCreateFlags2(pipeline_state->create_flags).c_str());
        }
    }

    const uint32_t total_group_count = CalcTotalShaderGroupCount(*this, *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 ") + groupCount (%" PRIu32
                         ") must be less than or equal to the number of shader groups in pipeline (%" PRIu32 ").",
                         firstGroup, groupCount, total_group_count);
    }
    if (!(pipeline_state->create_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_VkPipelineCreateFlags2(pipeline_state->create_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 ValidateCmd(*cb_state, error_obj.location);
}

static vku::safe_VkRayTracingShaderGroupCreateInfoKHR *GetRayTracingShaderGroup(const CoreChecks &validator,
                                                                                const vvl::Pipeline &pipeline, uint32_t group_i) {
    const vku::safe_VkRayTracingPipelineCreateInfoCommon &create_info = pipeline.RayTracingCreateInfo();
    // Target group is in currently explored pipeline
    if (group_i < create_info.groupCount) {
        return &create_info.pGroups[group_i];
    }

    // Target group is in a linked pipeline library, recursively explore them
    if (create_info.pLibraryInfo) {
        for (uint32_t i = 0; i < create_info.pLibraryInfo->libraryCount; ++i) {
            auto library_pipeline_state = validator.Get<vvl::Pipeline>(create_info.pLibraryInfo->pLibraries[i]);
            if (!library_pipeline_state) {
                continue;
            }
            return GetRayTracingShaderGroup(validator, *library_pipeline_state.get(), group_i - create_info.groupCount);
        }
    }

    return 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);
    ASSERT_AND_RETURN_SKIP(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 {
        const auto &create_info = pipeline_state->RayTracingCreateInfo();
        const uint32_t total_group_count = CalcTotalShaderGroupCount(*this, *pipeline_state);
        if (group >= total_group_count) {
            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 (create info had a groupCount of %" PRIu32
                " and %" PRIu32 " got added from pLibraryInfo).",
                group, create_info.groupCount, total_group_count - create_info.groupCount);
        } else {
            const auto *group_info = GetRayTracingShaderGroup(*this, *pipeline_state, group);
            ASSERT_AND_RETURN_SKIP(group_info);
            bool unused_group = false;
            switch (groupShader) {
                case VK_SHADER_GROUP_SHADER_GENERAL_KHR:
                    if (group_info->generalShader == VK_SHADER_UNUSED_KHR) {
                        unused_group = true;
                    }
                    break;
                case VK_SHADER_GROUP_SHADER_CLOSEST_HIT_KHR:
                    if (group_info->closestHitShader == VK_SHADER_UNUSED_KHR) {
                        unused_group = true;
                    }
                    break;
                case VK_SHADER_GROUP_SHADER_ANY_HIT_KHR:
                    if (group_info->anyHitShader == VK_SHADER_UNUSED_KHR) {
                        unused_group = true;
                    }
                    break;
                case VK_SHADER_GROUP_SHADER_INTERSECTION_KHR:
                    if (group_info->intersectionShader == VK_SHADER_UNUSED_KHR) {
                        unused_group = true;
                    }
                    break;

                default:
                    break;
            }
            if (unused_group) {
                const LogObjectList objlist(device, pipeline);
                skip |= LogError("VUID-vkGetRayTracingShaderGroupStackSizeKHR-groupShader-03609", objlist,
                                 error_obj.location.dot(Field::groupShader),
                                 "is %s but the corresponding shader in shader group %" PRIu32 " is VK_SHADER_UNUSED_KHR.",
                                 string_VkShaderGroupShaderKHR(groupShader), group);
            }
        }
    }
    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 && vb_state->create_info.size <= triangles.vertexOffset) {
        skip |= LogError("VUID-VkGeometryTrianglesNV-vertexOffset-02428", device, loc, "is invalid.");
    }

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

    auto td_state = Get<vvl::Buffer>(triangles.transformData);
    if (td_state && td_state->create_info.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 && aabb_state->create_info.size > 0 && aabb_state->create_info.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(const vvl::CommandBuffer &cb_state, const Location &table_loc,
                                                      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 VkDeviceSize requested_size = binding_table.size - 1;
    const vvl::range<VkDeviceSize> requested_range(binding_table.deviceAddress, binding_table.deviceAddress + requested_size);

    BufferAddressValidation<3> buffer_address_validator = {{{
        {vuid_binding_table_flag,
         [](const vvl::Buffer &buffer_state) {
             return (static_cast<uint32_t>(buffer_state.usage) & VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR) == 0;
         },
         []() { return "The following buffers are missing VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR"; }, kUsageErrorMsgBuffer},

        {"VUID-VkStridedDeviceAddressRegionKHR-size-04631",
         [&requested_range](const vvl::Buffer &buffer_state) {
             const auto buffer_address_range = buffer_state.DeviceAddressRange();
             return !buffer_address_range.includes(requested_range);
         },
         [&table_loc, &binding_table]() {
             return "The " + table_loc.Fields() + "->size (" + std::to_string(binding_table.size) +
                    ") - 1 does not fit in any buffer";
         },
         kEmptyErrorMsgBuffer},

        {"VUID-VkStridedDeviceAddressRegionKHR-size-04632",
         [&binding_table](const vvl::Buffer &buffer_state) { return binding_table.stride > buffer_state.create_info.size; },
         [table_loc, &binding_table]() {
             return "The " + table_loc.Fields() + "->stride (" + std::to_string(binding_table.stride) +
                    ") does not fit in any buffer";
         },
         kEmptyErrorMsgBuffer},
    }}};

    skip |= buffer_address_validator.ValidateDeviceAddress(*this, table_loc.dot(Field::deviceAddress),
                                                           cb_state.GetObjectList(VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR),
                                                           binding_table.deviceAddress, requested_size);

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

bool CoreChecks::PreCallValidateCmdBuildPartitionedAccelerationStructuresNV(
    VkCommandBuffer commandBuffer, const VkBuildPartitionedAccelerationStructureInfoNV *pBuildInfo,
    const ErrorObject &error_obj) const {
    bool skip = false;

    if (!enabled_features.partitionedAccelerationStructure) {
        skip |= LogError("VUID-vkCmdBuildPartitionedAccelerationStructuresNV-partitionedAccelerationStructure-10536", commandBuffer,
                         error_obj.location, "partitionedAccelerationStructure feature was not enabled.");
    }
    // Get build size info here for memory size check
    VkAccelerationStructureBuildSizesInfoKHR build_size_info = vku::InitStructHelper();
    const VkPartitionedAccelerationStructureInstancesInputNV input = pBuildInfo->input;
    DispatchGetPartitionedAccelerationStructuresBuildSizesNV(device, &input, &build_size_info);

    skip |= ValidateBuildPartitionedAccelerationStructureInfoNV(*pBuildInfo, error_obj.location.dot(Field::pBuildInfo),
                                                                build_size_info.buildScratchSize,
                                                                build_size_info.accelerationStructureSize);

    if (!IsPointerAligned(pBuildInfo->srcAccelerationStructureData, 256)) {
        skip |= LogError("VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10544", commandBuffer,
                         error_obj.location.dot(Field::pBuildInfo).dot(Field::srcAccelerationStructureData),
                         "(0x%" PRIx64 ") must be aligned to 256 bytes", pBuildInfo->srcAccelerationStructureData);
    }

    if (!IsPointerAligned(pBuildInfo->dstAccelerationStructureData, 256)) {
        skip |= LogError("VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10545", commandBuffer,
                         error_obj.location.dot(Field::pBuildInfo).dot(Field::dstAccelerationStructureData),
                         "(0x%" PRIx64 ") must be aligned to 256 bytes", pBuildInfo->dstAccelerationStructureData);
    }

    if (!IsPointerAligned(pBuildInfo->scratchData, 256)) {
        skip |= LogError("VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10542", commandBuffer,
                         error_obj.location.dot(Field::pBuildInfo).dot(Field::scratchData),
                         "(0x%" PRIx64 ") must be aligned to 256 bytes", pBuildInfo->scratchData);
    }

    {
        BufferAddressValidation<2> buffer_address_validator = {
            {{{"VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10550",
               [](const vvl::Buffer &buffer_state) { return (buffer_state.usage & VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT) == 0; },
               []() { return "The following buffers are missing VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT"; }, kUsageErrorMsgBuffer},
              {"VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10541",
               [&build_size_info](const vvl::Buffer &buffer_state) {
                   return buffer_state.requirements.size < build_size_info.buildScratchSize;
               },
               [&build_size_info]() {
                   return "The buildScratchSize (" + std::to_string(build_size_info.buildScratchSize) +
                          ") does not fit in any buffer";
               },
               kEmptyErrorMsgBuffer}}}};

        skip |= buffer_address_validator.ValidateDeviceAddress(
            *this, error_obj.location.dot(Field::pBuildInfo).dot(Field::scratchData), LogObjectList(commandBuffer),
            pBuildInfo->scratchData, build_size_info.buildScratchSize);
    }

    {
        BufferAddressValidation<1> buffer_address_validator = {
            {{{"VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10551",
               [](const vvl::Buffer &buffer_state) {
                   return (buffer_state.usage & VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR) == 0;
               },
               []() {
                   return "The following buffers are missing "
                          "VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR";
               },
               kUsageErrorMsgBuffer}}}};

        skip |=
            buffer_address_validator.ValidateDeviceAddress(*this, error_obj.location.dot(Field::pBuildInfo).dot(Field::srcInfos),
                                                           LogObjectList(commandBuffer), pBuildInfo->srcInfos);
    }

    {
        BufferAddressValidation<1> buffer_address_validator = {
            {{{"VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10551",
               [](const vvl::Buffer &buffer_state) {
                   return (buffer_state.usage & VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR) == 0;
               },
               []() {
                   return "The following buffers are missing "
                          "VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR";
               },
               kUsageErrorMsgBuffer}}}};

        skip |= buffer_address_validator.ValidateDeviceAddress(*this,
                                                               error_obj.location.dot(Field::pBuildInfo).dot(Field::srcInfosCount),
                                                               LogObjectList(commandBuffer), pBuildInfo->srcInfosCount);
    }

    {
        BufferAddressValidation<1> buffer_address_validator = {
            {{{"VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10552",
               [](const vvl::Buffer &buffer_state) {
                   return (buffer_state.usage & VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR) == 0;
               },
               []() { return "The following buffers are missing VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR"; },
               kUsageErrorMsgBuffer}}}};

        skip |= buffer_address_validator.ValidateDeviceAddress(
            *this, error_obj.location.dot(Field::pBuildInfo).dot(Field::srcAccelerationStructureData), LogObjectList(commandBuffer),
            pBuildInfo->srcAccelerationStructureData);
    }
    if (pBuildInfo->srcAccelerationStructureData && pBuildInfo->scratchData) {
        const auto src_buffer_states = GetBuffersByAddress(pBuildInfo->srcAccelerationStructureData);
        const auto scratch_buffer_states = GetBuffersByAddress(pBuildInfo->scratchData);
        for (const auto &scratch_buffer_state : scratch_buffer_states) {
            vvl::range<VkDeviceAddress> scratch_address_range = scratch_buffer_state->DeviceAddressRange();

            if (!scratch_address_range.empty()) {
                for (const auto &buffer_state : src_buffer_states) {
                    const vvl::range<VkDeviceAddress> buffer_address_range = buffer_state->DeviceAddressRange();
                    if (buffer_address_range.intersects(scratch_address_range)) {
                        const LogObjectList objlist(commandBuffer, buffer_state->Handle(), scratch_buffer_state->Handle());
                        skip |=
                            LogError("VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10547", objlist,
                                     error_obj.location.dot(Field::pBuildInfo).dot(Field::srcAccelerationStructureData),
                                     "%s address range %s intersects scratchData address range %s",
                                     FormatHandle(buffer_state->Handle()).c_str(), string_range_hex(buffer_address_range).c_str(),
                                     string_range_hex(scratch_address_range).c_str());
                    }
                }
            }
        }
    }

    {
        BufferAddressValidation<2> buffer_address_validator = {
            {{{"VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10552",
               [](const vvl::Buffer &buffer_state) {
                   return (buffer_state.usage & VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR) == 0;
               },
               []() { return "The following buffers are missing VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR"; },
               kUsageErrorMsgBuffer},
              {"VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10543",
               [&build_size_info](const vvl::Buffer &buffer_state) {
                   return buffer_state.requirements.size < build_size_info.accelerationStructureSize;
               },
               [&build_size_info]() {
                   return "The accelerationStructureSize (" + std::to_string(build_size_info.accelerationStructureSize) +
                          ") does not fit in any buffer";
               },
               kEmptyErrorMsgBuffer}}}};

        skip |= buffer_address_validator.ValidateDeviceAddress(
            *this, error_obj.location.dot(Field::pBuildInfo).dot(Field::dstAccelerationStructureData), LogObjectList(commandBuffer),
            pBuildInfo->dstAccelerationStructureData, build_size_info.accelerationStructureSize);
    }

    if (pBuildInfo->dstAccelerationStructureData && pBuildInfo->scratchData) {
        const auto dst_buffer_states = GetBuffersByAddress(pBuildInfo->dstAccelerationStructureData);
        if (!dst_buffer_states.empty()) {
            const auto scratch_buffer_states = GetBuffersByAddress(pBuildInfo->scratchData);
            for (const auto &scratch_buffer_state : scratch_buffer_states) {
                vvl::range<VkDeviceAddress> scratch_address_range = scratch_buffer_state->DeviceAddressRange();
                if (!scratch_address_range.empty()) {
                    for (const auto &buffer_state : dst_buffer_states) {
                        const vvl::range<VkDeviceAddress> buffer_address_range = buffer_state->DeviceAddressRange();
                        if (buffer_address_range.intersects(scratch_address_range)) {
                            const LogObjectList objlist(commandBuffer, buffer_state->Handle(), scratch_buffer_state->Handle());
                            skip |= LogError("VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10548", objlist,
                                             error_obj.location.dot(Field::pBuildInfo).dot(Field::dstAccelerationStructureData),
                                             "%s address range %s intersects scratchData address range %s",
                                             FormatHandle(buffer_state->Handle()).c_str(),
                                             string_range_hex(buffer_address_range).c_str(),
                                             string_range_hex(scratch_address_range).c_str());
                        }
                    }
                }
            }
        }
    }

    // Check for src vs dst overlap, but only if they are different addresses (in-place update is allowed)
    if (pBuildInfo->srcAccelerationStructureData && pBuildInfo->dstAccelerationStructureData &&
        pBuildInfo->srcAccelerationStructureData != pBuildInfo->dstAccelerationStructureData) {
        const auto src_buffer_states = GetBuffersByAddress(pBuildInfo->srcAccelerationStructureData);
        const auto dst_buffer_states = GetBuffersByAddress(pBuildInfo->dstAccelerationStructureData);
        for (const auto &src_buffer_state : src_buffer_states) {
            const vvl::range<VkDeviceAddress> src_address_range = src_buffer_state->DeviceAddressRange();
            if (!src_address_range.empty()) {
                for (const auto &dst_buffer_state : dst_buffer_states) {
                    const vvl::range<VkDeviceAddress> dst_address_range = dst_buffer_state->DeviceAddressRange();
                    if (src_address_range.intersects(dst_address_range)) {
                        const LogObjectList objlist(commandBuffer, src_buffer_state->Handle(), dst_buffer_state->Handle());
                        skip |= LogError("VUID-vkCmdBuildPartitionedAccelerationStructuresNV-pBuildInfo-10549", objlist,
                                         error_obj.location.dot(Field::pBuildInfo),
                                         "srcAccelerationStructureData %s address range %s intersects "
                                         "dstAccelerationStructureData %s address range %s",
                                         FormatHandle(src_buffer_state->Handle()).c_str(),
                                         string_range_hex(src_address_range).c_str(),
                                         FormatHandle(dst_buffer_state->Handle()).c_str(),
                                         string_range_hex(dst_address_range).c_str());
                    }
                }
            }
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateGetPartitionedAccelerationStructuresBuildSizesNV(
    VkDevice device, const VkPartitionedAccelerationStructureInstancesInputNV *pInfo,
    VkAccelerationStructureBuildSizesInfoKHR *pBuildInfo, const ErrorObject &error_obj) const {
    bool skip = false;
    if (!enabled_features.partitionedAccelerationStructure) {
        skip |= LogError("VUID-vkGetPartitionedAccelerationStructuresBuildSizesNV-partitionedAccelerationStructure-10534", device,
                         error_obj.location, "partitionedAccelerationStructure feature was not enabled.");
    }
    if ((pInfo->partitionCount + pInfo->maxInstanceInGlobalPartitionCount) >
        phys_dev_ext_props.partitioned_acceleration_structure_props.maxPartitionCount) {
        skip |= LogError("VUID-VkPartitionedAccelerationStructureInstancesInputNV-partitionCount-10535", device,
                         error_obj.location.dot(Field::pInfo).dot(Field::partitionCount),
                         "(%" PRIu32 ")  and maxInstanceInGlobalPartitionCount (%" PRIu32
                         ") sum must be less than or equal to "
                         "maxPartitionCount (%" PRIu32 ") ",
                         pInfo->partitionCount, pInfo->maxInstanceInGlobalPartitionCount,
                         phys_dev_ext_props.partitioned_acceleration_structure_props.maxPartitionCount);
    }
    return skip;
}

bool CoreChecks::ValidateBuildPartitionedAccelerationStructureInfoNV(
    const VkBuildPartitionedAccelerationStructureInfoNV &build_info, const Location &build_info_loc,
    VkDeviceSize build_scratch_size, VkDeviceSize build_acceleration_structure_size) const {
    bool skip = false;

    if (!build_info.scratchData) {
        skip |= LogError("VUID-VkBuildPartitionedAccelerationStructureInfoNV-scratchData-10558", device,
                         build_info_loc.dot(Field::scratchData), "(0x%" PRIx64 ") must not be NULL", build_info.scratchData);
    } else {
        BufferAddressValidation<1> buffer_address_validator = {
            {{{"VUID-VkBuildPartitionedAccelerationStructureInfoNV-scratchData-10559",
               [&build_scratch_size](const vvl::Buffer &buffer_state) {
                   return buffer_state.requirements.size < build_scratch_size;
               },
               [&build_scratch_size]() {
                   return "The buildScratchSize (" + std::to_string(build_scratch_size) + ") does not fit in any buffer";
               },
               kEmptyErrorMsgBuffer}}}};

        skip |= buffer_address_validator.ValidateDeviceAddress(*this, build_info_loc.dot(Field::scratchData), LogObjectList(device),
                                                               build_info.scratchData, build_scratch_size);
    }
    if (!build_info.dstAccelerationStructureData) {
        skip |= LogError("VUID-VkBuildPartitionedAccelerationStructureInfoNV-dstAccelerationStructureData-10561", device,
                         build_info_loc.dot(Field::dstAccelerationStructureData), "(0x%" PRIx64 ") must not be NULL",
                         build_info.dstAccelerationStructureData);
    } else {
        BufferAddressValidation<1> buffer_address_validator = {
            {{{"VUID-VkBuildPartitionedAccelerationStructureInfoNV-dstAccelerationStructureData-10562",
               [&build_acceleration_structure_size](const vvl::Buffer &buffer_state) {
                   return buffer_state.requirements.size < build_acceleration_structure_size;
               },
               [&build_acceleration_structure_size]() {
                   return "The accelerationStructureSize (" + std::to_string(build_acceleration_structure_size) +
                          ") does not fit in any buffer";
               },
               kEmptyErrorMsgBuffer}}}};
        skip |= buffer_address_validator.ValidateDeviceAddress(*this, build_info_loc.dot(Field::dstAccelerationStructureData),
                                                               LogObjectList(device), build_info.dstAccelerationStructureData,
                                                               build_acceleration_structure_size);
    }

    if (!IsPointerAligned(build_info.srcInfosCount, 4)) {
        skip |= LogError("VUID-VkBuildPartitionedAccelerationStructureInfoNV-srcInfosCount-10563", device,
                         build_info_loc.dot(Field::srcInfosCount), "(0x%" PRIx64 ") must be aligned to 256 bytes",
                         build_info.srcInfosCount);
    }
    return skip;
}

bool CoreChecks::PreCallValidateCmdBuildClusterAccelerationStructureIndirectNV(
    VkCommandBuffer commandBuffer, const VkClusterAccelerationStructureCommandsInfoNV *pCommandInfos,
    const ErrorObject &error_obj) const {
    bool skip = false;
    auto cb_state = GetRead<vvl::CommandBuffer>(commandBuffer);
    const Location command_infos_loc = error_obj.location.dot(Field::pCommandInfos);
    const LogObjectList objlist(commandBuffer);
    skip |= ValidateCmd(*cb_state, error_obj.location);
    skip |= ValidateClusterAccelerationStructureCommandsInfoNV(*pCommandInfos, objlist, command_infos_loc);
    if (!enabled_features.clusterAccelerationStructure) {
        skip |= LogError("VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-clusterAccelerationStructure-10443", objlist,
                         error_obj.location, "clusterAccelerationStructures feature was not enabled.");
    }

    const auto &last_bound_state = cb_state->GetLastBoundRayTracing();
    const auto *pipeline_state = last_bound_state.pipeline_state;
    if (pipeline_state && !vku::FindStructInPNextChain<VkRayTracingPipelineClusterAccelerationStructureCreateInfoNV>(
                              pipeline_state->RayTracingCreateInfo().pNext)) {
        skip |= LogError("VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-pNext-10444", objlist, error_obj.location,
                         "The pNext chain of the bound ray tracing pipeline must include a "
                         "VkRayTracingPipelineClusterAccelerationStructureCreateInfoNV structure.\n%s",
                         PrintPNextChain(Struct::VkRayTracingPipelineClusterAccelerationStructureCreateInfoNV,
                                         pipeline_state->RayTracingCreateInfo().pNext)
                             .c_str());
    }

    {
        VkAccelerationStructureBuildSizesInfoKHR accelerationStructure_size = vku::InitStructHelper();
        DispatchGetClusterAccelerationStructureBuildSizesNV(device, &(pCommandInfos->input), &accelerationStructure_size);
        BufferAddressValidation<2> scratch_buffer_validator = {{{
            {"VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-scratchData-12300",
             [&accelerationStructure_size](const vvl::Buffer &buffer_state) {
                 return buffer_state.create_info.size < accelerationStructure_size.buildScratchSize;
             },
             [&accelerationStructure_size]() {
                 return "The buildScratchSize (" + std::to_string(accelerationStructure_size.buildScratchSize) +
                        ") does not fit in any buffer";
             },
             kEmptyErrorMsgBuffer},
            {"VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-pCommandInfos-12304",
             [](const vvl::Buffer &buffer_state) {
                 return (static_cast<uint32_t>(buffer_state.usage) & VK_BUFFER_USAGE_2_STORAGE_BUFFER_BIT) == 0;
             },
             []() { return "The following buffers are missing VK_BUFFER_USAGE_STORAGE_BUFFER_BIT"; }, kUsageErrorMsgBuffer},
        }}};
        skip |= scratch_buffer_validator.ValidateDeviceAddress(*this, command_infos_loc.dot(Field::scratchData), objlist,
                                                               pCommandInfos->scratchData);
    }

    if (!IsPointerAligned(pCommandInfos->scratchData, phys_dev_ext_props.cluster_acceleration_props.clusterScratchByteAlignment)) {
        skip |= LogError(
            "VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-scratchData-12301", commandBuffer,
            command_infos_loc.dot(Field::scratchData),
            "(0x%" PRIx64
            ") must be aligned to VkPhysicalDeviceClusterAccelerationPropertiesNV::clusterScratchByteAlignment (%" PRIu32 ")",
            pCommandInfos->scratchData, phys_dev_ext_props.cluster_acceleration_props.clusterScratchByteAlignment);
    }

    {
        BufferAddressValidation<1> src_infos_array_validator = {{{
            {"VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-pCommandInfos-12305",
             [](const vvl::Buffer &buffer_state) {
                 return (static_cast<uint32_t>(buffer_state.usage) &
                         VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR) == 0;
             },
             []() {
                 return "The following buffers are missing VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR";
             },
             kUsageErrorMsgBuffer},
        }}};

        skip |= src_infos_array_validator.ValidateDeviceAddress(
            *this, command_infos_loc.dot(Field::srcInfosArray).dot(Field::deviceAddress), objlist,
            pCommandInfos->srcInfosArray.deviceAddress);
    }

    {
        BufferAddressValidation<1> src_infos_count_validator = {{{
            {"VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-pCommandInfos-12306",
             [](const vvl::Buffer &buffer_state) {
                 return (static_cast<uint32_t>(buffer_state.usage) &
                         VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR) == 0;
             },
             []() {
                 return "The following buffers are missing VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR";
             },
             kUsageErrorMsgBuffer},
        }}};

        skip |= src_infos_count_validator.ValidateDeviceAddress(*this, command_infos_loc.dot(Field::srcInfosCount), objlist,
                                                                pCommandInfos->srcInfosCount);
    }

    {
        BufferAddressValidation<1> dst_addresses_array_validator = {{{
            {"VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-pCommandInfos-12307",
             [](const vvl::Buffer &buffer_state) {
                 return (static_cast<uint32_t>(buffer_state.usage) & VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR) == 0;
             },
             []() { return "The following buffers are missing VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR"; },
             kUsageErrorMsgBuffer},
        }}};

        skip |= dst_addresses_array_validator.ValidateDeviceAddress(
            *this, command_infos_loc.dot(Field::dstAddressesArray).dot(Field::deviceAddress), objlist,
            pCommandInfos->dstAddressesArray.deviceAddress);
    }

    {
        BufferAddressValidation<1> dst_implicit_data_validator = {{{
            {"VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-pCommandInfos-12308",
             [](const vvl::Buffer &buffer_state) {
                 return (static_cast<uint32_t>(buffer_state.usage) & VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR) == 0;
             },
             []() { return "The following buffers are missing VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR"; },
             kUsageErrorMsgBuffer},
        }}};

        skip |= dst_implicit_data_validator.ValidateDeviceAddress(*this, command_infos_loc.dot(Field::dstImplicitData), objlist,
                                                                  pCommandInfos->dstImplicitData);
    }

    if (pCommandInfos->scratchData && pCommandInfos->dstImplicitData) {
        const auto scratch_buffer_states = GetBuffersByAddress(pCommandInfos->scratchData);
        const auto dst_implicit_buffer_states = GetBuffersByAddress(pCommandInfos->dstImplicitData);
        for (const auto &scratch_buffer_state : scratch_buffer_states) {
            vvl::range<VkDeviceAddress> scratch_address_range = scratch_buffer_state->DeviceAddressRange();

            if (!scratch_address_range.empty()) {
                for (const auto &dst_implicit_buffer_state : dst_implicit_buffer_states) {
                    const vvl::range<VkDeviceAddress> dst_implicit_address_range = dst_implicit_buffer_state->DeviceAddressRange();
                    if (dst_implicit_address_range.intersects(scratch_address_range)) {
                        const LogObjectList objlist_implicit(commandBuffer, dst_implicit_buffer_state->Handle(),
                                                             scratch_buffer_state->Handle());
                        skip |= LogError("VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-dstImplicitData-12303",
                                         objlist_implicit, command_infos_loc.dot(Field::dstImplicitData),
                                         "%s address range %s intersects with scratchData address range %s",
                                         FormatHandle(dst_implicit_buffer_state->Handle()).c_str(),
                                         string_range_hex(dst_implicit_address_range).c_str(),
                                         string_range_hex(scratch_address_range).c_str());
                    }
                }
            }
        }
    }

    if (pCommandInfos->scratchData && pCommandInfos->dstAddressesArray.deviceAddress) {
        const auto scratch_buffer_states = GetBuffersByAddress(pCommandInfos->scratchData);
        const auto dst_addresses_buffer_states = GetBuffersByAddress(pCommandInfos->dstAddressesArray.deviceAddress);
        for (const auto &scratch_buffer_state : scratch_buffer_states) {
            vvl::range<VkDeviceAddress> scratch_address_range = scratch_buffer_state->DeviceAddressRange();

            if (!scratch_address_range.empty()) {
                for (const auto &dst_addresses_buffer_state : dst_addresses_buffer_states) {
                    const vvl::range<VkDeviceAddress> dst_addresses_address_range =
                        dst_addresses_buffer_state->DeviceAddressRange();
                    if (dst_addresses_address_range.intersects(scratch_address_range)) {
                        const LogObjectList objlist_addresses(commandBuffer, dst_addresses_buffer_state->Handle(),
                                                              scratch_buffer_state->Handle());
                        skip |= LogError("VUID-vkCmdBuildClusterAccelerationStructureIndirectNV-dstAddressesArray-12302",
                                         objlist_addresses, command_infos_loc.dot(Field::dstAddressesArray),
                                         "%s address range %s intersects with scratchData address range %s",
                                         FormatHandle(dst_addresses_buffer_state->Handle()).c_str(),
                                         string_range_hex(dst_addresses_address_range).c_str(),
                                         string_range_hex(scratch_address_range).c_str());
                    }
                }
            }
        }
    }

    return skip;
}

bool CoreChecks::PreCallValidateGetClusterAccelerationStructureBuildSizesNV(VkDevice device,
                                                                            const VkClusterAccelerationStructureInputInfoNV *pinfo,
                                                                            VkAccelerationStructureBuildSizesInfoKHR *pSizeInfo,
                                                                            const ErrorObject &error_obj) const {
    bool skip = false;
    if (!enabled_features.clusterAccelerationStructure) {
        skip |= LogError("VUID-vkGetClusterAccelerationStructureBuildSizesNV-clusterAccelerationStructure-10438", device,
                         error_obj.location, "clusterAccelerationStructures feature was not enabled.");
    }

    if (IsValueIn(pinfo->opType, {VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_NV,
                                  VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_TEMPLATE_NV,
                                  VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_INSTANTIATE_TRIANGLE_CLUSTER_NV,
                                  VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_GET_CLUSTER_TEMPLATE_INDICES_NV})) {
        const VkClusterAccelerationStructureTriangleClusterInputNV *triangle_input =
            reinterpret_cast<const VkClusterAccelerationStructureTriangleClusterInputNV *>(pinfo->opInput.pTriangleClusters);

        skip |= ValidateClusterAccelerationStructureTriangleClusterInputNV(
            *triangle_input, error_obj.location.dot(Field::input).dot(Field::opInput).dot(Field::pTriangleClusters));
    }

    return skip;
}
bool CoreChecks::ValidateClusterAccelerationStructureTriangleClusterInputNV(
    const VkClusterAccelerationStructureTriangleClusterInputNV &input, const Location &input_loc) const {
    bool skip = false;
    const VkFormatProperties3 vertex_properties = GetPDFormatProperties(input.vertexFormat);
    if (!(vertex_properties.bufferFeatures & VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR)) {
        skip |=
            LogError("VUID-VkClusterAccelerationStructureTriangleClusterInputNV-vertexFormat-10439", device,
                     input_loc.dot(Field::vertexFormat),
                     "is %s which doesn't support VK_FORMAT_FEATURE_ACCELERATION_STRUCTURE_VERTEX_BUFFER_BIT_KHR.\n"
                     "(supported bufferFeatures: %s)",
                     string_VkFormat(input.vertexFormat), string_VkFormatFeatureFlags2(vertex_properties.bufferFeatures).c_str());
    }

    if (input.maxClusterTriangleCount > phys_dev_ext_props.cluster_acceleration_props.maxTrianglesPerCluster) {
        skip |= LogError("VUID-VkClusterAccelerationStructureTriangleClusterInputNV-maxClusterTriangleCount-10440", device,
                         input_loc.dot(Field::maxClusterTriangleCount),
                         "(%" PRIu32
                         ") must be less than or equal to "
                         "VkPhysicalDeviceClusterAccelerationStructurePropertiesNV::maxTrianglesPerCluster (%" PRIu32 ")",
                         input.maxClusterTriangleCount, phys_dev_ext_props.cluster_acceleration_props.maxTrianglesPerCluster);
    }

    if (input.maxClusterVertexCount > phys_dev_ext_props.cluster_acceleration_props.maxVerticesPerCluster) {
        skip |= LogError("VUID-VkClusterAccelerationStructureTriangleClusterInputNV-maxClusterVertexCount-10441", device,
                         input_loc.dot(Field::maxClusterVertexCount),
                         "(%" PRIu32
                         ") must be less than or equal to "
                         "VkPhysicalDeviceClusterAccelerationStructurePropertiesNV::maxVerticesPerCluster (%" PRIu32 ")",
                         input.maxClusterVertexCount, phys_dev_ext_props.cluster_acceleration_props.maxVerticesPerCluster);
    }

    if (input.minPositionTruncateBitCount > 32) {
        skip |= LogError("VUID-VkClusterAccelerationStructureTriangleClusterInputNV-minPositionTruncateBitCount-10442", device,
                         input_loc.dot(Field::minPositionTruncateBitCount), "(%" PRIu32 ") must be less than or equal to 32",
                         input.minPositionTruncateBitCount);
    }
    return skip;
}

bool CoreChecks::ValidateClusterAccelerationStructureCommandsInfoNV(
    const VkClusterAccelerationStructureCommandsInfoNV &command_infos, const LogObjectList &objlist,
    const Location &command_infos_loc) const {
    bool skip = false;
    bool invalid_triangle_input = false;
    if (IsValueIn(command_infos.input.opType, {VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_NV,
                                               VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_TEMPLATE_NV,
                                               VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_INSTANTIATE_TRIANGLE_CLUSTER_NV,
                                               VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_GET_CLUSTER_TEMPLATE_INDICES_NV})) {
        const VkClusterAccelerationStructureTriangleClusterInputNV *triangle_input =
            reinterpret_cast<const VkClusterAccelerationStructureTriangleClusterInputNV *>(
                command_infos.input.opInput.pTriangleClusters);
        skip |= ValidateClusterAccelerationStructureTriangleClusterInputNV(
            *triangle_input, command_infos_loc.dot(Field::input).dot(Field::opInput).dot(Field::pTriangleClusters));

        if (triangle_input->maxClusterTriangleCount > phys_dev_ext_props.cluster_acceleration_props.maxTrianglesPerCluster ||
            triangle_input->maxClusterVertexCount > phys_dev_ext_props.cluster_acceleration_props.maxVerticesPerCluster ||
            triangle_input->minPositionTruncateBitCount > 32) {
            invalid_triangle_input = true;
        }
    }
    // aligned based on the cluster acceleration structure type and its alignment properties as described in
    // VkPhysicalDeviceClusterAccelerationStructurePropertiesNV
    uint32_t alignment_type = 1;
    const char *vuid = kVUIDUndefined;
    switch (command_infos.input.opType) {
        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_TEMPLATE_NV:
            alignment_type = phys_dev_ext_props.cluster_acceleration_props.clusterTemplateByteAlignment;
            vuid = "VUID-VkClusterAccelerationStructureCommandsInfoNV-input-12318";
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_NV:
            alignment_type = phys_dev_ext_props.cluster_acceleration_props.clusterByteAlignment;
            vuid = "VUID-VkClusterAccelerationStructureCommandsInfoNV-input-12317";
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_INSTANTIATE_TRIANGLE_CLUSTER_NV:
            alignment_type = phys_dev_ext_props.cluster_acceleration_props.clusterByteAlignment;
            vuid = "VUID-VkClusterAccelerationStructureCommandsInfoNV-input-12319";
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_GET_CLUSTER_TEMPLATE_INDICES_NV:
        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_CLUSTERS_BOTTOM_LEVEL_NV:
        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_MOVE_OBJECTS_NV:
        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_MAX_ENUM_NV:
            break;
    }
    VkAccelerationStructureBuildSizesInfoKHR accelerationStructure_size = vku::InitStructHelper();
    DispatchGetClusterAccelerationStructureBuildSizesNV(device, &(command_infos.input), &accelerationStructure_size);
    if (command_infos.input.opMode == VK_CLUSTER_ACCELERATION_STRUCTURE_OP_MODE_IMPLICIT_DESTINATIONS_NV) {
        if (command_infos.dstImplicitData == 0) {
            skip |= LogError(
                "VUID-VkClusterAccelerationStructureCommandsInfoNV-opMode-12309", objlist,
                command_infos_loc.dot(Field::dstImplicitData),
                "(0x%" PRIx64
                ") must be a valid address if input::opMode is VK_CLUSTER_ACCELERATION_STRUCTURE_OP_MODE_IMPLICIT_DESTINATIONS_NV",
                command_infos.dstImplicitData);
        } else if (!IsPointerAligned(command_infos.dstImplicitData, alignment_type)) {
            skip |= LogError(vuid, objlist, command_infos_loc.dot(Field::dstImplicitData),
                             "(0x%" PRIx64 ") must be aligned to (%" PRIu32
                             ") depending on the input::opMode (%s) and input::opType (%s)",
                             command_infos.dstImplicitData, alignment_type,
                             string_VkClusterAccelerationStructureOpModeNV(command_infos.input.opMode),
                             string_VkClusterAccelerationStructureOpTypeNV(command_infos.input.opType));
        } else {
            if (!invalid_triangle_input &&
                command_infos.input.opType != VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_MOVE_OBJECTS_NV) {
                BufferAddressValidation<1> dst_implicit_size_validator = {{{
                    {"VUID-VkClusterAccelerationStructureCommandsInfoNV-opMode-12310",
                     [&accelerationStructure_size](const vvl::Buffer &buffer_state) {
                         return buffer_state.create_info.size < accelerationStructure_size.accelerationStructureSize;
                     },
                     [&accelerationStructure_size]() {
                         return "The accelerationStructureSize (" +
                                std::to_string(accelerationStructure_size.accelerationStructureSize) +
                                ") does not fit in any buffer";
                     },
                     kEmptyErrorMsgBuffer},
                }}};

                skip |= dst_implicit_size_validator.ValidateDeviceAddress(*this, command_infos_loc.dot(Field::dstImplicitData),
                                                                          objlist, command_infos.dstImplicitData,
                                                                          accelerationStructure_size.accelerationStructureSize);
            }
        }
    }

    if (command_infos.input.opMode == VK_CLUSTER_ACCELERATION_STRUCTURE_OP_MODE_COMPUTE_SIZES_NV) {
        if (command_infos.dstSizesArray.deviceAddress == 0) {
            skip |= LogError("VUID-VkClusterAccelerationStructureCommandsInfoNV-opMode-12312", objlist,
                             command_infos_loc.dot(Field::dstSizesArray).dot(Field::deviceAddress),
                             "is zero, but input::opMode is VK_CLUSTER_ACCELERATION_STRUCTURE_OP_MODE_COMPUTE_SIZES_NV");
        }
        skip |= ValidateDeviceAddress(command_infos_loc.dot(Field::dstSizesArray).dot(Field::deviceAddress), objlist,
                                      command_infos.dstSizesArray.deviceAddress);
    }

    if (command_infos.input.opMode == VK_CLUSTER_ACCELERATION_STRUCTURE_OP_MODE_EXPLICIT_DESTINATIONS_NV) {
        if (command_infos.dstAddressesArray.deviceAddress == 0) {
            skip |= LogError("VUID-VkClusterAccelerationStructureCommandsInfoNV-opMode-12313", objlist,
                             command_infos_loc.dot(Field::dstAddressesArray).dot(Field::deviceAddress),
                             "is zero, but input::opMode is VK_CLUSTER_ACCELERATION_STRUCTURE_OP_MODE_EXPLICIT_DESTINATIONS_NV");
        }
        skip |= ValidateDeviceAddress(command_infos_loc.dot(Field::dstAddressesArray).dot(Field::deviceAddress), objlist,
                                      command_infos.dstAddressesArray.deviceAddress);
    }

    if (command_infos.dstAddressesArray.deviceAddress != 0 && command_infos.dstAddressesArray.stride < 8) {
        skip |= LogError("VUID-VkClusterAccelerationStructureCommandsInfoNV-dstAddressesArray-10474", objlist,
                         command_infos_loc.dot(Field::dstAddressesArray).dot(Field::stride),
                         "(%" PRIu64 ") must be greater than or equal to 8", command_infos.dstAddressesArray.stride);
    }

    if (command_infos.dstSizesArray.deviceAddress != 0 && command_infos.dstSizesArray.stride < 4) {
        skip |= LogError("VUID-VkClusterAccelerationStructureCommandsInfoNV-dstSizesArray-10475", objlist,
                         command_infos_loc.dot(Field::dstSizesArray).dot(Field::stride),
                         "(%" PRIu64 ") must be greater than or equal to 4", command_infos.dstSizesArray.stride);
    }
    uint32_t stride_min = 0;
    switch (command_infos.input.opType) {
        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_MOVE_OBJECTS_NV:
            stride_min = sizeof(VkClusterAccelerationStructureMoveObjectsInfoNV);
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_CLUSTERS_BOTTOM_LEVEL_NV:
            stride_min = sizeof(VkClusterAccelerationStructureBuildClustersBottomLevelInfoNV);
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_NV:
            stride_min = sizeof(VkClusterAccelerationStructureBuildTriangleClusterInfoNV);
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_BUILD_TRIANGLE_CLUSTER_TEMPLATE_NV:
            stride_min = sizeof(VkClusterAccelerationStructureBuildTriangleClusterTemplateInfoNV);
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_INSTANTIATE_TRIANGLE_CLUSTER_NV:
            stride_min = sizeof(VkClusterAccelerationStructureInstantiateClusterInfoNV);
            break;

        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_GET_CLUSTER_TEMPLATE_INDICES_NV:
        case VK_CLUSTER_ACCELERATION_STRUCTURE_OP_TYPE_MAX_ENUM_NV:
            break;
    }

    if (command_infos.srcInfosArray.stride < stride_min && command_infos.srcInfosArray.stride != 0) {
        skip |= LogError("VUID-VkClusterAccelerationStructureCommandsInfoNV-srcInfosArray-10476", objlist,
                         command_infos_loc.dot(Field::srcInfosArray).dot(Field::stride),
                         "(%" PRIu64 ") must be greater than size of %s (%" PRIu32 ")", command_infos.srcInfosArray.stride,
                         string_VkClusterAccelerationStructureOpTypeNV(command_infos.input.opType), stride_min);
    }

    if (!IsPointerAligned(command_infos.scratchData, phys_dev_ext_props.cluster_acceleration_props.clusterScratchByteAlignment)) {
        skip |= LogError(
            "VUID-VkClusterAccelerationStructureCommandsInfoNV-scratchData-12320", objlist,
            command_infos_loc.dot(Field::scratchData),
            "(0x%" PRIx64
            ") must be aligned to VkPhysicalDeviceClusterAccelerationPropertiesNV::clusterScratchByteAlignment (%" PRIu32 ")",
            command_infos.scratchData, phys_dev_ext_props.cluster_acceleration_props.clusterScratchByteAlignment);
    }

    if (accelerationStructure_size.buildScratchSize != 0) {
        if (command_infos.scratchData == 0) {
            skip |= LogError("VUID-VkClusterAccelerationStructureCommandsInfoNV-buildScratchSize-12321", objlist,
                             command_infos_loc.dot(Field::scratchData),
                             "(0x%" PRIx64 ") must be a valid VkDeviceAddress when buildScratchSize (%" PRIu64 ") is not 0",
                             command_infos.scratchData, static_cast<uint64_t>(accelerationStructure_size.buildScratchSize));
        } else {
            skip |= ValidateDeviceAddress(command_infos_loc.dot(Field::scratchData), objlist, command_infos.scratchData);
        }
    }

    if (!IsPointerAligned(command_infos.srcInfosCount, 4)) {
        skip |= LogError("VUID-VkClusterAccelerationStructureCommandsInfoNV-srcInfosCount-12322", objlist,
                         command_infos_loc.dot(Field::srcInfosCount), "(0x%" PRIx64 ") must be 4-byte aligned",
                         command_infos.srcInfosCount);
    }

    return skip;
}
