| /* 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) 2025-2026 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 <vulkan/vulkan_core.h> |
| #include <vulkan/utility/vk_format_utils.h> |
| #include "containers/span.h" |
| #include "error_message/error_location.h" |
| #include "stateless/stateless_validation.h" |
| |
| #include "utils/math_utils.h" |
| |
| namespace stateless { |
| |
| bool Device::ValidateMicromapBuildInfo(const Context& context, const VkMicromapBuildInfoEXT& build_info, |
| const Location& build_info_loc) const { |
| bool skip = false; |
| |
| if (build_info.pUsageCounts && build_info.ppUsageCounts) { |
| skip |= LogError("VUID-VkMicromapBuildInfoEXT-pUsageCounts-07516", device, build_info_loc, |
| "both pUsageCounts and ppUsageCounts are not NULL."); |
| } |
| |
| if (build_info.type == VK_MICROMAP_TYPE_OPACITY_MICROMAP_EXT && (build_info.pUsageCounts || build_info.ppUsageCounts)) { |
| const Field usage_field = build_info.pUsageCounts ? Field::pUsageCounts : Field::ppUsageCounts; |
| for (uint32_t usage_i = 0; usage_i < build_info.usageCountsCount; ++usage_i) { |
| const VkMicromapUsageEXT& usage = |
| build_info.pUsageCounts ? build_info.pUsageCounts[usage_i] : *build_info.ppUsageCounts[usage_i]; |
| skip |= context.ValidateRangedEnum( |
| build_info_loc.dot(usage_field, usage_i).dot(Field::format), vvl::Enum::VkOpacityMicromapFormatKHR, |
| static_cast<VkOpacityMicromapFormatKHR>(usage.format), "VUID-VkMicromapBuildInfoEXT-format-11652"); |
| } |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCreateMicromapEXT(VkDevice device, const VkMicromapCreateInfoEXT* pCreateInfo, |
| const VkAllocationCallbacks* pAllocator, VkMicromapEXT* pMicromap, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| if (!enabled_features.micromapEXT) { |
| skip |= LogError("VUID-vkCreateMicromapEXT-micromap-11615", device, error_obj.location, |
| "The VkPhysicalDeviceOpacityMicromapFeaturesEXT::micromap feature was not enabled."); |
| } |
| |
| if ((pCreateInfo->createFlags & VK_MICROMAP_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_EXT) && |
| !enabled_features.micromapCaptureReplayEXT) { |
| skip |= LogError("VUID-VkMicromapCreateInfoEXT-micromapCaptureReplay-11616", device, error_obj.location, |
| "micromapCaptureReplay feature was not enabled."); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateDestroyMicromapEXT(VkDevice device, VkMicromapEXT micromap, |
| const VkAllocationCallbacks* pAllocator, const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| if (!enabled_features.micromapEXT) { |
| skip |= LogError("VUID-vkDestroyMicromapEXT-micromap-11619", device, error_obj.location, |
| "The VkPhysicalDeviceOpacityMicromapFeaturesEXT::micromap feature was not enabled."); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCmdBuildMicromapsEXT(VkCommandBuffer commandBuffer, uint32_t infoCount, |
| const VkMicromapBuildInfoEXT* pInfos, const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (!enabled_features.micromapEXT) { |
| skip |= LogError("VUID-vkCmdBuildMicromapsEXT-micromap-11648", device, error_obj.location, |
| "The VkPhysicalDeviceOpacityMicromapFeaturesEXT::micromap feature was not enabled."); |
| } |
| |
| for (const auto [info_i, info] : vvl::enumerate(pInfos, infoCount)) { |
| const Location info_loc = error_obj.location.dot(Field::pInfos, info_i); |
| |
| if (!IsPointerAligned(info.scratchData.deviceAddress, |
| phys_dev_ext_props.acc_structure_props.minAccelerationStructureScratchOffsetAlignment)) { |
| skip |= LogError("VUID-vkCmdBuildMicromapsEXT-pInfos-07514", commandBuffer, |
| info_loc.dot(Field::scratchData).dot(Field::deviceAddress), |
| "(0x%" PRIx64 ") must be aligned to minAccelerationStructureScratchOffsetAlignment (%" PRIu32 ").", |
| info.scratchData.deviceAddress, |
| phys_dev_ext_props.acc_structure_props.minAccelerationStructureScratchOffsetAlignment); |
| } |
| |
| if (info.triangleArray.deviceAddress == 0) { |
| // TODO - This needs to be in CoreChecks and use cc_buffer_address.cpp |
| skip |= LogError("VUID-vkCmdBuildMicromapsEXT-pInfos-10897", device, |
| info_loc.dot(Field::triangleArray).dot(Field::deviceAddress), "is NULL."); |
| } else if (!IsPointerAligned(info.triangleArray.deviceAddress, 256)) { |
| skip |= LogError("VUID-vkCmdBuildMicromapsEXT-pInfos-07515", commandBuffer, |
| info_loc.dot(Field::triangleArray).dot(Field::deviceAddress), |
| "(0x%" PRIx64 ") must be aligned to 256.", info.triangleArray.deviceAddress); |
| } |
| |
| if (info.data.deviceAddress == 0) { |
| // TODO - This needs to be in CoreChecks and use cc_buffer_address.cpp |
| skip |= LogError("VUID-vkCmdBuildMicromapsEXT-pInfos-10896", device, |
| info_loc.dot(Field::data).dot(Field::deviceAddress), "is NULL."); |
| } else if (!IsPointerAligned(info.data.deviceAddress, 256)) { |
| skip |= LogError("VUID-vkCmdBuildMicromapsEXT-pInfos-07515", commandBuffer, |
| info_loc.dot(Field::data).dot(Field::deviceAddress), "(0x%" PRIx64 ") must be aligned to 256.", |
| info.data.deviceAddress); |
| } |
| |
| skip |= ValidateMicromapBuildInfo(context, info, info_loc); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateBuildMicromapsEXT(VkDevice device, VkDeferredOperationKHR deferredOperation, uint32_t infoCount, |
| const VkMicromapBuildInfoEXT* pInfos, const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (!enabled_features.micromapHostCommandsEXT) { |
| skip |= LogError("VUID-vkBuildMicromapsEXT-micromapHostCommands-07555", device, error_obj.location, |
| "micromapHostCommands feature was not enabled."); |
| } |
| |
| for (const auto [info_i, info] : vvl::enumerate(pInfos, infoCount)) { |
| const Location info_loc = error_obj.location.dot(Field::pInfos, info_i); |
| if (!info.data.hostAddress) { |
| skip |= LogError("VUID-vkBuildMicromapsEXT-pInfos-07553", device, info_loc.dot(Field::data).dot(Field::hostAddress), |
| "is zero."); |
| } |
| if (!info.triangleArray.hostAddress) { |
| skip |= LogError("VUID-vkBuildMicromapsEXT-pInfos-07554", device, |
| info_loc.dot(Field::triangleArray).dot(Field::hostAddress), "is zero."); |
| } |
| |
| skip |= ValidateMicromapBuildInfo(context, info, info_loc); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCopyMicromapEXT(VkDevice device, VkDeferredOperationKHR deferredOperation, |
| const VkCopyMicromapInfoEXT* pInfo, const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (!enabled_features.micromapHostCommandsEXT) { |
| skip |= LogError("VUID-vkCopyMicromapEXT-micromapHostCommands-07560", device, error_obj.location, |
| "micromapHostCommands feature was not enabled."); |
| } |
| |
| const Location info_loc = error_obj.location.dot(Field::pInfo); |
| if (pInfo->mode != VK_COPY_MICROMAP_MODE_COMPACT_EXT && pInfo->mode != VK_COPY_MICROMAP_MODE_CLONE_EXT) { |
| skip |= LogError("VUID-VkCopyMicromapInfoEXT-mode-07531", device, info_loc.dot(Field::mode), "is %s.", |
| string_VkCopyMicromapModeEXT(pInfo->mode)); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCopyMicromapToMemoryEXT(VkDevice device, VkDeferredOperationKHR deferredOperation, |
| const VkCopyMicromapToMemoryInfoEXT* pInfo, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (!enabled_features.micromapHostCommandsEXT) { |
| skip |= LogError("VUID-vkCopyMicromapToMemoryEXT-micromapHostCommands-07571", device, error_obj.location, |
| "micromapHostCommands feature was not enabled."); |
| } |
| |
| const Location info_loc = error_obj.location.dot(Field::pInfo); |
| if (pInfo->mode != VK_COPY_MICROMAP_MODE_SERIALIZE_EXT) { |
| skip |= LogError("VUID-VkCopyMicromapToMemoryInfoEXT-mode-07542", device, info_loc.dot(Field::mode), "is %s.", |
| string_VkCopyMicromapModeEXT(pInfo->mode)); |
| } |
| |
| if (!pInfo->dst.hostAddress) { |
| skip |= LogError("VUID-vkCopyMicromapToMemoryEXT-pInfo-07569", device, info_loc.dot(Field::dst).dot(Field::hostAddress), |
| "is zero."); |
| } else if (!IsPointerAligned(pInfo->dst.hostAddress, 16)) { |
| skip |= LogError("VUID-vkCopyMicromapToMemoryEXT-pInfo-07570", device, info_loc.dot(Field::dst).dot(Field::hostAddress), |
| "(%p) must be aligned to 16 bytes.", pInfo->dst.hostAddress); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCopyMemoryToMicromapEXT(VkDevice device, VkDeferredOperationKHR deferredOperation, |
| const VkCopyMemoryToMicromapInfoEXT* pInfo, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (!enabled_features.micromapHostCommandsEXT) { |
| skip |= LogError("VUID-vkCopyMemoryToMicromapEXT-micromapHostCommands-07566", device, error_obj.location, |
| "micromapHostCommands feature was not enabled."); |
| } |
| |
| const Location info_loc = error_obj.location.dot(Field::pInfo); |
| if (pInfo->mode != VK_COPY_MICROMAP_MODE_DESERIALIZE_EXT) { |
| skip |= LogError("VUID-VkCopyMemoryToMicromapInfoEXT-mode-07548", device, info_loc.dot(Field::mode), "is %s.", |
| string_VkCopyMicromapModeEXT(pInfo->mode)); |
| } |
| |
| if (!pInfo->src.hostAddress) { |
| skip |= LogError("VUID-vkCopyMemoryToMicromapEXT-pInfo-07563", device, info_loc.dot(Field::src).dot(Field::hostAddress), |
| "is zero."); |
| } else if (!IsPointerAligned(pInfo->src.hostAddress, 16)) { |
| skip |= LogError("VUID-vkCopyMemoryToMicromapEXT-pInfo-07564", device, info_loc.dot(Field::src).dot(Field::hostAddress), |
| "(%p) must be aligned to 16 bytes.", pInfo->src.hostAddress); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateWriteMicromapsPropertiesEXT(VkDevice device, uint32_t micromapCount, |
| const VkMicromapEXT* pMicromaps, VkQueryType queryType, |
| size_t dataSize, void* pData, size_t stride, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (queryType != VK_QUERY_TYPE_MICROMAP_COMPACTED_SIZE_EXT && queryType != VK_QUERY_TYPE_MICROMAP_SERIALIZATION_SIZE_EXT) { |
| skip |= LogError("VUID-vkWriteMicromapsPropertiesEXT-queryType-07503", device, error_obj.location, "is %s.", |
| string_VkQueryType(queryType)); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCmdCopyMicromapEXT(VkCommandBuffer commandBuffer, const VkCopyMicromapInfoEXT* pInfo, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| const Location info_loc = error_obj.location.dot(Field::pInfo); |
| if (pInfo->mode != VK_COPY_MICROMAP_MODE_COMPACT_EXT && pInfo->mode != VK_COPY_MICROMAP_MODE_CLONE_EXT) { |
| skip |= LogError("VUID-VkCopyMicromapInfoEXT-mode-07531", commandBuffer, info_loc.dot(Field::mode), "is %s.", |
| string_VkCopyMicromapModeEXT(pInfo->mode)); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCmdCopyMicromapToMemoryEXT(VkCommandBuffer commandBuffer, |
| const VkCopyMicromapToMemoryInfoEXT* pInfo, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| const Location info_loc = error_obj.location.dot(Field::pInfo); |
| if (pInfo->mode != VK_COPY_MICROMAP_MODE_SERIALIZE_EXT) { |
| skip |= LogError("VUID-VkCopyMicromapToMemoryInfoEXT-mode-07542", commandBuffer, info_loc.dot(Field::mode), "is %s.", |
| string_VkCopyMicromapModeEXT(pInfo->mode)); |
| } |
| |
| if (pInfo->dst.deviceAddress == 0) { |
| skip |= LogError("VUID-vkCmdCopyMicromapToMemoryEXT-pInfo-07536", device, |
| info_loc.dot(Field::dst).dot(Field::deviceAddress), "is NULL."); |
| } else if (!IsPointerAligned(pInfo->dst.deviceAddress, 256)) { |
| skip |= |
| LogError("VUID-vkCmdCopyMicromapToMemoryEXT-pInfo-07537", device, info_loc.dot(Field::dst).dot(Field::deviceAddress), |
| "(0x%" PRIx64 ") must be aligned to 256 bytes.", pInfo->dst.deviceAddress); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCmdCopyMemoryToMicromapEXT(VkCommandBuffer commandBuffer, |
| const VkCopyMemoryToMicromapInfoEXT* pInfo, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| const Location info_loc = error_obj.location.dot(Field::pInfo); |
| if (pInfo->mode != VK_COPY_MICROMAP_MODE_DESERIALIZE_EXT) { |
| skip |= LogError("VUID-VkCopyMemoryToMicromapInfoEXT-mode-07548", commandBuffer, info_loc.dot(Field::mode), "is %s.", |
| string_VkCopyMicromapModeEXT(pInfo->mode)); |
| } |
| |
| if (pInfo->src.deviceAddress == 0) { |
| skip |= LogError("VUID-vkCmdCopyMemoryToMicromapEXT-pInfo-07543", device, |
| info_loc.dot(Field::src).dot(Field::deviceAddress), "is NULL."); |
| } else if (!IsPointerAligned(pInfo->src.deviceAddress, 256)) { |
| skip |= |
| LogError("VUID-vkCmdCopyMemoryToMicromapEXT-pInfo-07544", device, info_loc.dot(Field::src).dot(Field::deviceAddress), |
| "(0x%" PRIx64 ") must be aligned to 256 bytes.", pInfo->src.deviceAddress); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateCmdWriteMicromapsPropertiesEXT(VkCommandBuffer commandBuffer, uint32_t micromapCount, |
| const VkMicromapEXT* pMicromaps, VkQueryType queryType, |
| VkQueryPool queryPool, uint32_t firstQuery, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (queryType != VK_QUERY_TYPE_MICROMAP_COMPACTED_SIZE_EXT && queryType != VK_QUERY_TYPE_MICROMAP_SERIALIZATION_SIZE_EXT) { |
| skip |= LogError("VUID-vkCmdWriteMicromapsPropertiesEXT-queryType-07503", commandBuffer, error_obj.location, "is %s.", |
| string_VkQueryType(queryType)); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateGetDeviceMicromapCompatibilityEXT(VkDevice device, const VkMicromapVersionInfoEXT* pVersionInfo, |
| VkAccelerationStructureCompatibilityKHR* pCompatibility, |
| const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (!enabled_features.micromapEXT) { |
| skip |= LogError("VUID-vkGetDeviceMicromapCompatibilityEXT-micromap-11666", device, error_obj.location, |
| "The VkPhysicalDeviceOpacityMicromapFeaturesEXT::micromap feature was not enabled."); |
| } |
| |
| return skip; |
| } |
| |
| bool Device::manual_PreCallValidateGetMicromapBuildSizesEXT(VkDevice device, VkAccelerationStructureBuildTypeKHR buildType, |
| const VkMicromapBuildInfoEXT* pBuildInfo, |
| VkMicromapBuildSizesInfoEXT* pSizeInfo, const Context& context) const { |
| bool skip = false; |
| const auto& error_obj = context.error_obj; |
| |
| if (!enabled_features.micromapEXT) { |
| skip |= LogError("VUID-vkGetMicromapBuildSizesEXT-micromap-11618", device, error_obj.location, |
| "The VkPhysicalDeviceOpacityMicromapFeaturesEXT::micromap feature was not enabled."); |
| } |
| |
| skip |= ValidateMicromapBuildInfo(context, *pBuildInfo, error_obj.location.dot(Field::pBuildInfo)); |
| |
| return skip; |
| } |
| |
| } // namespace stateless |